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

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 (65) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/openclaw.plugin.json +2 -7
  3. package/package.json +2 -2
  4. package/src/channel.ts +25 -50
  5. package/src/config/schema.ts +8 -4
  6. package/src/core/connection.ts +26 -1
  7. package/src/core/message-handler.ts +465 -73
  8. package/src/gateway-methods.ts +28 -68
  9. package/src/reply-dispatcher.ts +11 -26
  10. package/src/sdk/types.ts +0 -2
  11. package/src/services/media/chunk-upload.ts +3 -2
  12. package/src/services/media/common.ts +2 -1
  13. package/src/services/media/image.ts +33 -27
  14. package/src/services/media.ts +3 -2
  15. package/src/services/messaging.ts +22 -2
  16. package/skills/dws/SKILL.md +0 -124
  17. package/skills/dws/references/error-codes.md +0 -93
  18. package/skills/dws/references/field-rules.md +0 -105
  19. package/skills/dws/references/global-reference.md +0 -36
  20. package/skills/dws/references/intent-guide.md +0 -179
  21. package/skills/dws/references/products/aiapp.md +0 -412
  22. package/skills/dws/references/products/aitable-record-ops.md +0 -135
  23. package/skills/dws/references/products/aitable.md +0 -511
  24. package/skills/dws/references/products/attendance.md +0 -93
  25. package/skills/dws/references/products/calendar.md +0 -217
  26. package/skills/dws/references/products/chat.md +0 -297
  27. package/skills/dws/references/products/contact.md +0 -108
  28. package/skills/dws/references/products/ding.md +0 -57
  29. package/skills/dws/references/products/doc.md +0 -371
  30. package/skills/dws/references/products/drive.md +0 -140
  31. package/skills/dws/references/products/mail.md +0 -109
  32. package/skills/dws/references/products/minutes.md +0 -204
  33. package/skills/dws/references/products/oa.md +0 -180
  34. package/skills/dws/references/products/report.md +0 -164
  35. package/skills/dws/references/products/simple.md +0 -110
  36. package/skills/dws/references/products/todo.md +0 -146
  37. package/skills/dws/references/products/workbench.md +0 -39
  38. package/skills/dws/references/url-patterns.md +0 -12
  39. package/skills/dws/scripts/aiapp_create_and_poll.py +0 -138
  40. package/skills/dws/scripts/attendance_my_record.py +0 -91
  41. package/skills/dws/scripts/attendance_team_shift.py +0 -89
  42. package/skills/dws/scripts/bot_broadcast.py +0 -112
  43. package/skills/dws/scripts/bulk_add_fields.py +0 -250
  44. package/skills/dws/scripts/calendar_free_slot_finder.py +0 -195
  45. package/skills/dws/scripts/calendar_schedule_meeting.py +0 -159
  46. package/skills/dws/scripts/calendar_today_agenda.py +0 -132
  47. package/skills/dws/scripts/chat_export_messages.py +0 -136
  48. package/skills/dws/scripts/chat_history_with_user.py +0 -142
  49. package/skills/dws/scripts/contact_dept_members.py +0 -110
  50. package/skills/dws/scripts/doc_create_and_write.py +0 -112
  51. package/skills/dws/scripts/drive_tree_list.py +0 -124
  52. package/skills/dws/scripts/finance_daily_cashflow.py +0 -70
  53. package/skills/dws/scripts/finance_expense_flow.py +0 -128
  54. package/skills/dws/scripts/import_records.py +0 -330
  55. package/skills/dws/scripts/mail_send_with_cc.py +0 -122
  56. package/skills/dws/scripts/mail_unread_summary.py +0 -114
  57. package/skills/dws/scripts/minutes_extract_todos.py +0 -110
  58. package/skills/dws/scripts/minutes_recent_summary.py +0 -114
  59. package/skills/dws/scripts/oa_batch_approve.py +0 -136
  60. package/skills/dws/scripts/oa_pending_review.py +0 -118
  61. package/skills/dws/scripts/report_inbox_today.py +0 -114
  62. package/skills/dws/scripts/todo_batch_create.py +0 -159
  63. package/skills/dws/scripts/todo_daily_summary.py +0 -169
  64. package/skills/dws/scripts/todo_overdue_check.py +0 -122
  65. package/skills/dws/scripts/upload_attachment.py +0 -190
package/CHANGELOG.md CHANGED
@@ -5,6 +5,57 @@ 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.10] - 2026-03-31
9
+
10
+ ### 修复 / Fixes
11
+ - 🐛 **Gateway Methods 配置访问失败** ([#397](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/issues/397)) - 修复 SDK 升级后 `context.deps.config` 为 `undefined`,所有 Gateway RPC 方法调用失败。改用 SDK 的 `loadConfig()` 函数获取配置
12
+ **Gateway Methods config access failure** - Fixed `context.deps.config` being `undefined` after SDK upgrade, now uses `loadConfig()` from `openclaw/plugin-sdk/config-runtime`
13
+
14
+ - 🐛 **锁定 axios 版本避免兼容性问题** ([#396](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/issues/396)) - 将 `axios` 从 `^1.6.0` 锁定为 `1.6.0`,避免自动升级引入不兼容变更
15
+ **Pin axios version** - Pinned `axios` from `^1.6.0` to `1.6.0` to prevent incompatible upgrades
16
+
17
+ ### 改进 / Improvements
18
+ - ✅ **connection.ts 动态 import 优化** - 将 `createLoggerFromConfig` 改为动态 import,避免潜在循环依赖
19
+ **connection.ts dynamic import** - Changed `createLoggerFromConfig` to dynamic import to avoid potential circular dependencies
20
+
21
+ ## [0.8.9] - 2026-03-31
22
+
23
+ ### 新增 / Added
24
+ - ✨ **引用消息完整解析** - 新增 `extractQuotedMsgText` 递归解析引用消息(最多 3 层嵌套),支持 text、richText、picture、video、audio、file、markdown、interactiveCard 等消息类型,自动提取引用中的媒体附件和链接
25
+ **Quoted message full parsing** - Added recursive quoted message parsing (up to 3 levels) with media attachment and URL extraction
26
+
27
+ - ✨ **新增配置项 asyncMode / ackText / endpoint / debug** - `configSchema` 新增四个配置字段
28
+ **New config options** - Added `asyncMode`, `ackText`, `endpoint`, `debug` to configSchema
29
+
30
+ - ✨ **普通消息本地图片后处理** - `sendNormalToUser` 和 `sendNormalToGroup` 新增本地图片上传后处理,发送普通消息时自动替换本地图片路径为 media_id
31
+ **Local image post-processing for normal messages** - Added automatic local image upload and replacement in `sendNormalToUser` and `sendNormalToGroup`
32
+
33
+ ### 修复 / Fixes
34
+ - 🐛 **macOS LaunchAgent 环境 WebSocket 连接失败** - 修复 macOS LaunchAgent/daemon 环境下 fd 0/1/2 无效(EBADF)导致 TCP 连接创建失败的问题
35
+ **WebSocket connection failure on macOS LaunchAgent** - Fixed EBADF errors on macOS LaunchAgent environments by redirecting invalid file descriptors to `/dev/null`
36
+
37
+ - 🐛 **AI Card 流式关闭竞争条件** - 修复 `closeStreaming` 被 `onIdle` 和 `onError` 同时触发时的竞争条件,采用 snapshot 模式防止并发崩溃
38
+ **AI Card streaming close race condition** - Fixed race condition in `closeStreaming` using snapshot pattern to prevent concurrent crashes
39
+
40
+ - 🐛 **FormData CJS 互操作问题** - 将 `form-data` 从动态 import 改为静态 import,修复 jiti/ESM 环境下 `.default` 偶发为 undefined 的问题
41
+ **FormData CJS interop issue** - Changed `form-data` from dynamic to static import, fixing intermittent `.default` undefined errors in jiti/ESM
42
+
43
+ - 🐛 **纯文本图片路径误转换** - 禁用纯文本中本地图片路径自动转换为图片语法的行为,避免影响用户展示路径文本的场景
44
+ **Bare image path false conversion** - Disabled automatic conversion of bare local image paths to image syntax
45
+
46
+ ### 改进 / Improvements
47
+ - ✅ **Zod Schema 拆分兼容 Web UI** - 将 `DingtalkConfigSchema` 拆分为 `DingtalkConfigBaseSchema` 和带 `superRefine` 的完整 Schema,解决 JSON Schema 生成兼容性问题
48
+ **Zod Schema split for Web UI compatibility** - Split schema to fix `buildChannelConfigSchema` JSON Schema generation
49
+
50
+ - ✅ **configSchema 类型简化** - 将 `clientId`、`clientSecret` 等字段从 `oneOf` 联合类型简化为单一 `string` 类型
51
+ **configSchema type simplification** - Simplified JSON Schema from `oneOf` union types to single `string` type
52
+
53
+ - ✅ **reply-dispatcher logger 统一** - 替换手动构建的 log 对象为 `createLoggerFromConfig`
54
+ **reply-dispatcher logger unification** - Replaced manual log object with `createLoggerFromConfig`
55
+
56
+ - ✅ **锁定 axios 版本到 1.6.0** - 避免自动升级引入不兼容变更
57
+ **Pin axios to 1.6.0** - Prevent automatic upgrades from introducing incompatible changes
58
+
8
59
  ## [0.8.8] - 2026-03-29
9
60
 
10
61
  ### 修复 / 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.10",
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.10",
4
4
  "description": "DingTalk (钉钉) channel connector — Stream mode with AI Card streaming",
5
5
  "main": "index.ts",
6
6
  "type": "module",
@@ -44,7 +44,7 @@
44
44
  "access": "public"
45
45
  },
46
46
  "dependencies": {
47
- "axios": "^1.6.0",
47
+ "axios": "1.6.0",
48
48
  "dingtalk-stream": "2.1.4",
49
49
  "fluent-ffmpeg": "^2.1.3",
50
50
  "form-data": "^4.0.0",
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,12 +12,12 @@
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 {
18
19
  checkAndMarkDingtalkMessage,
19
20
  } from "../utils/utils-legacy.ts";
20
- import { createLoggerFromConfig } from "../utils/logger.ts";
21
21
 
22
22
  // ============ 类型定义 ============
23
23
 
@@ -77,6 +77,7 @@ export async function monitorSingleAccount(
77
77
  const log = runtime?.log;
78
78
 
79
79
  // 创建 debug logger(仅在 debug 模式下输出 info/debug 日志)
80
+ const { createLoggerFromConfig } = await import('../utils/logger');
80
81
  const logger = createLoggerFromConfig(account.config, `DingTalk:${accountId}`);
81
82
 
82
83
  // 验证凭据是否存在
@@ -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)`);