@soimy/dingtalk 3.6.8 → 3.6.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 (178) hide show
  1. package/dist/index.d.ts +3 -12
  2. package/dist/index.js +1030 -318
  3. package/dist/src/access-control.d.ts +0 -1
  4. package/dist/src/ack-reaction/dynamic-ack-reaction-controller.d.ts +0 -1
  5. package/dist/src/ack-reaction/dynamic-ack-reaction-events.d.ts +0 -1
  6. package/dist/src/ack-reaction/dynamic-ack-reaction-progress.d.ts +0 -1
  7. package/dist/src/ack-reaction-classifier.d.ts +0 -1
  8. package/dist/src/ack-reaction-service.d.ts +0 -1
  9. package/dist/src/auth.d.ts +0 -1
  10. package/dist/src/card/ask-user-question-context.d.ts +0 -1
  11. package/dist/src/card/ask-user-question-store.d.ts +0 -1
  12. package/dist/src/card/ask-user-question.d.ts +0 -1
  13. package/dist/src/card/card-action-handler.d.ts +0 -1
  14. package/dist/src/card/card-markdown-image-reroute.d.ts +0 -1
  15. package/dist/src/card/card-run-registry.d.ts +0 -1
  16. package/dist/src/card/card-stop-handler.d.ts +0 -1
  17. package/dist/src/card/card-streaming-mode.d.ts +0 -1
  18. package/dist/src/card/card-template.d.ts +0 -1
  19. package/dist/src/card/reasoning-answer-split.d.ts +0 -1
  20. package/dist/src/card/reasoning-block-assembler.d.ts +0 -1
  21. package/dist/src/card/statusline-renderer.d.ts +0 -1
  22. package/dist/src/card/task-model-metadata.d.ts +0 -1
  23. package/dist/src/card-callback-service.d.ts +0 -1
  24. package/dist/src/card-draft-controller.d.ts +0 -1
  25. package/dist/src/card-service.d.ts +0 -1
  26. package/dist/src/channel.d.ts +0 -1
  27. package/dist/src/command/card-stop-command.d.ts +0 -1
  28. package/dist/src/command/inbound-command-dispatch-service.d.ts +0 -1
  29. package/dist/src/config-schema.d.ts +0 -1
  30. package/dist/src/config.d.ts +0 -1
  31. package/dist/src/connection-manager.d.ts +0 -1
  32. package/dist/src/dedup.d.ts +0 -1
  33. package/dist/src/device-registration.d.ts +0 -1
  34. package/dist/src/docs-service.d.ts +0 -1
  35. package/dist/src/draft-stream-loop.d.ts +0 -1
  36. package/dist/src/feedback-learning-service.d.ts +0 -1
  37. package/dist/src/feedback-learning-store.d.ts +0 -1
  38. package/dist/src/gateway/channel-gateway.d.ts +0 -1
  39. package/dist/src/gateway/inbound-session-queue-dispatcher.d.ts +0 -1
  40. package/dist/src/gateway/inbound-session-queue.d.ts +0 -1
  41. package/dist/src/gateway/reply-session-conflict.d.ts +0 -1
  42. package/dist/src/http-client.d.ts +0 -1
  43. package/dist/src/inbound-handler.d.ts +0 -1
  44. package/dist/src/learning-command-service.d.ts +0 -1
  45. package/dist/src/logger-context.d.ts +0 -1
  46. package/dist/src/media-utils.d.ts +0 -1
  47. package/dist/src/message-context-store.d.ts +0 -1
  48. package/dist/src/message-utils.d.ts +0 -1
  49. package/dist/src/messaging/attachment-text-extractor.d.ts +0 -1
  50. package/dist/src/messaging/btw-deliver.d.ts +0 -1
  51. package/dist/src/messaging/channel-actions.d.ts +0 -1
  52. package/dist/src/messaging/channel-outbound.d.ts +0 -1
  53. package/dist/src/messaging/quoted-context.d.ts +0 -1
  54. package/dist/src/messaging/quoted-file-service.d.ts +0 -1
  55. package/dist/src/messaging/quoted-ref.d.ts +0 -1
  56. package/dist/src/onboarding.d.ts +0 -1
  57. package/dist/src/path-utils.d.ts +0 -1
  58. package/dist/src/peer-id-registry.d.ts +0 -12
  59. package/dist/src/persistence-store.d.ts +0 -1
  60. package/dist/src/platform/channel-status.d.ts +0 -1
  61. package/dist/src/plugin-sdk-channel-actions-augment.d.ts +0 -1
  62. package/dist/src/proactive-risk-registry.d.ts +0 -1
  63. package/dist/src/reply-strategy-card.d.ts +0 -1
  64. package/dist/src/reply-strategy-markdown.d.ts +0 -1
  65. package/dist/src/reply-strategy-types.d.ts +0 -1
  66. package/dist/src/reply-strategy-with-reaction.d.ts +0 -1
  67. package/dist/src/reply-strategy.d.ts +0 -1
  68. package/dist/src/run-usage-store.d.ts +0 -1
  69. package/dist/src/runtime.d.ts +0 -1
  70. package/dist/src/secret-input.d.ts +3 -3
  71. package/dist/src/send-service.d.ts +0 -1
  72. package/dist/src/session-command-service.d.ts +0 -1
  73. package/dist/src/session-lock.d.ts +0 -1
  74. package/dist/src/session-peer-store.d.ts +0 -1
  75. package/dist/src/session-routing.d.ts +0 -1
  76. package/dist/src/session-state.d.ts +0 -1
  77. package/dist/src/signature.d.ts +0 -1
  78. package/dist/src/targeting/agent-name-matcher.d.ts +0 -1
  79. package/dist/src/targeting/agent-routing.d.ts +0 -1
  80. package/dist/src/targeting/group-members-store.d.ts +0 -1
  81. package/dist/src/targeting/target-directory-adapter.d.ts +0 -1
  82. package/dist/src/targeting/target-directory-store.d.ts +0 -1
  83. package/dist/src/targeting/target-input.d.ts +0 -1
  84. package/dist/src/types.d.ts +0 -1
  85. package/dist/src/utils.d.ts +0 -1
  86. package/index.ts +8 -2
  87. package/openclaw.plugin.json +4 -3
  88. package/package.json +10 -8
  89. package/src/channel.ts +4 -3
  90. package/src/gateway/channel-gateway.ts +67 -4
  91. package/src/messaging/channel-actions.ts +1 -4
  92. package/src/peer-id-registry.ts +0 -100
  93. package/src/secret-input.ts +33 -33
  94. package/dist/index.d.ts.map +0 -1
  95. package/dist/index.js.map +0 -7
  96. package/dist/src/access-control.d.ts.map +0 -1
  97. package/dist/src/ack-reaction/dynamic-ack-reaction-controller.d.ts.map +0 -1
  98. package/dist/src/ack-reaction/dynamic-ack-reaction-events.d.ts.map +0 -1
  99. package/dist/src/ack-reaction/dynamic-ack-reaction-progress.d.ts.map +0 -1
  100. package/dist/src/ack-reaction-classifier.d.ts.map +0 -1
  101. package/dist/src/ack-reaction-service.d.ts.map +0 -1
  102. package/dist/src/auth.d.ts.map +0 -1
  103. package/dist/src/card/ask-user-question-context.d.ts.map +0 -1
  104. package/dist/src/card/ask-user-question-store.d.ts.map +0 -1
  105. package/dist/src/card/ask-user-question.d.ts.map +0 -1
  106. package/dist/src/card/card-action-handler.d.ts.map +0 -1
  107. package/dist/src/card/card-markdown-image-reroute.d.ts.map +0 -1
  108. package/dist/src/card/card-run-registry.d.ts.map +0 -1
  109. package/dist/src/card/card-stop-handler.d.ts.map +0 -1
  110. package/dist/src/card/card-streaming-mode.d.ts.map +0 -1
  111. package/dist/src/card/card-template.d.ts.map +0 -1
  112. package/dist/src/card/reasoning-answer-split.d.ts.map +0 -1
  113. package/dist/src/card/reasoning-block-assembler.d.ts.map +0 -1
  114. package/dist/src/card/statusline-renderer.d.ts.map +0 -1
  115. package/dist/src/card/task-model-metadata.d.ts.map +0 -1
  116. package/dist/src/card-callback-service.d.ts.map +0 -1
  117. package/dist/src/card-draft-controller.d.ts.map +0 -1
  118. package/dist/src/card-service.d.ts.map +0 -1
  119. package/dist/src/channel.d.ts.map +0 -1
  120. package/dist/src/command/card-stop-command.d.ts.map +0 -1
  121. package/dist/src/command/inbound-command-dispatch-service.d.ts.map +0 -1
  122. package/dist/src/config-schema.d.ts.map +0 -1
  123. package/dist/src/config.d.ts.map +0 -1
  124. package/dist/src/connection-manager.d.ts.map +0 -1
  125. package/dist/src/dedup.d.ts.map +0 -1
  126. package/dist/src/device-registration.d.ts.map +0 -1
  127. package/dist/src/docs-service.d.ts.map +0 -1
  128. package/dist/src/draft-stream-loop.d.ts.map +0 -1
  129. package/dist/src/feedback-learning-service.d.ts.map +0 -1
  130. package/dist/src/feedback-learning-store.d.ts.map +0 -1
  131. package/dist/src/gateway/channel-gateway.d.ts.map +0 -1
  132. package/dist/src/gateway/inbound-session-queue-dispatcher.d.ts.map +0 -1
  133. package/dist/src/gateway/inbound-session-queue.d.ts.map +0 -1
  134. package/dist/src/gateway/reply-session-conflict.d.ts.map +0 -1
  135. package/dist/src/http-client.d.ts.map +0 -1
  136. package/dist/src/inbound-handler.d.ts.map +0 -1
  137. package/dist/src/learning-command-service.d.ts.map +0 -1
  138. package/dist/src/logger-context.d.ts.map +0 -1
  139. package/dist/src/media-utils.d.ts.map +0 -1
  140. package/dist/src/message-context-store.d.ts.map +0 -1
  141. package/dist/src/message-utils.d.ts.map +0 -1
  142. package/dist/src/messaging/attachment-text-extractor.d.ts.map +0 -1
  143. package/dist/src/messaging/btw-deliver.d.ts.map +0 -1
  144. package/dist/src/messaging/channel-actions.d.ts.map +0 -1
  145. package/dist/src/messaging/channel-outbound.d.ts.map +0 -1
  146. package/dist/src/messaging/quoted-context.d.ts.map +0 -1
  147. package/dist/src/messaging/quoted-file-service.d.ts.map +0 -1
  148. package/dist/src/messaging/quoted-ref.d.ts.map +0 -1
  149. package/dist/src/onboarding.d.ts.map +0 -1
  150. package/dist/src/path-utils.d.ts.map +0 -1
  151. package/dist/src/peer-id-registry.d.ts.map +0 -1
  152. package/dist/src/persistence-store.d.ts.map +0 -1
  153. package/dist/src/platform/channel-status.d.ts.map +0 -1
  154. package/dist/src/plugin-sdk-channel-actions-augment.d.ts.map +0 -1
  155. package/dist/src/proactive-risk-registry.d.ts.map +0 -1
  156. package/dist/src/reply-strategy-card.d.ts.map +0 -1
  157. package/dist/src/reply-strategy-markdown.d.ts.map +0 -1
  158. package/dist/src/reply-strategy-types.d.ts.map +0 -1
  159. package/dist/src/reply-strategy-with-reaction.d.ts.map +0 -1
  160. package/dist/src/reply-strategy.d.ts.map +0 -1
  161. package/dist/src/run-usage-store.d.ts.map +0 -1
  162. package/dist/src/runtime.d.ts.map +0 -1
  163. package/dist/src/secret-input.d.ts.map +0 -1
  164. package/dist/src/send-service.d.ts.map +0 -1
  165. package/dist/src/session-command-service.d.ts.map +0 -1
  166. package/dist/src/session-lock.d.ts.map +0 -1
  167. package/dist/src/session-peer-store.d.ts.map +0 -1
  168. package/dist/src/session-routing.d.ts.map +0 -1
  169. package/dist/src/session-state.d.ts.map +0 -1
  170. package/dist/src/signature.d.ts.map +0 -1
  171. package/dist/src/targeting/agent-name-matcher.d.ts.map +0 -1
  172. package/dist/src/targeting/agent-routing.d.ts.map +0 -1
  173. package/dist/src/targeting/group-members-store.d.ts.map +0 -1
  174. package/dist/src/targeting/target-directory-adapter.d.ts.map +0 -1
  175. package/dist/src/targeting/target-directory-store.d.ts.map +0 -1
  176. package/dist/src/targeting/target-input.d.ts.map +0 -1
  177. package/dist/src/types.d.ts.map +0 -1
  178. package/dist/src/utils.d.ts.map +0 -1
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "id": "dingtalk",
3
+ "name": "DingTalk",
3
4
  "channels": ["dingtalk"],
4
5
  "contracts": {
5
6
  "tools": ["dingtalk_ask_user_question"]
@@ -62,7 +63,7 @@
62
63
  }
63
64
  }
64
65
  ],
65
- "description": "DingTalk App Secret (Client Secret) used to obtain DingTalk access tokens. Supports SecretInput references such as env/file. Security boundary: file reads the configured local path; use file only with trusted plugin configuration."
66
+ "description": "DingTalk App Secret (Client Secret) used to obtain DingTalk access tokens. Supports env/file SecretInput references resolved by configured OpenClaw secret providers."
66
67
  },
67
68
  "dmPolicy": {
68
69
  "type": "string",
@@ -368,7 +369,7 @@
368
369
  }
369
370
  }
370
371
  ],
371
- "description": "DingTalk App Secret (Client Secret) used to obtain DingTalk access tokens. Supports SecretInput references such as env/file. Security boundary: file reads the configured local path; use file only with trusted plugin configuration."
372
+ "description": "DingTalk App Secret (Client Secret) used to obtain DingTalk access tokens. Supports env/file SecretInput references resolved by configured OpenClaw secret providers."
372
373
  },
373
374
  "dmPolicy": {
374
375
  "type": "string",
@@ -654,7 +655,7 @@
654
655
  },
655
656
  "clientSecret": {
656
657
  "label": "DingTalk Client Secret",
657
- "help": "App Secret used to obtain DingTalk access tokens. SecretInput file references are for trusted plugin configuration because file reads a local path.",
658
+ "help": "App Secret used to obtain DingTalk access tokens. SecretInput references are resolved by configured OpenClaw secret providers.",
658
659
  "sensitive": true
659
660
  },
660
661
  "dmPolicy": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soimy/dingtalk",
3
- "version": "3.6.8",
3
+ "version": "3.6.10",
4
4
  "description": "DingTalk (钉钉) channel plugin for OpenClaw",
5
5
  "keywords": [
6
6
  "bot",
@@ -20,9 +20,7 @@
20
20
  },
21
21
  "files": [
22
22
  "dist/**/*.js",
23
- "dist/**/*.js.map",
24
23
  "dist/**/*.d.ts",
25
- "dist/**/*.d.ts.map",
26
24
  "index.ts",
27
25
  "src/**/*.ts",
28
26
  "docs/assets/dingtalk-ask-user-card-template.json",
@@ -31,6 +29,9 @@
31
29
  "LICENSE"
32
30
  ],
33
31
  "type": "module",
32
+ "engines": {
33
+ "node": ">=22.22.3 <23 || >=24.15.0 <25 || >=25.9.0"
34
+ },
34
35
  "packageManager": "pnpm@10.33.0",
35
36
  "main": "./dist/index.js",
36
37
  "types": "./dist/index.d.ts",
@@ -69,12 +70,13 @@
69
70
  "form-data": "^4.0.0",
70
71
  "mammoth": "^1.12.0",
71
72
  "pdf-parse": "^2.4.5",
72
- "zod": "^4.3.6"
73
+ "zod": "^4.4.3"
73
74
  },
74
75
  "devDependencies": {
75
76
  "@types/node": "^25.2.0",
76
77
  "@vitest/coverage-v8": "^3.2.4",
77
78
  "esbuild": "^0.27.3",
79
+ "openclaw": "2026.7.1-2",
78
80
  "oxfmt": "0.34.0",
79
81
  "oxlint": "1.49.0",
80
82
  "oxlint-tsgolint": "0.18.1",
@@ -83,7 +85,7 @@
83
85
  "vitest": "^3.2.4"
84
86
  },
85
87
  "peerDependencies": {
86
- "openclaw": ">=2026.3.28"
88
+ "openclaw": ">=2026.7.1-2"
87
89
  },
88
90
  "peerDependenciesMeta": {
89
91
  "openclaw": {
@@ -92,10 +94,10 @@
92
94
  },
93
95
  "openclaw": {
94
96
  "compat": {
95
- "pluginApi": ">=2026.3.28"
97
+ "pluginApi": ">=2026.7.1-2"
96
98
  },
97
99
  "build": {
98
- "openclawVersion": "2026.3.28"
100
+ "openclawVersion": "2026.7.1-2"
99
101
  },
100
102
  "extensions": [
101
103
  "./index.ts"
@@ -121,7 +123,7 @@
121
123
  ]
122
124
  },
123
125
  "install": {
124
- "minHostVersion": ">=2026.3.28",
126
+ "minHostVersion": ">=2026.7.1-2",
125
127
  "npmSpec": "@soimy/dingtalk",
126
128
  "localPath": ".",
127
129
  "defaultChoice": "npm"
package/src/channel.ts CHANGED
@@ -1,6 +1,7 @@
1
- import { buildChannelConfigSchema, type OpenClawConfig } from "openclaw/plugin-sdk/core";
1
+ import { buildJsonChannelConfigSchema } from "openclaw/plugin-sdk/channel-config-schema";
2
+ import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
3
+ import pluginManifest from "../openclaw.plugin.json";
2
4
  import { getConfig, isConfigured, mergeAccountWithDefaults, resolveGroupConfig } from "./config";
3
- import { DingTalkConfigSchema } from "./config-schema.js";
4
5
  import {
5
6
  CHANNEL_INFLIGHT_NAMESPACE_POLICY,
6
7
  createDingTalkGateway,
@@ -29,7 +30,7 @@ export const dingtalkPlugin: DingTalkChannelPlugin = {
29
30
  blurb: "钉钉企业内部机器人,使用 Stream 模式,无需公网 IP。",
30
31
  aliases: ["dd", "ding"],
31
32
  },
32
- configSchema: buildChannelConfigSchema(DingTalkConfigSchema),
33
+ configSchema: buildJsonChannelConfigSchema(pluginManifest.channelConfigs.dingtalk.schema),
33
34
  setup: dingtalkSetupAdapter,
34
35
  setupWizard: dingtalkSetupWizard,
35
36
  capabilities: {
@@ -1,4 +1,5 @@
1
1
  import { DWClient, TOPIC_CARD, TOPIC_ROBOT } from "dingtalk-stream";
2
+ import { listAgentIds } from "openclaw/plugin-sdk/agent-runtime";
2
3
  import { analyzeCardCallback } from "../card-callback-service";
3
4
  import { finalizeActiveCardsForAccount, recoverPendingCardsForAccount } from "../card-service";
4
5
  import { recoverAskUserQuestionsForAccount } from "../card/ask-user-question";
@@ -13,9 +14,14 @@ import {
13
14
  } from "../feedback-learning-service";
14
15
  import { handleDingTalkMessage } from "../inbound-handler";
15
16
  import { setCurrentLogger } from "../logger-context";
16
- import { preloadPeerIdsFromSessions } from "../peer-id-registry";
17
+ import { registerPeerId } from "../peer-id-registry";
17
18
  import { getDingTalkRuntime } from "../runtime";
18
19
  import { sendProactiveTextOrMarkdown } from "../send-service";
20
+ import {
21
+ listKnownGroupTargets,
22
+ listKnownUserTargets,
23
+ upsertObservedGroupTarget,
24
+ } from "../targeting/target-directory-store";
19
25
  import type {
20
26
  ConnectionManagerConfig,
21
27
  DingTalkChannelPlugin,
@@ -180,6 +186,66 @@ export function createDingTalkGateway(): NonNullable<DingTalkChannelPlugin["gate
180
186
  debug: config.debug,
181
187
  baseLog: ctx.log,
182
188
  });
189
+ try {
190
+ const knownGroupIds = new Set<string>();
191
+ for (const group of listKnownGroupTargets({
192
+ storePath: accountStorePath,
193
+ accountId: account.accountId,
194
+ })) {
195
+ registerPeerId(group.conversationId);
196
+ knownGroupIds.add(group.conversationId.toLowerCase());
197
+ }
198
+ const runtime = getDingTalkRuntime();
199
+ for (const agentId of listAgentIds(cfg)) {
200
+ const sessionStorePath = runtime.channel.session.resolveStorePath(cfg.session?.store, {
201
+ agentId,
202
+ });
203
+ for (const { entry } of runtime.agent.session.listSessionEntries({
204
+ agentId,
205
+ storePath: sessionStorePath,
206
+ readConsistency: "latest",
207
+ })) {
208
+ const isDingTalkSession = [
209
+ entry.lastChannel,
210
+ entry.channel,
211
+ entry.origin?.provider,
212
+ entry.origin?.surface,
213
+ ].some((value) => value === "dingtalk");
214
+ const sessionAccountId = entry.lastAccountId || entry.origin?.accountId;
215
+ if (!isDingTalkSession || sessionAccountId !== account.accountId) {
216
+ continue;
217
+ }
218
+ for (const peerId of [entry.lastTo, entry.origin?.from, entry.origin?.to]) {
219
+ if (typeof peerId !== "string" || !peerId.startsWith("cid")) {
220
+ continue;
221
+ }
222
+ registerPeerId(peerId);
223
+ const normalizedPeerId = peerId.toLowerCase();
224
+ if (!knownGroupIds.has(normalizedPeerId)) {
225
+ upsertObservedGroupTarget({
226
+ storePath: accountStorePath,
227
+ accountId: account.accountId,
228
+ conversationId: peerId,
229
+ seenAt: entry.updatedAt,
230
+ });
231
+ knownGroupIds.add(normalizedPeerId);
232
+ }
233
+ }
234
+ }
235
+ }
236
+ for (const user of listKnownUserTargets({
237
+ storePath: accountStorePath,
238
+ accountId: account.accountId,
239
+ })) {
240
+ registerPeerId(user.canonicalUserId);
241
+ registerPeerId(user.staffId || "");
242
+ registerPeerId(user.senderId);
243
+ }
244
+ } catch (err: unknown) {
245
+ pluginLog?.warn?.(
246
+ `[${account.accountId}] Failed to restore peer IDs: ${err instanceof Error ? err.message : String(err)}`,
247
+ );
248
+ }
183
249
  // Stream credentials are resolved once per account start. If a file
184
250
  // SecretInput rotates, restart the gateway/account so reconnects use the
185
251
  // new secret.
@@ -188,9 +254,6 @@ export function createDingTalkGateway(): NonNullable<DingTalkChannelPlugin["gate
188
254
 
189
255
  pluginLog?.info?.(`[${account.accountId}] Initializing DingTalk Stream client...`);
190
256
 
191
- preloadPeerIdsFromSessions();
192
- pluginLog?.debug?.(`[${account.accountId}] Peer ID registry preloaded from sessions`);
193
-
194
257
  cleanupOrphanedTempFiles(pluginLog);
195
258
  try {
196
259
  const recovered = await recoverPendingCardsForAccount(
@@ -55,12 +55,9 @@ function describeDingTalkMessageTool(cfg: OpenClawConfig) {
55
55
  if (!configured && !(config.accounts && Object.keys(config.accounts).length > 0)) {
56
56
  return { actions: [], capabilities: [], schema: null };
57
57
  }
58
- const hasCardMode =
59
- config.messageType === "card" ||
60
- (config.accounts && Object.values(config.accounts).some((account) => account?.messageType === "card"));
61
58
  return {
62
59
  actions: ["send"] as const,
63
- capabilities: hasCardMode ? (["cards"] as const) : [],
60
+ capabilities: [],
64
61
  schema: null,
65
62
  };
66
63
  }
@@ -8,13 +8,7 @@
8
8
  * can be delivered correctly.
9
9
  */
10
10
 
11
- import { readFileSync, readdirSync } from "fs";
12
- import * as os from "os";
13
- import { join } from "path";
14
- import { getLogger } from "./logger-context";
15
-
16
11
  const peerIdMap = new Map<string, string>();
17
- let preloaded = false;
18
12
 
19
13
  /**
20
14
  * Register an original peer ID, keyed by its lowercased form.
@@ -26,20 +20,9 @@ export function registerPeerId(originalId: string): void {
26
20
  peerIdMap.set(originalId.toLowerCase(), originalId);
27
21
  }
28
22
 
29
- function maybeRegisterDingTalkGroupPeerId(value: unknown): void {
30
- // DingTalk group openConversationId values are base64-like and consistently start with "cid".
31
- // User IDs in DM contexts do not follow this pattern and do not require case restoration.
32
- if (typeof value === "string" && value.startsWith("cid")) {
33
- registerPeerId(value);
34
- }
35
- }
36
-
37
23
  /**
38
24
  * Resolve a possibly-lowercased peer ID back to its original casing.
39
25
  *
40
- * If registry is empty at first outbound call (for example, cron/delivery queue
41
- * fires before inbound callbacks), perform a one-time lazy preload from sessions.
42
- *
43
26
  * Returns the original if found, otherwise returns the input as-is.
44
27
  */
45
28
  export function resolveOriginalPeerId(id: string): string {
@@ -47,10 +30,6 @@ export function resolveOriginalPeerId(id: string): string {
47
30
  return id;
48
31
  }
49
32
 
50
- if (!preloaded) {
51
- preloadPeerIdsFromSessions();
52
- }
53
-
54
33
  return peerIdMap.get(id.toLowerCase()) || id;
55
34
  }
56
35
 
@@ -59,83 +38,4 @@ export function resolveOriginalPeerId(id: string): string {
59
38
  */
60
39
  export function clearPeerIdRegistry(): void {
61
40
  peerIdMap.clear();
62
- preloaded = false;
63
- }
64
-
65
- /**
66
- * Preload known peer IDs from all agents' sessions.json files.
67
- *
68
- * Safe to call repeatedly:
69
- * - without explicit homeDir: runs once, then no-ops;
70
- * - with explicit homeDir: always runs (useful for tests).
71
- */
72
- export function preloadPeerIdsFromSessions(homeDir?: string): void {
73
- if (!homeDir && preloaded) {
74
- return;
75
- }
76
-
77
- const home = homeDir || os.homedir();
78
- const agentsDir = join(home, ".openclaw", "agents");
79
- const log = getLogger();
80
- let preloadCompleted = false;
81
-
82
- try {
83
- const agentDirs = readdirSync(agentsDir, { withFileTypes: true })
84
- .filter((entry) => entry.isDirectory())
85
- .map((entry) => entry.name);
86
-
87
- for (const agentName of agentDirs) {
88
- const sessionsPath = join(agentsDir, agentName, "sessions", "sessions.json");
89
-
90
- try {
91
- const raw = readFileSync(sessionsPath, "utf-8");
92
- const parsed = JSON.parse(raw);
93
- if (!parsed || typeof parsed !== "object") {
94
- continue;
95
- }
96
-
97
- for (const session of Object.values(parsed as Record<string, unknown>)) {
98
- if (!session || typeof session !== "object") {
99
- continue;
100
- }
101
-
102
- const sessionRecord = session as Record<string, unknown>;
103
- maybeRegisterDingTalkGroupPeerId(sessionRecord.lastTo);
104
-
105
- const origin = sessionRecord.origin;
106
- if (!origin || typeof origin !== "object") {
107
- continue;
108
- }
109
-
110
- const originRecord = origin as Record<string, unknown>;
111
- maybeRegisterDingTalkGroupPeerId(originRecord.from);
112
- maybeRegisterDingTalkGroupPeerId(originRecord.to);
113
- }
114
- } catch (err) {
115
- // sessions.json may be missing or malformed for some agents; ignore per file.
116
- log?.debug?.(
117
- `[DingTalk][PeerIdRegistry] Failed to parse preload sessions file ${sessionsPath}: ${
118
- err instanceof Error ? err.message : String(err)
119
- }`,
120
- );
121
- }
122
- }
123
- preloadCompleted = true;
124
- } catch (err) {
125
- const errorCode = (err as NodeJS.ErrnoException | undefined)?.code;
126
- if (errorCode === "ENOENT") {
127
- // agents directory may not exist yet; this is a stable no-op state.
128
- preloadCompleted = true;
129
- } else {
130
- log?.debug?.(
131
- `[DingTalk][PeerIdRegistry] Failed to scan preload directory ${agentsDir}: ${
132
- err instanceof Error ? err.message : String(err)
133
- }`,
134
- );
135
- }
136
- }
137
-
138
- if (!homeDir && preloadCompleted) {
139
- preloaded = true;
140
- }
141
41
  }
@@ -1,6 +1,10 @@
1
- import { readFile } from "node:fs/promises";
1
+ import type { OpenClawConfig } from "openclaw/plugin-sdk/core";
2
+ import {
3
+ hasConfiguredSecretInput as hasConfiguredOpenClawSecretInput,
4
+ resolveConfiguredSecretInputString,
5
+ } from "openclaw/plugin-sdk/secret-input-runtime";
2
6
  import { z } from "zod";
3
- import { resolveRelativePath } from "./path-utils";
7
+ import { getDingTalkRuntime } from "./runtime";
4
8
 
5
9
  export type SecretInputRef = {
6
10
  source: "env" | "file";
@@ -68,19 +72,7 @@ export function isSecretInputRef(value: unknown): value is SecretInputRef {
68
72
  }
69
73
 
70
74
  export function hasConfiguredSecretInput(value: unknown): boolean {
71
- if (typeof value === "string") {
72
- return value.trim().length > 0;
73
- }
74
- if (!isSecretInputRef(value)) {
75
- return false;
76
- }
77
- if (value.source === "env") {
78
- return Boolean(process.env[value.id]?.trim());
79
- }
80
- // File references are considered configured when the reference shape is
81
- // present. The actual filesystem lookup happens at runtime so status checks
82
- // can stay side-effect free.
83
- return true;
75
+ return hasConfiguredOpenClawSecretInput(value);
84
76
  }
85
77
 
86
78
  export function normalizeSecretInputString(value: unknown): string | undefined {
@@ -116,13 +108,15 @@ export function parseSecretInputString(value: unknown): SecretInput | undefined
116
108
  export async function resolveSecretInputString(
117
109
  value: unknown,
118
110
  log?: SecretInputLog,
111
+ hostConfig?: OpenClawConfig,
119
112
  ): Promise<string | undefined> {
120
- return (await resolveSecretInputStringWithFailure(value, log)).value;
113
+ return (await resolveSecretInputStringWithFailure(value, log, hostConfig)).value;
121
114
  }
122
115
 
123
116
  export async function resolveSecretInputStringWithFailure(
124
117
  value: unknown,
125
118
  log?: SecretInputLog,
119
+ hostConfig?: OpenClawConfig,
126
120
  ): Promise<{ value?: string; failure?: SecretInputResolutionFailure }> {
127
121
  if (typeof value === "string") {
128
122
  const trimmed = value.trim();
@@ -131,34 +125,40 @@ export async function resolveSecretInputStringWithFailure(
131
125
  if (!isSecretInputRef(value)) {
132
126
  return {};
133
127
  }
134
- if (value.source === "env") {
135
- const envValue = process.env[value.id]?.trim();
136
- if (envValue) {
137
- return { value: envValue };
128
+ if (!hostConfig) {
129
+ try {
130
+ hostConfig = getDingTalkRuntime().config.current() as OpenClawConfig;
131
+ } catch {
132
+ hostConfig = {} as OpenClawConfig;
138
133
  }
139
- const failure = buildSecretInputFailure(value, "environment variable is not set or is empty");
140
- log?.warn?.("[DingTalk][SecretInput] Failed to resolve env secret", {
134
+ }
135
+ try {
136
+ const resolved = await resolveConfiguredSecretInputString({
137
+ config: hostConfig,
138
+ env: process.env,
139
+ value,
140
+ path: "channels.dingtalk.clientSecret",
141
+ unresolvedReasonStyle: "detailed",
142
+ });
143
+ if (resolved.value) {
144
+ return { value: resolved.value };
145
+ }
146
+ const failure = buildSecretInputFailure(
147
+ value,
148
+ resolved.unresolvedRefReason || "secret reference is unresolved",
149
+ );
150
+ log?.warn?.("[DingTalk][SecretInput] Failed to resolve secret", {
141
151
  provider: value.provider,
142
152
  id: value.id,
143
153
  error: failure.reason,
144
154
  });
145
155
  return { failure };
146
- }
147
- try {
148
- // Trust boundary: file SecretInput reads the configured local path. Use it
149
- // only with trusted plugin configuration.
150
- const filePath = resolveRelativePath(value.id);
151
- const secret = (await readFile(filePath, "utf8")).trim();
152
- if (secret) {
153
- return { value: secret };
154
- }
155
- return { failure: buildSecretInputFailure(value, "file secret is empty") };
156
156
  } catch (error) {
157
157
  const failure = buildSecretInputFailure(
158
158
  value,
159
159
  error instanceof Error ? error.message : String(error),
160
160
  );
161
- log?.warn?.("[DingTalk][SecretInput] Failed to read file secret", {
161
+ log?.warn?.("[DingTalk][SecretInput] Failed to resolve secret", {
162
162
  provider: value.provider,
163
163
  id: value.id,
164
164
  error: failure.reason,
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAuR5F,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;;kBAmCnC,iBAAiB;;;;;;;;AAFjC,wBAaE"}