@jeik/dingtalk-connector 0.8.28 → 0.8.30

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.
Files changed (40) hide show
  1. package/CHANGELOG.md +40 -30
  2. package/README.en.md +114 -130
  3. package/README.md +112 -131
  4. package/dist/{background-work-Cxb4b-91.mjs → background-work-DkaSDqWk.mjs} +12 -1
  5. package/dist/card-content-cache-C7-XHhHw.mjs +2 -0
  6. package/dist/{card-content-cache-bDQFz2u8.mjs → card-content-cache-DT_OYThh.mjs} +15 -1
  7. package/dist/chunk-upload-BKM1y4Ba.mjs +2 -0
  8. package/dist/{chunk-upload-BE71drPg.mjs → chunk-upload-CIU5G_ay.mjs} +1 -1
  9. package/dist/{common-BZiVgd3A.mjs → common-BfIYs1yC.mjs} +3 -3
  10. package/dist/{common-DgL28gCZ.mjs → common-C0Oaf0z-.mjs} +1 -1
  11. package/dist/{connection-CZ7ZoN5_.mjs → connection-obtrPkj4.mjs} +56 -65
  12. package/dist/entry-bundled.mjs +1 -1
  13. package/dist/{gateway-methods-kNX3qvqw.mjs → gateway-methods-BdcpfxEZ.mjs} +1 -1
  14. package/dist/gateway-methods-DQ6TwOAr.mjs +2 -0
  15. package/dist/index.mjs +2 -2
  16. package/dist/{logger-mZ9OSbmD.mjs → logger-CnBTcwyq.mjs} +16 -2
  17. package/dist/logger-D8H2IcYz.mjs +2 -0
  18. package/dist/{media-BqpUGCni.mjs → media-DDtX37T2.mjs} +1 -1
  19. package/dist/{media-DnQB2BrL.mjs → media-g8tJtpfU.mjs} +29 -20
  20. package/dist/{message-handler-B1usz1wb.mjs → message-handler-BeTQQCUe.mjs} +33 -30
  21. package/dist/{messaging-DVS3UKOp.mjs → messaging-CK0Wz3Ze.mjs} +18 -20
  22. package/dist/{runtime-A6G7WAWn.mjs → runtime-CjE4Z9G5.mjs} +6 -5
  23. package/dist/{utils-Bjh4r_qS.mjs → utils-Bwz2Fls_.mjs} +1 -1
  24. package/docs/RELEASE_NOTES_V0.8.28.md +4 -4
  25. package/docs/RELEASE_NOTES_V0.8.29.md +72 -0
  26. package/openclaw.plugin.json +9 -5
  27. package/package.json +1 -1
  28. package/src/config/schema.ts +2 -1
  29. package/src/core/connection.ts +90 -113
  30. package/src/core/message-handler.ts +24 -16
  31. package/src/reply-dispatcher.ts +33 -20
  32. package/src/services/media.ts +60 -27
  33. package/src/services/messaging/card-content-cache.ts +11 -6
  34. package/src/services/messaging.ts +52 -38
  35. package/src/utils/background-work.ts +3 -1
  36. package/src/utils/logger.ts +23 -2
  37. package/dist/card-content-cache-c4Ls96eJ.mjs +0 -2
  38. package/dist/chunk-upload-PwhogwfE.mjs +0 -2
  39. package/dist/gateway-methods-BkI1gKaC.mjs +0 -2
  40. package/dist/logger-BmJkQkm1.mjs +0 -2
@@ -1,7 +1,7 @@
1
1
  import { a as resolveDingtalkAccount, t as listDingtalkAccountIds } from "./accounts-BAzdqkAV.mjs";
2
2
  import { t as dingtalkHttp } from "./http-client-DFWZgO1n.mjs";
3
3
  import { r as getAccessToken, t as DINGTALK_API } from "./utils-CIfI_3Jh.mjs";
4
- import { c as prepareMultiBotMentions, d as finishAICard, i as sendProactive, s as buildBotMentionTable } from "./messaging-DVS3UKOp.mjs";
4
+ import { c as prepareMultiBotMentions, d as finishAICard, i as sendProactive, s as buildBotMentionTable } from "./messaging-CK0Wz3Ze.mjs";
5
5
  import { d as getUnionId, h as recallEmotionReply } from "./utils-legacy-DQ3HeGHh.mjs";
6
6
  //#region src/docs.ts
7
7
  var DingtalkDocsClient = class {
@@ -0,0 +1,2 @@
1
+ import { t as registerGatewayMethods } from "./gateway-methods-BdcpfxEZ.mjs";
2
+ export { registerGatewayMethods };
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { a as initDingtalkPluginConfigSchema, i as dingtalkPlugin, n as setDingtalkRuntime } from "./runtime-A6G7WAWn.mjs";
2
- import { t as registerGatewayMethods } from "./gateway-methods-kNX3qvqw.mjs";
1
+ import { a as initDingtalkPluginConfigSchema, i as dingtalkPlugin, n as setDingtalkRuntime } from "./runtime-CjE4Z9G5.mjs";
2
+ import { t as registerGatewayMethods } from "./gateway-methods-BdcpfxEZ.mjs";
3
3
  //#region index.ts
4
4
  /**
5
5
  * 检测同一 plugin id 在多个路径被加载的情况。
@@ -1,8 +1,21 @@
1
1
  //#region src/utils/logger.ts
2
2
  /**
3
3
  * 日志工具模块
4
- * 根据 debug 配置控制日志输出
4
+ * 根据配置里的 debug 控制日志输出(无需环境变量)
5
5
  */
6
+ /** 任一账号/连接开启过 debug 后为 true;默认静默 */
7
+ let pluginDebugEnabled = false;
8
+ /**
9
+ * 设置插件全局 debug(由配置 `debug: true` 驱动)
10
+ * 仅在 true 时打开;避免多账号中未开 debug 的账号把开关关掉。
11
+ */
12
+ function setDingtalkDebug(enabled) {
13
+ if (enabled) pluginDebugEnabled = true;
14
+ }
15
+ /** 是否输出图片/引用/队列等详细诊断日志 */
16
+ function isDingtalkDebug() {
17
+ return pluginDebugEnabled;
18
+ }
6
19
  /**
7
20
  * 创建日志记录器
8
21
  * @param debug - 是否启用 debug 模式
@@ -10,6 +23,7 @@
10
23
  * @returns 日志记录器对象
11
24
  */
12
25
  function createLogger(debug = false, prefix = "") {
26
+ if (debug) setDingtalkDebug(true);
13
27
  return {
14
28
  /**
15
29
  * 打印 info 级别日志
@@ -55,4 +69,4 @@ function createLoggerFromConfig(config, prefix = "") {
55
69
  return createLogger(!!config?.debug, prefix);
56
70
  }
57
71
  //#endregion
58
- export { createLoggerFromConfig as n, createLogger as t };
72
+ export { setDingtalkDebug as i, createLoggerFromConfig as n, isDingtalkDebug as r, createLogger as t };
@@ -0,0 +1,2 @@
1
+ import { i as setDingtalkDebug, n as createLoggerFromConfig } from "./logger-CnBTcwyq.mjs";
2
+ export { createLoggerFromConfig, setDingtalkDebug };
@@ -1,2 +1,2 @@
1
- import { _ as toLocalPath, d as processLocalImages, f as processRawMediaPaths, h as sendFileProactive, o as hasResidualLocalMdImagesOutsideCode, p as processVideoMarkers } from "./media-DnQB2BrL.mjs";
1
+ import { _ as toLocalPath, d as processLocalImages, f as processRawMediaPaths, h as sendFileProactive, o as hasResidualLocalMdImagesOutsideCode, p as processVideoMarkers } from "./media-g8tJtpfU.mjs";
2
2
  export { hasResidualLocalMdImagesOutsideCode, processLocalImages, processRawMediaPaths, processVideoMarkers, sendFileProactive, toLocalPath };
@@ -1,3 +1,4 @@
1
+ import { r as isDingtalkDebug } from "./logger-CnBTcwyq.mjs";
1
2
  import { n as dingtalkOapiHttp, t as dingtalkHttp } from "./http-client-DFWZgO1n.mjs";
2
3
  import { n as DINGTALK_OAPI } from "./utils-CIfI_3Jh.mjs";
3
4
  import { createRequire } from "node:module";
@@ -76,9 +77,8 @@ function toLocalPath(raw) {
76
77
  return filePath;
77
78
  }
78
79
  /**
79
- * 本地图诊断:默认 console.log 一行(不走 log.warn,避免 [sendTextToDingTalk] 双前缀)。
80
- * verbose=true 的细节(realpath 等)仅在 debug 时输出。
81
- * 决策路径(匹配/exists/上传成败/残留)务必 always-on,方便查灰图。
80
+ * 本地图诊断日志。
81
+ * 默认静默:无图/空扫描不再刷屏。仅在上传/失败/灰图风险,或 debug: true 时输出。
82
82
  */
83
83
  function logLocalImage(log, step, detail, verbose = false) {
84
84
  const line = detail ? `[DingTalk][LocalImage] ${step} | ${detail}` : `[DingTalk][LocalImage] ${step}`;
@@ -88,6 +88,12 @@ function logLocalImage(log, step, detail, verbose = false) {
88
88
  } catch {}
89
89
  return;
90
90
  }
91
+ if (!(isDingtalkDebug() || /失败|灰图|残留|上传成功|上传失败|下载成功|下载失败|重试|remote|Remote/i.test(`${step} ${detail || ""}`))) {
92
+ try {
93
+ log?.debug?.(line);
94
+ } catch {}
95
+ return;
96
+ }
91
97
  console.log(line);
92
98
  }
93
99
  /** 钉钉 mediaId 形态(oapi 返回常带 @) */
@@ -117,7 +123,7 @@ function logMediaIdTrace(stage, content, extra, force = false) {
117
123
  const residualLocal = mdTargets.filter((t) => isLocalImageRef(t));
118
124
  const httpCount = (text.match(/https?:\/\//gi) || []).length;
119
125
  const suspiciousBare = /type=image|sampleImage|photoURL/i.test(`${stage} ${extra || ""}`) ? [] : bareIds;
120
- if (!(force || suspiciousBare.length > 0 || residualLocal.length > 0 || /API前|完成|:out|:in|after-process|processImages/i.test(stage))) return;
126
+ if (!(force || isDingtalkDebug() || suspiciousBare.length > 0 || residualLocal.length > 0)) return;
121
127
  const preview = text.replace(/\n/g, "\\n").slice(0, 200);
122
128
  console.log(`[DingTalk][MediaIdTrace] ${stage} | len=${text.length} mediaIdCount=${ids.length}` + (ids.length ? ` ids=[${ids.slice(0, 4).join(",")}]` : "") + ` BARE_ids=${suspiciousBare.length ? suspiciousBare.join(",") : "无"} mdImgs=[${mdImgs.length ? mdImgs.join(" || ") : "-"}] httpCount=${httpCount}` + (residualLocal.length ? ` residualLocal=${residualLocal.length}:${residualLocal.slice(0, 3).map((p) => p.slice(0, 80)).join("|")}` : " residualLocal=0") + (extra ? ` ${extra}` : "") + ` preview="${preview}"`);
123
129
  }
@@ -277,7 +283,7 @@ async function downloadRemoteUrlToTemp(url) {
277
283
  }
278
284
  const tmp = path.join(os.tmpdir(), `dingtalk-md-remote-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`);
279
285
  fs.writeFileSync(tmp, buf);
280
- console.log(`[DingTalk][MediaIdTrace] 远程图下载成功 | bytes=${buf.length} tmp=${tmp}`);
286
+ if (isDingtalkDebug()) console.log(`[DingTalk][MediaIdTrace] 远程图下载成功 | bytes=${buf.length} tmp=${tmp}`);
281
287
  return tmp;
282
288
  } catch (e) {
283
289
  console.warn(`[DingTalk][MediaIdTrace] 远程图下载异常 | ${e?.message || e}`);
@@ -312,27 +318,29 @@ function replaceOutsideImageTargets(content, original, replacement) {
312
318
  * - 本地:正文重复的本地路径换成 down.dingtalk.com 可点链接(仍同一条消息)
313
319
  * - **不再拆第二条 text 消息**
314
320
  *
315
- * 日志约定(always-on,查灰图必看):
316
- * - 开始 / 扫描每张 ![] / 本地 exists / 上传成败 / 远程下载 / 残留 / out
321
+ * 日志约定(默认静默;配置 debug: true 全量):
322
+ * - 有图上传/失败/灰图残留才 console;无图路径不刷屏
317
323
  */
318
324
  async function processImagesForOutbound(content, oapiToken, log) {
319
325
  logLocalImage(log, "processImagesForOutbound 开始", `contentLen=${content?.length ?? 0} hasToken=${!!oapiToken}`);
320
326
  if (!oapiToken) {
321
- logLocalImage(log, "processImagesForOutbound 跳过", "无 oapiToken(本地路径无法上传→必灰图)");
322
- logMediaIdTrace("processImagesForOutbound:skip-no-token", content, void 0, true);
327
+ if (/!\[[^\]]*\]\([^)]+\)/.test(content || "")) {
328
+ logLocalImage(log, "processImagesForOutbound 跳过", "无 oapiToken(本地路径无法上传→必灰图)");
329
+ logMediaIdTrace("processImagesForOutbound:skip-no-token", content);
330
+ }
323
331
  return {
324
332
  text: content || "",
325
333
  followUpUrls: []
326
334
  };
327
335
  }
328
336
  if (!content || typeof content !== "string") {
329
- logLocalImage(log, "processImagesForOutbound 跳过", "content 为空");
337
+ logLocalImage(log, "processImagesForOutbound 跳过", "content 为空", true);
330
338
  return {
331
339
  text: content || "",
332
340
  followUpUrls: []
333
341
  };
334
342
  }
335
- logMediaIdTrace("processImagesForOutbound:in", content, void 0, true);
343
+ logMediaIdTrace("processImagesForOutbound:in", content);
336
344
  const codeRanges = getMarkdownCodeRanges(content);
337
345
  if (codeRanges.length > 0) logLocalImage(log, "processImagesForOutbound 代码区域", `count=${codeRanges.length}(围栏/行内 code 内的图路径将跳过)`);
338
346
  const mdMatches = [...content.matchAll(LOCAL_IMAGE_RE)];
@@ -435,9 +443,10 @@ async function processImagesForOutbound(content, oapiToken, log) {
435
443
  if (m.index !== void 0 && isIndexInCodeRange(m.index, getMarkdownCodeRanges(result))) return false;
436
444
  return isLocalImageRef((m[2] || "").trim());
437
445
  }).map((m) => (m[2] || "").trim());
438
- logLocalImage(log, "processImagesForOutbound 完成", `uploads=${uploadCount} localTried=${localTried} remoteTried=${remoteTried} skippedCode=${skippedCode} skippedOther=${skippedOther} outLen=${result.length}` + (residual.length ? ` ⚠️residualLocal=${residual.length}:${residual.slice(0, 3).join("|")}` : " residualLocal=0"));
446
+ if (uploadCount > 0 || localTried > 0 || remoteTried > 0 || residual.length > 0) logLocalImage(log, residual.length ? "processImagesForOutbound 完成(有残留)" : "processImagesForOutbound 上传成功", `uploads=${uploadCount} localTried=${localTried} remoteTried=${remoteTried} skippedCode=${skippedCode} skippedOther=${skippedOther} outLen=${result.length}` + (residual.length ? ` ⚠️residualLocal=${residual.length}:${residual.slice(0, 3).join("|")}` : " residualLocal=0"));
447
+ else logLocalImage(log, "processImagesForOutbound 完成", `uploads=0 outLen=${result.length}`, true);
439
448
  if (residual.length) logLocalImage(log, "processImagesForOutbound ⚠️仍有代码块外本地路径未替换(钉钉必灰图)", residual.join(" | "));
440
- logMediaIdTrace("processImagesForOutbound:out", result, void 0, true);
449
+ logMediaIdTrace("processImagesForOutbound:out", result);
441
450
  return {
442
451
  text: result,
443
452
  followUpUrls: []
@@ -658,7 +667,7 @@ async function extractAudioDuration(filePath, log) {
658
667
  */
659
668
  async function sendVideoMessage(config, sessionWebhook, fileName, mediaId, log, metadata) {
660
669
  try {
661
- const token = await (await import("./utils-Bjh4r_qS.mjs")).getAccessToken(config);
670
+ const token = await (await import("./utils-Bwz2Fls_.mjs")).getAccessToken(config);
662
671
  const videoMessage = {
663
672
  msgtype: "video",
664
673
  video: {
@@ -686,8 +695,8 @@ async function sendVideoMessage(config, sessionWebhook, fileName, mediaId, log,
686
695
  */
687
696
  async function sendVideoProactive(config, target, videoMediaId, picMediaId, metadata, log) {
688
697
  try {
689
- const token = await (await import("./utils-Bjh4r_qS.mjs")).getAccessToken(config);
690
- const { DINGTALK_API } = await import("./utils-Bjh4r_qS.mjs");
698
+ const token = await (await import("./utils-Bwz2Fls_.mjs")).getAccessToken(config);
699
+ const { DINGTALK_API } = await import("./utils-Bwz2Fls_.mjs");
691
700
  const msgParam = {
692
701
  duration: metadata?.duration.toString() || "60000",
693
702
  videoMediaId,
@@ -732,8 +741,8 @@ async function sendVideoProactive(config, target, videoMediaId, picMediaId, meta
732
741
  */
733
742
  async function sendAudioProactive(config, target, fileName, mediaId, log, durationMs) {
734
743
  try {
735
- const token = await (await import("./utils-Bjh4r_qS.mjs")).getAccessToken(config);
736
- const { DINGTALK_API } = await import("./utils-Bjh4r_qS.mjs");
744
+ const token = await (await import("./utils-Bwz2Fls_.mjs")).getAccessToken(config);
745
+ const { DINGTALK_API } = await import("./utils-Bwz2Fls_.mjs");
737
746
  const msgParam = {
738
747
  mediaId,
739
748
  duration: durationMs && durationMs > 0 ? durationMs.toString() : "60000"
@@ -770,8 +779,8 @@ async function sendAudioProactive(config, target, fileName, mediaId, log, durati
770
779
  */
771
780
  async function sendFileProactive(config, target, fileInfo, mediaId, log) {
772
781
  try {
773
- const token = await (await import("./utils-Bjh4r_qS.mjs")).getAccessToken(config);
774
- const { DINGTALK_API } = await import("./utils-Bjh4r_qS.mjs");
782
+ const token = await (await import("./utils-Bwz2Fls_.mjs")).getAccessToken(config);
783
+ const { DINGTALK_API } = await import("./utils-Bwz2Fls_.mjs");
775
784
  const resolvedFileName = fileInfo.fileName || path.basename(fileInfo.path);
776
785
  const msgParam = {
777
786
  mediaId,
@@ -1,15 +1,15 @@
1
1
  import { a as resolveDingtalkAccount } from "./accounts-BAzdqkAV.mjs";
2
- import { r as CHANNEL_ID, t as getDingtalkRuntime } from "./runtime-A6G7WAWn.mjs";
3
- import { n as createLoggerFromConfig } from "./logger-mZ9OSbmD.mjs";
2
+ import { r as CHANNEL_ID, t as getDingtalkRuntime } from "./runtime-CjE4Z9G5.mjs";
3
+ import { n as createLoggerFromConfig, r as isDingtalkDebug } from "./logger-CnBTcwyq.mjs";
4
4
  import { t as dingtalkHttp } from "./http-client-DFWZgO1n.mjs";
5
5
  import { i as getOapiAccessToken } from "./utils-CIfI_3Jh.mjs";
6
- import { S as finalClean, _ as FINAL_TAG, a as sendTextMessage, b as estimateTokens, d as finishAICard, g as unregisterActiveCard, h as streamAICard, i as sendProactive, m as registerActiveCard, p as isQpsLimitError, r as sendMessage, t as sendMarkdownMessage, u as createAICardForTarget, v as PROCESS_TAG, x as extractFinal, y as displayClean } from "./messaging-DVS3UKOp.mjs";
6
+ import { S as finalClean, _ as FINAL_TAG, a as sendTextMessage, b as estimateTokens, d as finishAICard, g as unregisterActiveCard, h as streamAICard, i as sendProactive, m as registerActiveCard, p as isQpsLimitError, r as sendMessage, t as sendMarkdownMessage, u as createAICardForTarget, v as PROCESS_TAG, x as extractFinal, y as displayClean } from "./messaging-CK0Wz3Ze.mjs";
7
7
  import { a as QUEUE_BUSY_ACK_PHRASES, n as normalizeSlashCommand, t as buildSessionContext } from "./session-DJ4jYqPv.mjs";
8
8
  import { c as formatSenderRoles, f as getUserProfile, h as recallEmotionReply, l as getAccessToken, o as formatHiredDateShanghai, r as addEmotionReply, s as formatSenderDisplayLabel, t as DINGTALK_API, u as getOapiAccessToken$1 } from "./utils-legacy-DQ3HeGHh.mjs";
9
- import { r as trackBackgroundWork } from "./background-work-Cxb4b-91.mjs";
10
- import "./chunk-upload-BE71drPg.mjs";
11
- import { a as toLocalPath, i as VIDEO_MARKER_PATTERN, o as uploadMediaToDingTalk, r as FILE_MARKER_PATTERN, t as AUDIO_MARKER_PATTERN } from "./common-BZiVgd3A.mjs";
12
- import { n as lookupCardContent, t as collectCardLookupIds } from "./card-content-cache-bDQFz2u8.mjs";
9
+ import { r as trackBackgroundWork } from "./background-work-DkaSDqWk.mjs";
10
+ import "./chunk-upload-CIU5G_ay.mjs";
11
+ import { a as toLocalPath, i as VIDEO_MARKER_PATTERN, o as uploadMediaToDingTalk, r as FILE_MARKER_PATTERN, t as AUDIO_MARKER_PATTERN } from "./common-BfIYs1yC.mjs";
12
+ import { n as lookupCardContent, t as collectCardLookupIds } from "./card-content-cache-DT_OYThh.mjs";
13
13
  import * as fs from "fs";
14
14
  import * as path from "path";
15
15
  import * as os from "node:os";
@@ -63,7 +63,7 @@ function resolveAgentWorkspaceDir(cfg, agentId) {
63
63
  * 直接复用 media.ts 权威实现,避免 reply 路径与 message 路径两套逻辑分叉。
64
64
  */
65
65
  async function processLocalImages(content, oapiToken, log) {
66
- const { processLocalImages: impl } = await import("./media-BqpUGCni.mjs");
66
+ const { processLocalImages: impl } = await import("./media-DDtX37T2.mjs");
67
67
  return impl(content, oapiToken, log);
68
68
  }
69
69
  //#endregion
@@ -606,22 +606,20 @@ function createDingtalkReplyDispatcher(params) {
606
606
  type: "group",
607
607
  openConversationId: conversationId
608
608
  };
609
- console.log(`[DingTalk][LocalImage] closeStreaming 开始媒体处理 | target=${JSON.stringify(target)} hasToken=${!!oapiToken} textLen=${finalText.length}`);
610
609
  const mdImgCount = (finalText.match(/!\[[^\]]*\]\([^)]+\)/g) || []).length;
611
- console.log(`[DingTalk][LocalImage] closeStreaming 正文 markdown 图数量=${mdImgCount} preview=${JSON.stringify(finalText.slice(0, 200))}`);
610
+ if (mdImgCount > 0 || isDingtalkDebug()) log.info(`[DingTalk][LocalImage] closeStreaming 媒体处理 | target=${JSON.stringify(target)} hasToken=${!!oapiToken} textLen=${finalText.length} mdImgs=${mdImgCount}`);
612
611
  if (oapiToken) {
613
612
  const beforeImg = finalText;
614
613
  finalText = await processLocalImages(finalText, oapiToken, log);
615
- if (beforeImg !== finalText) console.log(`[DingTalk][LocalImage] closeStreaming 图片处理 | ${beforeImg.length}→${finalText.length}`);
614
+ if (beforeImg !== finalText) log.info(`[DingTalk][LocalImage] closeStreaming 图片已处理 | ${beforeImg.length}→${finalText.length}`);
616
615
  try {
617
- const { hasResidualLocalMdImagesOutsideCode } = await import("./media-BqpUGCni.mjs");
618
- if (hasResidualLocalMdImagesOutsideCode(finalText)) console.warn(`[DingTalk][LocalImage] closeStreaming 定稿后仍有代码块外的本地 MD 图未上传,钉钉可能灰图`);
616
+ const { hasResidualLocalMdImagesOutsideCode } = await import("./media-DDtX37T2.mjs");
617
+ if (hasResidualLocalMdImagesOutsideCode(finalText)) log.warn(`[DingTalk][LocalImage] closeStreaming 定稿后仍有代码块外的本地 MD 图未上传,钉钉可能灰图`);
619
618
  } catch {}
620
619
  finalText = await processVideoMarkers(finalText, "", account.config, oapiToken, log, true, target);
621
620
  finalText = await processAudioMarkers(finalText, "", account.config, oapiToken, log, true, target);
622
621
  finalText = await uploadAndReplaceFileMarkers(finalText, "", account.config, oapiToken, log, true, target);
623
- log.info(`[DingTalk][closeStreaming] 准备调用 processRawMediaPaths`);
624
- const { processRawMediaPaths } = await import("./media-BqpUGCni.mjs");
622
+ const { processRawMediaPaths } = await import("./media-DDtX37T2.mjs");
625
623
  finalText = await processRawMediaPaths(finalText, account.config, oapiToken, log, target);
626
624
  log.info(`[DingTalk][closeStreaming] processRawMediaPaths 处理完成`);
627
625
  } else log.warn(`[DingTalk][closeStreaming] oapiToken 为空,跳过媒体处理`);
@@ -774,11 +772,8 @@ function createDingtalkReplyDispatcher(params) {
774
772
  onSkip: (payload, info) => {
775
773
  const raw = readPayloadRawError(payload);
776
774
  const isErr = Boolean(payload?.isError);
777
- if (!raw && !isErr) {
778
- log.info(`[DingTalk][onSkip] kind=${info?.kind} reason=${info?.reason} 非错误静默(NO_REPLY/empty),跳过错误捕获`);
779
- return;
780
- }
781
- log.warn(`[DingTalk][onSkip] kind=${info?.kind} reason=${info?.reason} isError=${isErr} rawLen=${raw.length}(群聊静默失败常见)`);
775
+ if (!raw && !isErr) return;
776
+ log.warn(`[DingTalk][onSkip] 群聊静默失败 kind=${info?.kind} reason=${info?.reason} rawLen=${raw.length}`);
782
777
  captureErrorFromPayload(payload, `onSkip:${info?.kind ?? "?"}:${info?.reason ?? "?"}`);
783
778
  },
784
779
  onReplyStart: () => {
@@ -815,6 +810,13 @@ function createDingtalkReplyDispatcher(params) {
815
810
  await startStreaming();
816
811
  if (isNonAnswerPayload(payload)) {
817
812
  const isModelErrorFinal = Boolean(payload.isError) && info?.kind === "final" && Boolean(text.trim());
813
+ const existingAnswerText = (lastAnswerText.trim() || accumulatedText.trim()).trim();
814
+ if (isModelErrorFinal && /Agent couldn't generate a response/i.test(text) && existingAnswerText.length >= 20 && !/Agent couldn't generate a response/i.test(existingAnswerText)) {
815
+ pendingErrorText = null;
816
+ log.warn(`[DingTalk][deliver] 忽略 incomplete_turn 误报 isError final(卡片已有真实答案 len=${existingAnswerText.length}),不定格错误卡`);
817
+ await closeStreaming();
818
+ return;
819
+ }
818
820
  if (isModelErrorFinal) {
819
821
  accumulatedText = text;
820
822
  log.info(`[DingTalk][deliver] 将上游异常 final 文本存入 accumulatedText(len=${text.length}),防止 onIdle 覆盖`);
@@ -846,7 +848,7 @@ function createDingtalkReplyDispatcher(params) {
846
848
  const oapiToken = await getOapiAccessToken(account.config);
847
849
  if (oapiToken) {
848
850
  log.info(`[DingTalk][deliver] 检测到 final 响应,准备处理裸露文件路径`);
849
- const { processRawMediaPaths } = await import("./media-BqpUGCni.mjs");
851
+ const { processRawMediaPaths } = await import("./media-DDtX37T2.mjs");
850
852
  text = await processRawMediaPaths(text, account.config, oapiToken, log, target);
851
853
  log.info(`[DingTalk][deliver] 裸露文件路径处理完成`);
852
854
  }
@@ -976,7 +978,7 @@ function createDingtalkReplyDispatcher(params) {
976
978
  currentToolLine = "";
977
979
  await startStreaming();
978
980
  if (currentCardTarget) try {
979
- const { FILE_MARKER_PATTERN, VIDEO_MARKER_PATTERN, AUDIO_MARKER_PATTERN } = await import("./common-DgL28gCZ.mjs");
981
+ const { FILE_MARKER_PATTERN, VIDEO_MARKER_PATTERN, AUDIO_MARKER_PATTERN } = await import("./common-C0Oaf0z-.mjs");
980
982
  await enqueueCardStream(buildCardContent().replace(FILE_MARKER_PATTERN, "").replace(VIDEO_MARKER_PATTERN, "").replace(AUDIO_MARKER_PATTERN, "").trim(), { force: true });
981
983
  } catch (err) {
982
984
  if (!isQpsLimitError(err)) log.error(`[DingTalk][onPartialReply] final-marker 刷卡失败:${err.message}`);
@@ -989,7 +991,7 @@ function createDingtalkReplyDispatcher(params) {
989
991
  if (payload.text?.trim()) hasModelStreamText = true;
990
992
  currentToolLine = "";
991
993
  try {
992
- const { FILE_MARKER_PATTERN, VIDEO_MARKER_PATTERN, AUDIO_MARKER_PATTERN } = await import("./common-DgL28gCZ.mjs");
994
+ const { FILE_MARKER_PATTERN, VIDEO_MARKER_PATTERN, AUDIO_MARKER_PATTERN } = await import("./common-C0Oaf0z-.mjs");
993
995
  await enqueueCardStream(buildCardContent().replace(FILE_MARKER_PATTERN, "").replace(VIDEO_MARKER_PATTERN, "").replace(AUDIO_MARKER_PATTERN, "").trim());
994
996
  } catch (err) {
995
997
  if (isQpsLimitError(err)) log.warn(`[DingTalk][onPartialReply] QPS 限流,已排队尾随重试 latestLen=${latestCardContent.length}`);
@@ -1184,8 +1186,9 @@ function extractQuotedMsgText(container, maxDepth, conversationId) {
1184
1186
  conversationId,
1185
1187
  allowConversationRecent: false
1186
1188
  }) || "";
1187
- if (cardText) console.log(`[DingTalk][Quote] interactiveCard 已从 CardCache 精确回填 | ids=${ids.join(",") || "-"} len=${cardText.length}`);
1188
- else {
1189
+ if (cardText) {
1190
+ if (isDingtalkDebug()) console.log(`[DingTalk][Quote] interactiveCard 已从 CardCache 精确回填 | ids=${ids.join(",") || "-"} len=${cardText.length}`);
1191
+ } else if (isDingtalkDebug()) {
1189
1192
  const dump = JSON.stringify(repliedMsg ?? {}).slice(0, 800);
1190
1193
  console.log(`[DingTalk][Quote] interactiveCard 无正文且缓存未命中(未使用会话最近兜底) | conv=${conversationId || "-"} keys=${Object.keys(contentObj || {}).join(",")} repliedMsg=${dump}`);
1191
1194
  }
@@ -1208,13 +1211,13 @@ function extractQuotedMsgText(container, maxDepth, conversationId) {
1208
1211
  });
1209
1212
  if (cached) {
1210
1213
  bodyText = cached;
1211
- console.log(`[DingTalk][Quote] msgType=${msgType} 已从 CardCache 精确回填 | ids=${ids.join(",") || "-"} len=${cached.length}`);
1214
+ if (isDingtalkDebug()) console.log(`[DingTalk][Quote] msgType=${msgType} 已从 CardCache 精确回填 | ids=${ids.join(",") || "-"} len=${cached.length}`);
1212
1215
  } else if (/card|interactive/i.test(String(msgType || "")) || ids.length > 0) {
1213
1216
  bodyText = "钉钉卡片消息。";
1214
- console.log(`[DingTalk][Quote] 未知卡片类引用 msgType=${msgType} 已简化占位 | keys=${Object.keys(contentObj || {}).join(",")}`);
1215
- } else console.log(`[DingTalk][Quote] 未知引用类型 msgType=${msgType} keys=${Object.keys(contentObj || {}).join(",")} dump=${JSON.stringify(repliedMsg ?? {}).slice(0, 600)}`);
1217
+ if (isDingtalkDebug()) console.log(`[DingTalk][Quote] 未知卡片类引用 msgType=${msgType} 已简化占位 | keys=${Object.keys(contentObj || {}).join(",")}`);
1218
+ } else if (isDingtalkDebug()) console.log(`[DingTalk][Quote] 未知引用类型 msgType=${msgType} keys=${Object.keys(contentObj || {}).join(",")} dump=${JSON.stringify(repliedMsg ?? {}).slice(0, 600)}`);
1216
1219
  } catch {
1217
- console.log(`[DingTalk][Quote] 未知引用类型 msgType=${msgType} keys=${Object.keys(contentObj || {}).join(",")}`);
1220
+ if (isDingtalkDebug()) console.log(`[DingTalk][Quote] 未知引用类型 msgType=${msgType} keys=${Object.keys(contentObj || {}).join(",")}`);
1218
1221
  }
1219
1222
  break;
1220
1223
  }
@@ -2320,7 +2323,7 @@ async function handleDingTalkMessageInternal(params) {
2320
2323
  finalText = await processVideoMarkers(finalText, "", config, oapiToken, log, true, mediaTarget);
2321
2324
  finalText = await processAudioMarkers(finalText, "", config, oapiToken, log, true, mediaTarget);
2322
2325
  finalText = await uploadAndReplaceFileMarkers(finalText, "", config, oapiToken, log, true, mediaTarget);
2323
- const { processRawMediaPaths } = await import("./media-BqpUGCni.mjs");
2326
+ const { processRawMediaPaths } = await import("./media-DDtX37T2.mjs");
2324
2327
  finalText = await processRawMediaPaths(finalText, config, oapiToken, log, mediaTarget);
2325
2328
  }
2326
2329
  const textToSend = finalText.trim() || "✅ 任务执行完成(无文本输出)";
@@ -1,5 +1,5 @@
1
- import { c as logMediaIdTrace, d as processLocalImages, u as processImagesForOutbound, y as uploadMediaToDingTalk } from "./media-DnQB2BrL.mjs";
2
- import { n as createLoggerFromConfig } from "./logger-mZ9OSbmD.mjs";
1
+ import { c as logMediaIdTrace, d as processLocalImages, u as processImagesForOutbound, y as uploadMediaToDingTalk } from "./media-g8tJtpfU.mjs";
2
+ import { n as createLoggerFromConfig, r as isDingtalkDebug } from "./logger-CnBTcwyq.mjs";
3
3
  import { t as dingtalkHttp } from "./http-client-DFWZgO1n.mjs";
4
4
  import { i as getOapiAccessToken, r as getAccessToken, t as DINGTALK_API } from "./utils-CIfI_3Jh.mjs";
5
5
  import { r as MEDIA_MSG_TYPES } from "./session-DJ4jYqPv.mjs";
@@ -371,7 +371,7 @@ async function finishAICard(card, content, config, log, contentVar, skipInputing
371
371
  const fixedContent = ensureTableBlankLines(cleanContent);
372
372
  log?.info?.(`[DingTalk][AICard] 开始 finish(一次性定稿,无流式回放),最终内容长度=${fixedContent.length}`);
373
373
  try {
374
- const { rememberCardContent } = await import("./card-content-cache-c4Ls96eJ.mjs");
374
+ const { rememberCardContent } = await import("./card-content-cache-C7-XHhHw.mjs");
375
375
  rememberCardContent({
376
376
  text: fixedContent,
377
377
  outTrackId: card.cardInstanceId,
@@ -821,7 +821,7 @@ function isImageMediaPath(mediaUrl) {
821
821
  async function downloadRemoteMediaToTemp(url, log) {
822
822
  const tag = "[DingTalk][RemoteMedia]";
823
823
  try {
824
- console.log(`${tag} 开始下载 | ${url}`);
824
+ if (isDingtalkDebug()) console.log(`${tag} 开始下载 | ${url}`);
825
825
  const res = await fetch(url, {
826
826
  redirect: "follow",
827
827
  signal: AbortSignal.timeout(6e4),
@@ -858,7 +858,7 @@ async function downloadRemoteMediaToTemp(url, log) {
858
858
  const { writeFileSync } = await import("node:fs");
859
859
  const tmp = path.join(os.tmpdir(), `dingtalk-remote-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${ext}`);
860
860
  writeFileSync(tmp, buf);
861
- console.log(`${tag} 下载成功 | bytes=${buf.length} ct=${ct || "-"} tmp=${tmp}`);
861
+ if (isDingtalkDebug()) console.log(`${tag} 下载成功 | bytes=${buf.length} ct=${ct || "-"} tmp=${tmp}`);
862
862
  return tmp;
863
863
  } catch (err) {
864
864
  console.warn(`${tag} 下载异常 | ${err?.message || err}`);
@@ -876,7 +876,7 @@ async function resolveMediaSourceToLocalFile(mediaUrl, mediaLocalRoots, log) {
876
876
  cleanup: tmp
877
877
  };
878
878
  }
879
- const { toLocalPath } = await import("./media-BqpUGCni.mjs");
879
+ const { toLocalPath } = await import("./media-DDtX37T2.mjs");
880
880
  const _fs = await import("fs");
881
881
  const _path = await import("path");
882
882
  let resolved = toLocalPath(mediaUrl);
@@ -911,24 +911,22 @@ async function sendTextToDingTalk(params) {
911
911
  };
912
912
  }
913
913
  const targetParam = resolveOutboundTarget(target);
914
- console.log(`[DingTalk][LocalImage] sendTextToDingTalk 入口 | target=${target} textLen=${text?.length ?? 0}`);
915
- logMediaIdTrace("sendTextToDingTalk:in", text, `target=${target}`, true);
916
914
  const beforeLen = text?.length ?? 0;
915
+ const hasMdImg = /!\[[^\]]*\]\([^)]+\)/.test(text || "");
916
+ if (hasMdImg || isDingtalkDebug()) logMediaIdTrace("sendTextToDingTalk:in", text, `target=${target}`);
917
917
  try {
918
918
  const oapiToken = await getOapiAccessToken(config);
919
- console.log(`[DingTalk][LocalImage] sendTextToDingTalk 入口 | target=${target} textLen=${beforeLen} hasToken=${!!oapiToken}`);
920
919
  if (oapiToken && text) {
921
920
  text = (await processImagesForOutbound(text, oapiToken, log)).text;
922
- console.log(`[DingTalk][LocalImage] sendTextToDingTalk 处理后 | ${beforeLen}→${text.length} 字`);
923
- logMediaIdTrace("sendTextToDingTalk:after-process", text, void 0, true);
924
- } else if (!oapiToken) console.warn(`[DingTalk][LocalImage] sendTextToDingTalk 无 oapiToken,跳过图片上传(本地 MD 图会灰)`);
921
+ if (beforeLen !== text.length && (hasMdImg || isDingtalkDebug())) console.log(`[DingTalk][LocalImage] sendTextToDingTalk 处理后 | ${beforeLen}→${text.length} 字`);
922
+ if (hasMdImg || isDingtalkDebug()) logMediaIdTrace("sendTextToDingTalk:after-process", text);
923
+ } else if (!oapiToken && hasMdImg) console.warn(`[DingTalk][LocalImage] sendTextToDingTalk 无 oapiToken,跳过图片上传(本地 MD 图会灰)`);
925
924
  } catch (err) {
926
925
  console.warn(`[DingTalk][LocalImage] sendTextToDingTalk 图片处理失败: ${err?.message || err}`);
927
926
  }
928
927
  const msgType = shouldSendAsMarkdown(text) ? "markdown" : "text";
929
928
  const cardOpts = messageToolProactiveCardOptions(config);
930
- logMediaIdTrace("sendTextToDingTalk:API前", text, `msgType=${msgType} messageAnswerCard=${isMessageAnswerCardEnabled(config)} useAICard=${cardOpts.useAICard}`, true);
931
- console.log(`[DingTalk][MessageCard] sendTextToDingTalk | messageAnswerCard=${isMessageAnswerCardEnabled(config)} useAICard=${cardOpts.useAICard} tpl=${cardOpts.cardTemplateId || "-"}`);
929
+ if (hasMdImg || isDingtalkDebug()) logMediaIdTrace("sendTextToDingTalk:API前", text, `msgType=${msgType} messageAnswerCard=${isMessageAnswerCardEnabled(config)} useAICard=${cardOpts.useAICard}`);
932
930
  return sendProactive(config, targetParam, text, {
933
931
  msgType,
934
932
  replyToId,
@@ -1090,7 +1088,7 @@ async function sendMediaToDingTalk(params) {
1090
1088
  const mediaId = uploadResult.mediaId;
1091
1089
  const imgsInCaption = (caption.match(/!\[[^\]]*\]\([^)]+\)/g) || []).length;
1092
1090
  const mergeMd = config.messageImageMd === true && caption.length > 0 && imgsInCaption >= 1;
1093
- console.log(`[DingTalk][LocalImage] sendMedia 图片策略 | messageImageMd=${config.messageImageMd === true} imgsInCaption=${imgsInCaption} mergeMd=${mergeMd} remote=${isRemoteHttpUrl(mediaUrl)}`);
1091
+ if (isDingtalkDebug()) console.log(`[DingTalk][LocalImage] sendMedia 图片策略 | messageImageMd=${config.messageImageMd === true} imgsInCaption=${imgsInCaption} mergeMd=${mergeMd} remote=${isRemoteHttpUrl(mediaUrl)}`);
1094
1092
  if (mergeMd) {
1095
1093
  const result = await sendProactive(config, targetParam, `${caption}\n\n![](${mediaId})`, {
1096
1094
  msgType: "markdown",
@@ -1145,7 +1143,7 @@ async function sendMediaToDingTalk(params) {
1145
1143
  const fileName = mediaUrl.split("/").filter(Boolean).pop()?.split("?")[0] || "file";
1146
1144
  if (mediaType === "video") {
1147
1145
  const videoMarker = `[DINGTALK_VIDEO]{"path":"${resolvedMediaUrl}"}[/DINGTALK_VIDEO]`;
1148
- const { processVideoMarkers } = await import("./media-BqpUGCni.mjs");
1146
+ const { processVideoMarkers } = await import("./media-DDtX37T2.mjs");
1149
1147
  await processVideoMarkers(videoMarker, "", config, oapiToken, console, true, targetParam);
1150
1148
  return {
1151
1149
  ok: true,
@@ -1164,7 +1162,7 @@ async function sendMediaToDingTalk(params) {
1164
1162
  fileName,
1165
1163
  fileType: ext && ext.length < 12 ? ext : "file"
1166
1164
  };
1167
- const { sendFileProactive } = await import("./media-BqpUGCni.mjs");
1165
+ const { sendFileProactive } = await import("./media-DDtX37T2.mjs");
1168
1166
  await sendFileProactive(config, targetParam, fileInfo, uploadResult.mediaId, log);
1169
1167
  return {
1170
1168
  ok: true,
@@ -1174,7 +1172,7 @@ async function sendMediaToDingTalk(params) {
1174
1172
  } finally {
1175
1173
  if (cleanupTemp) try {
1176
1174
  _fs.unlinkSync(cleanupTemp);
1177
- console.log(`[DingTalk][RemoteMedia] 已清理临时文件 | ${cleanupTemp}`);
1175
+ if (isDingtalkDebug()) console.log(`[DingTalk][RemoteMedia] 已清理临时文件 | ${cleanupTemp}`);
1178
1176
  } catch {}
1179
1177
  }
1180
1178
  } catch (err) {
@@ -1249,7 +1247,7 @@ async function sendProactiveInternal(config, target, content, options) {
1249
1247
  const card = await createAICardForTarget(config, target, externalLog, cardTemplateId);
1250
1248
  if (card) {
1251
1249
  await finishAICard(card, content, config, externalLog, void 0, skipInputingWalk);
1252
- console.log(`[DingTalk][MessageCard] AI Card 发送成功 | outTrack=${card.cardInstanceId} tpl=${cardTemplateId || "default"} skipInputing=${skipInputingWalk}`);
1250
+ if (isDingtalkDebug()) console.log(`[DingTalk][MessageCard] AI Card 发送成功 | outTrack=${card.cardInstanceId} tpl=${cardTemplateId || "default"} skipInputing=${skipInputingWalk}`);
1253
1251
  return {
1254
1252
  ok: true,
1255
1253
  cardInstanceId: card.cardInstanceId,
@@ -1292,7 +1290,7 @@ async function sendProactiveInternal(config, target, content, options) {
1292
1290
  usedAICard: false
1293
1291
  };
1294
1292
  }
1295
- logMediaIdTrace("API出站", content, `msgKey=${payload.msgKey} type=${msgType}`, true);
1293
+ if (/!\[[^\]]*\]\([^)]+\)/.test(content || "") || isDingtalkDebug()) logMediaIdTrace("API出站", content, `msgKey=${payload.msgKey} type=${msgType}`);
1296
1294
  const body = {
1297
1295
  robotCode: String(config.clientId),
1298
1296
  msgKey: payload.msgKey,
@@ -1,9 +1,9 @@
1
- import { b as __exportAll } from "./media-DnQB2BrL.mjs";
1
+ import { b as __exportAll } from "./media-g8tJtpfU.mjs";
2
2
  import { a as resolveDingtalkAccount, c as addWildcardAllowFrom, d as hasConfiguredSecretInput, f as normalizeAccountId, l as createDefaultChannelRuntimeState, m as resolveDefaultGroupPolicy, o as resolveDingtalkCredentials, p as resolveAllowlistProviderRuntimeGroupPolicy, r as resolveDefaultDingtalkAccountId, s as DEFAULT_ACCOUNT_ID, t as listDingtalkAccountIds, u as formatDocsLink } from "./accounts-BAzdqkAV.mjs";
3
- import { t as createLogger } from "./logger-mZ9OSbmD.mjs";
3
+ import { t as createLogger } from "./logger-CnBTcwyq.mjs";
4
4
  import { t as dingtalkHttp } from "./http-client-DFWZgO1n.mjs";
5
5
  import "./utils-CIfI_3Jh.mjs";
6
- import { S as finalClean, f as getActiveCardForConversation, n as sendMediaToDingTalk, o as sendTextToDingTalk } from "./messaging-DVS3UKOp.mjs";
6
+ import { S as finalClean, f as getActiveCardForConversation, n as sendMediaToDingTalk, o as sendTextToDingTalk } from "./messaging-CK0Wz3Ze.mjs";
7
7
  import { createRequire } from "node:module";
8
8
  import { z, z as z$1 } from "zod";
9
9
  //#region src/secret-input.ts
@@ -92,6 +92,7 @@ const DingtalkSharedConfigShape = {
92
92
  asyncMode: z$1.boolean().optional(),
93
93
  ackText: z$1.string().optional(),
94
94
  endpoint: z$1.string().optional(),
95
+ /** 调试日志:连接/入站摘要、图片 mediaId 追踪、引用回填、队列计数等(默认关,生产勿开) */
95
96
  debug: z$1.boolean().optional(),
96
97
  enableMediaUpload: z$1.boolean().optional(),
97
98
  systemPrompt: z$1.string().optional(),
@@ -955,8 +956,8 @@ async function monitorDingtalkProvider(opts = {}) {
955
956
  const log = createLogger(cfg.channels?.["dingtalk-connector"]?.debug ?? false);
956
957
  const [accountsModule, monitorAccountModule, monitorSingleModule] = await Promise.all([
957
958
  import("./accounts-BQptOmgB.mjs"),
958
- import("./message-handler-B1usz1wb.mjs"),
959
- import("./connection-CZ7ZoN5_.mjs")
959
+ import("./message-handler-BeTQQCUe.mjs"),
960
+ import("./connection-obtrPkj4.mjs")
960
961
  ]);
961
962
  const { resolveDingtalkAccount, listEnabledDingtalkAccounts } = accountsModule;
962
963
  const { handleDingTalkMessage } = monitorAccountModule;
@@ -1,4 +1,4 @@
1
- import "./logger-mZ9OSbmD.mjs";
1
+ import "./logger-CnBTcwyq.mjs";
2
2
  import { r as getAccessToken, t as DINGTALK_API } from "./utils-CIfI_3Jh.mjs";
3
3
  import "./session-DJ4jYqPv.mjs";
4
4
  export { DINGTALK_API, getAccessToken };
@@ -2,13 +2,13 @@
2
2
 
3
3
  **发布日期:** 2026-07-25
4
4
  **npm:** `@jeik/dingtalk-connector@0.8.28`
5
- **定位:** 基于官方 **0.8.24** 的社区增强正式版
5
+ **定位:** 基于官方 **0.8.24**,叠加本仓库独有优化的正式版
6
6
 
7
7
  ---
8
8
 
9
9
  ## 一句话
10
10
 
11
- 连接更稳、消息更少丢、模型出错时提示更清楚;官方 0.8.24 的长连接改进已吸收,社区图片/答案卡等增强继续保留。
11
+ 连接更稳、消息更少丢、模型出错时提示更清楚;官方 0.8.24 的长连接改进已吸收,**本仓库独有**的图片/答案卡等优化继续保留。
12
12
 
13
13
  ---
14
14
 
@@ -22,13 +22,13 @@
22
22
 
23
23
  ---
24
24
 
25
- ## 社区额外优化(效果)
25
+ ## 本仓库额外优化(效果)
26
26
 
27
27
  1. **真正能收消息才显示已连接** — 减少「显示在线、发了没反应」
28
28
  2. **连发 / 错误后 / 刚回完再发** — 消息进网关的成功率明显提高
29
29
  3. **上一条还在跑时再发** — 更容易马上看到排队提示和思考中表情
30
30
  4. **模型 503、欠费、无可用线路等** — 更容易定格成中文错误说明,少卡转圈
31
- 5. **原有社区能力** — 大段答案独立卡片、图片全路径、中文错误映射等均保留
31
+ 5. **本仓库独有能力** — 大段答案独立卡片、图片全路径、中文错误映射等均保留
32
32
 
33
33
  ---
34
34
 
@@ -0,0 +1,72 @@
1
+ # Release Notes · v0.8.29
2
+
3
+ **发布日期:** 2026-07-25
4
+ **npm:** `@jeik/dingtalk-connector@0.8.29`
5
+ **定位:** 正式加固版(日志 / 排障 / 文档)
6
+
7
+ ---
8
+
9
+ ## 一句话
10
+
11
+ 默认日志更干净;排障只需配置 `"debug": true`。连接、消息、错误卡能力继承 0.8.28。
12
+
13
+ ---
14
+
15
+ ## 本版变更
16
+
17
+ 1. **默认静默** — 无图正常回复不再刷 LocalImage / MediaIdTrace / CardCache
18
+ 2. **debug 配置** — 打开后输出连接、图片 mediaId、引用回填、队列等详细日志(无需环境变量)
19
+ 3. **文档** — 安装改为三条递进命令(最新 → 指定版本 → 官方源)
20
+
21
+ ---
22
+
23
+ ## 排障
24
+
25
+ ```json
26
+ "channels": {
27
+ "dingtalk-connector": {
28
+ "debug": true
29
+ }
30
+ }
31
+ ```
32
+
33
+ 改完后:`openclaw gateway restart`。用完请改回 `false`。
34
+
35
+ ---
36
+
37
+ ## 安装
38
+
39
+ **npx(一键扫码,按顺序试):**
40
+
41
+ ```bash
42
+ # 1)装最新
43
+ npx @jeik/dingtalk-connector install --force && openclaw gateway restart
44
+
45
+ # 2)若不是最新,指定版本
46
+ npx @jeik/dingtalk-connector@0.8.29 install --force && openclaw gateway restart
47
+
48
+ # 3)仍失败时走 npm 官方源
49
+ NPM_CONFIG_REGISTRY=https://registry.npmjs.org npx @jeik/dingtalk-connector@0.8.29 install --force && openclaw gateway restart
50
+ ```
51
+
52
+ **openclaw plugins install(只装插件,按顺序试):**
53
+
54
+ ```bash
55
+ # 1)装最新
56
+ openclaw plugins install @jeik/dingtalk-connector --force && openclaw gateway restart
57
+
58
+ # 2)若不是最新,指定版本
59
+ openclaw plugins install @jeik/dingtalk-connector@0.8.29 --force && openclaw gateway restart
60
+
61
+ # 3)仍失败时走 npm 官方源
62
+ NPM_CONFIG_REGISTRY=https://registry.npmjs.org openclaw plugins install @jeik/dingtalk-connector@0.8.29 --force && openclaw gateway restart
63
+ ```
64
+
65
+ ---
66
+
67
+ ## 建议验收
68
+
69
+ 1. 默认:纯文本对话日志无 LocalImage / MediaIdTrace 刷屏
70
+ 2. `"debug": true` 重启后:可见图片/引用类详细日志
71
+ 3. 模型 503:错误卡中文定稿,不永久转圈
72
+ 4. 任务进行中再发:有排队/处理中反馈