@dingtalk-real-ai/dingtalk-connector 0.8.8 → 0.8.9

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 (64) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/openclaw.plugin.json +2 -7
  3. package/package.json +1 -1
  4. package/src/channel.ts +25 -50
  5. package/src/config/schema.ts +8 -4
  6. package/src/core/connection.ts +25 -0
  7. package/src/core/message-handler.ts +465 -73
  8. package/src/reply-dispatcher.ts +11 -26
  9. package/src/sdk/types.ts +0 -2
  10. package/src/services/media/chunk-upload.ts +3 -2
  11. package/src/services/media/common.ts +2 -1
  12. package/src/services/media/image.ts +33 -27
  13. package/src/services/media.ts +3 -2
  14. package/src/services/messaging.ts +22 -2
  15. package/skills/dws/SKILL.md +0 -124
  16. package/skills/dws/references/error-codes.md +0 -93
  17. package/skills/dws/references/field-rules.md +0 -105
  18. package/skills/dws/references/global-reference.md +0 -36
  19. package/skills/dws/references/intent-guide.md +0 -179
  20. package/skills/dws/references/products/aiapp.md +0 -412
  21. package/skills/dws/references/products/aitable-record-ops.md +0 -135
  22. package/skills/dws/references/products/aitable.md +0 -511
  23. package/skills/dws/references/products/attendance.md +0 -93
  24. package/skills/dws/references/products/calendar.md +0 -217
  25. package/skills/dws/references/products/chat.md +0 -297
  26. package/skills/dws/references/products/contact.md +0 -108
  27. package/skills/dws/references/products/ding.md +0 -57
  28. package/skills/dws/references/products/doc.md +0 -371
  29. package/skills/dws/references/products/drive.md +0 -140
  30. package/skills/dws/references/products/mail.md +0 -109
  31. package/skills/dws/references/products/minutes.md +0 -204
  32. package/skills/dws/references/products/oa.md +0 -180
  33. package/skills/dws/references/products/report.md +0 -164
  34. package/skills/dws/references/products/simple.md +0 -110
  35. package/skills/dws/references/products/todo.md +0 -146
  36. package/skills/dws/references/products/workbench.md +0 -39
  37. package/skills/dws/references/url-patterns.md +0 -12
  38. package/skills/dws/scripts/aiapp_create_and_poll.py +0 -138
  39. package/skills/dws/scripts/attendance_my_record.py +0 -91
  40. package/skills/dws/scripts/attendance_team_shift.py +0 -89
  41. package/skills/dws/scripts/bot_broadcast.py +0 -112
  42. package/skills/dws/scripts/bulk_add_fields.py +0 -250
  43. package/skills/dws/scripts/calendar_free_slot_finder.py +0 -195
  44. package/skills/dws/scripts/calendar_schedule_meeting.py +0 -159
  45. package/skills/dws/scripts/calendar_today_agenda.py +0 -132
  46. package/skills/dws/scripts/chat_export_messages.py +0 -136
  47. package/skills/dws/scripts/chat_history_with_user.py +0 -142
  48. package/skills/dws/scripts/contact_dept_members.py +0 -110
  49. package/skills/dws/scripts/doc_create_and_write.py +0 -112
  50. package/skills/dws/scripts/drive_tree_list.py +0 -124
  51. package/skills/dws/scripts/finance_daily_cashflow.py +0 -70
  52. package/skills/dws/scripts/finance_expense_flow.py +0 -128
  53. package/skills/dws/scripts/import_records.py +0 -330
  54. package/skills/dws/scripts/mail_send_with_cc.py +0 -122
  55. package/skills/dws/scripts/mail_unread_summary.py +0 -114
  56. package/skills/dws/scripts/minutes_extract_todos.py +0 -110
  57. package/skills/dws/scripts/minutes_recent_summary.py +0 -114
  58. package/skills/dws/scripts/oa_batch_approve.py +0 -136
  59. package/skills/dws/scripts/oa_pending_review.py +0 -118
  60. package/skills/dws/scripts/report_inbox_today.py +0 -114
  61. package/skills/dws/scripts/todo_batch_create.py +0 -159
  62. package/skills/dws/scripts/todo_daily_summary.py +0 -169
  63. package/skills/dws/scripts/todo_overdue_check.py +0 -122
  64. package/skills/dws/scripts/upload_attachment.py +0 -190
package/CHANGELOG.md CHANGED
@@ -5,6 +5,44 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.8.9] - 2026-03-31
9
+
10
+ ### 新增 / Added
11
+ - ✨ **引用消息完整解析** - 新增 `extractQuotedMsgText` 递归解析引用消息(最多 3 层嵌套),支持 text、richText、picture、video、audio、file、markdown、interactiveCard 等消息类型,自动提取引用中的媒体附件和链接
12
+ **Quoted message full parsing** - Added recursive quoted message parsing (up to 3 levels) with media attachment and URL extraction
13
+
14
+ - ✨ **新增配置项 asyncMode / ackText / endpoint / debug** - `configSchema` 新增四个配置字段
15
+ **New config options** - Added `asyncMode`, `ackText`, `endpoint`, `debug` to configSchema
16
+
17
+ - ✨ **普通消息本地图片后处理** - `sendNormalToUser` 和 `sendNormalToGroup` 新增本地图片上传后处理,发送普通消息时自动替换本地图片路径为 media_id
18
+ **Local image post-processing for normal messages** - Added automatic local image upload and replacement in `sendNormalToUser` and `sendNormalToGroup`
19
+
20
+ ### 修复 / Fixes
21
+ - 🐛 **macOS LaunchAgent 环境 WebSocket 连接失败** - 修复 macOS LaunchAgent/daemon 环境下 fd 0/1/2 无效(EBADF)导致 TCP 连接创建失败的问题
22
+ **WebSocket connection failure on macOS LaunchAgent** - Fixed EBADF errors on macOS LaunchAgent environments by redirecting invalid file descriptors to `/dev/null`
23
+
24
+ - 🐛 **AI Card 流式关闭竞争条件** - 修复 `closeStreaming` 被 `onIdle` 和 `onError` 同时触发时的竞争条件,采用 snapshot 模式防止并发崩溃
25
+ **AI Card streaming close race condition** - Fixed race condition in `closeStreaming` using snapshot pattern to prevent concurrent crashes
26
+
27
+ - 🐛 **FormData CJS 互操作问题** - 将 `form-data` 从动态 import 改为静态 import,修复 jiti/ESM 环境下 `.default` 偶发为 undefined 的问题
28
+ **FormData CJS interop issue** - Changed `form-data` from dynamic to static import, fixing intermittent `.default` undefined errors in jiti/ESM
29
+
30
+ - 🐛 **纯文本图片路径误转换** - 禁用纯文本中本地图片路径自动转换为图片语法的行为,避免影响用户展示路径文本的场景
31
+ **Bare image path false conversion** - Disabled automatic conversion of bare local image paths to image syntax
32
+
33
+ ### 改进 / Improvements
34
+ - ✅ **Zod Schema 拆分兼容 Web UI** - 将 `DingtalkConfigSchema` 拆分为 `DingtalkConfigBaseSchema` 和带 `superRefine` 的完整 Schema,解决 JSON Schema 生成兼容性问题
35
+ **Zod Schema split for Web UI compatibility** - Split schema to fix `buildChannelConfigSchema` JSON Schema generation
36
+
37
+ - ✅ **configSchema 类型简化** - 将 `clientId`、`clientSecret` 等字段从 `oneOf` 联合类型简化为单一 `string` 类型
38
+ **configSchema type simplification** - Simplified JSON Schema from `oneOf` union types to single `string` type
39
+
40
+ - ✅ **reply-dispatcher logger 统一** - 替换手动构建的 log 对象为 `createLoggerFromConfig`
41
+ **reply-dispatcher logger unification** - Replaced manual log object with `createLoggerFromConfig`
42
+
43
+ - ✅ **锁定 axios 版本到 1.6.0** - 避免自动升级引入不兼容变更
44
+ **Pin axios to 1.6.0** - Prevent automatic upgrades from introducing incompatible changes
45
+
8
46
  ## [0.8.8] - 2026-03-29
9
47
 
10
48
  ### 修复 / Fixes
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "dingtalk-connector",
3
3
  "name": "DingTalk Channel",
4
- "version": "0.8.8",
4
+ "version": "0.8.9",
5
5
  "description": "DingTalk (钉钉) messaging channel via Stream mode with AI Card streaming",
6
6
  "author": "DingTalk Real Team",
7
7
  "main": "index.ts",
@@ -11,11 +11,6 @@
11
11
  "configSchema": {
12
12
  "type": "object",
13
13
  "additionalProperties": false,
14
- "properties": {
15
- "enabled": {
16
- "type": "boolean",
17
- "default": true
18
- }
19
- }
14
+ "properties": {}
20
15
  }
21
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dingtalk-real-ai/dingtalk-connector",
3
- "version": "0.8.8",
3
+ "version": "0.8.9",
4
4
  "description": "DingTalk (钉钉) channel connector — Stream mode with AI Card streaming",
5
5
  "main": "index.ts",
6
6
  "type": "module",
package/src/channel.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import type {
2
- ChannelMeta,
3
2
  ChannelPlugin,
4
3
  ClawdbotConfig,
5
4
  } from "openclaw/plugin-sdk";
@@ -30,33 +29,17 @@ import { monitorDingtalkProvider } from "./core/provider.ts";
30
29
  import { sendTextToDingTalk, sendMediaToDingTalk } from "./services/messaging/index.ts";
31
30
  import type { ResolvedDingtalkAccount, DingtalkConfig } from "./types/index.ts";
32
31
 
33
- const meta: ChannelMeta = {
32
+ const meta = {
34
33
  id: "dingtalk-connector",
35
34
  label: "DingTalk",
36
35
  selectionLabel: "DingTalk (钉钉)",
37
36
  docsPath: "/channels/dingtalk-connector",
38
37
  docsLabel: "dingtalk-connector",
39
38
  blurb: "钉钉企业内部机器人,使用 Stream 模式,无需公网 IP,支持 AI Card 流式响应。",
40
- aliases: ["dd", "ding"],
39
+ aliases: ["dd", "ding"] as string[],
41
40
  order: 70,
42
41
  };
43
42
 
44
- const secretInputJsonSchema = {
45
- oneOf: [
46
- { type: "string" },
47
- {
48
- type: "object",
49
- additionalProperties: false,
50
- required: ["source", "provider", "id"],
51
- properties: {
52
- source: { type: "string", enum: ["env", "file", "exec"] },
53
- provider: { type: "string", minLength: 1 },
54
- id: { type: "string", minLength: 1 },
55
- },
56
- },
57
- ],
58
- } as const;
59
-
60
43
  export const dingtalkPlugin: ChannelPlugin<ResolvedDingtalkAccount> = {
61
44
  id: "dingtalk-connector",
62
45
  meta: {
@@ -100,30 +83,27 @@ export const dingtalkPlugin: ChannelPlugin<ResolvedDingtalkAccount> = {
100
83
  properties: {
101
84
  enabled: { type: "boolean" },
102
85
  defaultAccount: { type: "string" },
103
- clientId: { oneOf: [{ type: "string" }, { type: "number" }] },
104
- clientSecret: secretInputJsonSchema,
86
+ clientId: { type: "string" },
87
+ clientSecret: { type: "string" },
105
88
  enableMediaUpload: { type: "boolean" },
106
89
  systemPrompt: { type: "string" },
107
90
  dmPolicy: { type: "string", enum: ["open", "pairing", "allowlist"] },
108
- allowFrom: { type: "array", items: { oneOf: [{ type: "string" }, { type: "number" }] } },
91
+ allowFrom: { type: "array", items: { type: "string" } },
109
92
  groupPolicy: { type: "string", enum: ["open", "allowlist", "disabled"] },
110
- groupAllowFrom: {
111
- type: "array",
112
- items: { oneOf: [{ type: "string" }, { type: "number" }] },
113
- },
93
+ groupAllowFrom: { type: "array", items: { type: "string" } },
114
94
  requireMention: { type: "boolean" },
115
- groupSessionScope: {
116
- type: "string",
117
- enum: ["group", "group_sender"],
118
- },
95
+ groupSessionScope: { type: "string", enum: ["group", "group_sender"] },
119
96
  separateSessionByConversation: { type: "boolean" },
120
97
  sharedMemoryAcrossConversations: { type: "boolean" },
121
98
  historyLimit: { type: "integer", minimum: 0 },
122
- dmHistoryLimit: { type: "integer", minimum: 0 },
123
99
  textChunkLimit: { type: "integer", minimum: 1 },
124
100
  mediaMaxMb: { type: "number", minimum: 0 },
125
101
  typingIndicator: { type: "boolean" },
126
102
  resolveSenderNames: { type: "boolean" },
103
+ asyncMode: { type: "boolean" },
104
+ ackText: { type: "string" },
105
+ endpoint: { type: "string" },
106
+ debug: { type: "boolean" },
127
107
  tools: {
128
108
  type: "object",
129
109
  additionalProperties: false,
@@ -146,12 +126,9 @@ export const dingtalkPlugin: ChannelPlugin<ResolvedDingtalkAccount> = {
146
126
  },
147
127
  },
148
128
  enabled: { type: "boolean" },
149
- allowFrom: { type: "array", items: { oneOf: [{ type: "string" }, { type: "number" }] } },
129
+ allowFrom: { type: "array", items: { type: "string" } },
150
130
  systemPrompt: { type: "string" },
151
- groupSessionScope: {
152
- type: "string",
153
- enum: ["group", "group_sender"],
154
- },
131
+ groupSessionScope: { type: "string", enum: ["group", "group_sender"] },
155
132
  },
156
133
  },
157
134
  },
@@ -162,28 +139,26 @@ export const dingtalkPlugin: ChannelPlugin<ResolvedDingtalkAccount> = {
162
139
  properties: {
163
140
  enabled: { type: "boolean" },
164
141
  name: { type: "string" },
165
- clientId: { oneOf: [{ type: "string" }, { type: "number" }] },
166
- clientSecret: secretInputJsonSchema,
142
+ clientId: { type: "string" },
143
+ clientSecret: { type: "string" },
167
144
  enableMediaUpload: { type: "boolean" },
168
145
  systemPrompt: { type: "string" },
169
146
  dmPolicy: { type: "string", enum: ["open", "pairing", "allowlist"] },
170
- allowFrom: { type: "array", items: { oneOf: [{ type: "string" }, { type: "number" }] } },
147
+ allowFrom: { type: "array", items: { type: "string" } },
171
148
  groupPolicy: { type: "string", enum: ["open", "allowlist", "disabled"] },
172
- groupAllowFrom: {
173
- type: "array",
174
- items: { oneOf: [{ type: "string" }, { type: "number" }] },
175
- },
149
+ groupAllowFrom: { type: "array", items: { type: "string" } },
176
150
  requireMention: { type: "boolean" },
177
- groupSessionScope: {
178
- type: "string",
179
- enum: ["group", "group_sender"],
180
- },
151
+ groupSessionScope: { type: "string", enum: ["group", "group_sender"] },
181
152
  separateSessionByConversation: { type: "boolean" },
182
153
  sharedMemoryAcrossConversations: { type: "boolean" },
183
154
  historyLimit: { type: "integer", minimum: 0 },
184
155
  textChunkLimit: { type: "integer", minimum: 1 },
185
156
  mediaMaxMb: { type: "number", minimum: 0 },
186
157
  typingIndicator: { type: "boolean" },
158
+ asyncMode: { type: "boolean" },
159
+ ackText: { type: "string" },
160
+ endpoint: { type: "string" },
161
+ debug: { type: "boolean" },
187
162
  tools: {
188
163
  type: "object",
189
164
  additionalProperties: false,
@@ -342,7 +317,7 @@ export const dingtalkPlugin: ChannelPlugin<ResolvedDingtalkAccount> = {
342
317
  };
343
318
  },
344
319
  },
345
- onboarding: dingtalkOnboardingAdapter,
320
+ setupWizard: dingtalkOnboardingAdapter as any,
346
321
  messaging: {
347
322
  normalizeTarget: (raw) => normalizeDingtalkTarget(raw) ?? undefined,
348
323
  targetResolver: {
@@ -465,7 +440,7 @@ export const dingtalkPlugin: ChannelPlugin<ResolvedDingtalkAccount> = {
465
440
  },
466
441
  },
467
442
  status: {
468
- defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID, { port: null }),
443
+ defaultRuntime: createDefaultChannelRuntimeState(DEFAULT_ACCOUNT_ID, { port: null }) as any,
469
444
  buildChannelSummary: ({ snapshot }) => ({
470
445
  // 只返回 probe 相关字段,不透传运行时字段(running/lastStartAt 等)。
471
446
  // 运行时状态由框架从 store.runtimes 自动维护,buildChannelSummary 在 probe
@@ -572,7 +547,7 @@ export const dingtalkPlugin: ChannelPlugin<ResolvedDingtalkAccount> = {
572
547
  });
573
548
  } catch (err: any) {
574
549
  // 打印真实错误到 stderr,绕过框架 log 系统(框架的 runtime.log 可能未初始化)
575
- ctx.log?.error(`[dingtalk-connector][${ctx.accountId}] startAccount error:`, err?.message ?? err, err?.stack);
550
+ ctx.log?.error(`[dingtalk-connector][${ctx.accountId}] startAccount error: ${err?.message ?? err}\n${err?.stack ?? ''}`);
576
551
  throw err;
577
552
  }
578
553
  },
@@ -54,7 +54,6 @@ const DingtalkSharedConfigShape = {
54
54
  requireMention: z.boolean().optional(),
55
55
  groups: z.record(z.string(), DingtalkGroupSchema.optional()).optional(),
56
56
  historyLimit: z.number().int().min(0).optional(),
57
- dmHistoryLimit: z.number().int().min(0).optional(),
58
57
  textChunkLimit: z.number().int().positive().optional(),
59
58
  mediaMaxMb: z.number().positive().optional(),
60
59
  tools: DingtalkToolsConfigSchema,
@@ -83,7 +82,11 @@ export const DingtalkAccountConfigSchema = z
83
82
  })
84
83
  .strict();
85
84
 
86
- export const DingtalkConfigSchema = z
85
+ /**
86
+ * Base schema (ZodObject) without superRefine, used for JSON Schema generation (Web UI).
87
+ * superRefine turns the schema into ZodEffects which is not compatible with buildChannelConfigSchema.
88
+ */
89
+ export const DingtalkConfigBaseSchema = z
87
90
  .object({
88
91
  enabled: z.boolean().optional(),
89
92
  defaultAccount: z.string().optional(),
@@ -102,8 +105,9 @@ export const DingtalkConfigSchema = z
102
105
  // Multi-account configuration
103
106
  accounts: z.record(z.string(), DingtalkAccountConfigSchema.optional()).optional(),
104
107
  })
105
- .strict()
106
- .superRefine((value, ctx) => {
108
+ .strict();
109
+
110
+ export const DingtalkConfigSchema = DingtalkConfigBaseSchema.superRefine((value, ctx) => {
107
111
  const defaultAccount = value.defaultAccount?.trim();
108
112
  if (defaultAccount && value.accounts && Object.keys(value.accounts).length > 0) {
109
113
  const normalizedDefaultAccount = normalizeAccountId(defaultAccount);
@@ -12,6 +12,7 @@
12
12
  * - 详细的消息接收日志(三阶段:接收、解析、处理)
13
13
  * - 连接统计和监控(每分钟输出)
14
14
  */
15
+ import * as fs from 'fs';
15
16
  import type { ClawdbotConfig, RuntimeEnv } from "openclaw/plugin-sdk";
16
17
  import type { ResolvedDingtalkAccount } from "../types/index.ts";
17
18
  import {
@@ -101,6 +102,30 @@ export async function monitorSingleAccount(
101
102
  );
102
103
  }
103
104
 
105
+ // ============ 修复 macOS LaunchAgent 环境下的文件描述符问题 ============
106
+ //
107
+ // 在 macOS LaunchAgent/daemon 环境下,进程启动时 stdin/stdout/stderr(fd 0/1/2)
108
+ // 可能无效(EBADF),导致 Node.js 的 net.Socket 在创建 TCP 连接时出现 EBADF 错误。
109
+ // 通过打开 /dev/null 来确保 fd 0/1/2 有效,避免 socket 创建时使用无效的 fd。
110
+ //
111
+ // 参考:OpenClaw issue #8021 (spawn EBADF on macOS with Node.js 22+)
112
+ if (process.platform === 'darwin') {
113
+ for (const stdioFd of [0, 1, 2]) {
114
+ try {
115
+ fs.fstatSync(stdioFd);
116
+ } catch (fdError: any) {
117
+ if (fdError.code === 'EBADF') {
118
+ logger.warn(`[LaunchAgent] 检测到 fd ${stdioFd} 无效(EBADF),重定向到 /dev/null 以防止 TCP socket 创建失败`);
119
+ try {
120
+ fs.openSync('/dev/null', stdioFd === 0 ? 'r' : 'w');
121
+ } catch (openError: any) {
122
+ logger.warn(`[LaunchAgent] 无法修复 fd ${stdioFd}: ${openError.message}`);
123
+ }
124
+ }
125
+ }
126
+ }
127
+ }
128
+
104
129
  logger.info(`Starting DingTalk Stream client...`);
105
130
  logger.info(`Initializing with clientId: ${clientIdStr.substring(0, 8)}...`);
106
131
  logger.info(`WebSocket keepAlive: false (using application-layer heartbeat)`);