@jeik/dingtalk-connector 0.8.32 → 0.8.34

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 (32) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/card-content-cache-BsFPIwOr.mjs +2 -0
  3. package/dist/{runtime-CjE4Z9G5.mjs → channel-HfPzJGwO.mjs} +18 -29
  4. package/dist/chunk-upload-CNlZg1SF.mjs +2 -0
  5. package/dist/{common-BfIYs1yC.mjs → common-1-GDZ2h3.mjs} +2 -2
  6. package/dist/{common-C0Oaf0z-.mjs → common-DCVdWMiZ.mjs} +1 -1
  7. package/dist/{connection-obtrPkj4.mjs → connection-Bh3U9I4x.mjs} +2 -2
  8. package/dist/dws-delivery-context-DLR1ghNQ.mjs +272 -0
  9. package/dist/entry-bundled.mjs +1 -1
  10. package/dist/gateway-methods-CV8y0xGw.mjs +2 -0
  11. package/dist/{gateway-methods-BdcpfxEZ.mjs → gateway-methods-Ce1FXmCx.mjs} +4 -4
  12. package/dist/index.d.mts +12 -0
  13. package/dist/index.mjs +3 -2
  14. package/dist/{message-handler-BeTQQCUe.mjs → message-handler-Dsyk414y.mjs} +43 -14
  15. package/dist/{messaging-CK0Wz3Ze.mjs → messaging-CwQLykl6.mjs} +1 -1
  16. package/dist/runtime-DDMzayvA.mjs +26 -0
  17. package/dist/utils-legacy-Bx5izlVF.mjs +3 -0
  18. package/openclaw.plugin.json +28 -1
  19. package/package.json +1 -1
  20. package/src/config/schema.ts +11 -0
  21. package/src/core/message-handler.ts +2 -0
  22. package/src/reply-dispatcher.ts +46 -0
  23. package/src/services/dws-delivery-context.ts +458 -0
  24. package/dist/card-content-cache-C7-XHhHw.mjs +0 -2
  25. package/dist/chunk-upload-BKM1y4Ba.mjs +0 -2
  26. package/dist/gateway-methods-DQ6TwOAr.mjs +0 -2
  27. package/dist/utils-legacy-BOqY3zE7.mjs +0 -3
  28. /package/dist/{background-work-DkaSDqWk.mjs → background-work-874-aK4-.mjs} +0 -0
  29. /package/dist/{card-content-cache-DT_OYThh.mjs → card-content-cache-CGggfgCg.mjs} +0 -0
  30. /package/dist/{chunk-upload-CIU5G_ay.mjs → chunk-upload-z185bTqH.mjs} +0 -0
  31. /package/dist/{game-xiyou-uwsv23AC.mjs → game-xiyou-CKoWJobs.mjs} +0 -0
  32. /package/dist/{utils-legacy-DQ3HeGHh.mjs → utils-legacy-CGEa8tg2.mjs} +0 -0
package/CHANGELOG.md CHANGED
@@ -5,6 +5,26 @@ 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.34] - 2026-07-31
9
+
10
+ ### Fixes
11
+
12
+ - **dwsDeliveryContext 在 jeikclaw 上无法注入** — 插件原先只 `import("openclaw/plugin-sdk/outbound-runtime")`,而生产安装包名为 `jeikclaw`,导致模块解析失败、目标会话写不进 outbound_message。现按顺序尝试 `openclaw` / `jeikclaw` / 从 gateway 入口 resolve 包路径;并改为始终 `console` 输出注入结果,便于无 debug 时排障。解析命令时折叠 shell `\` 续行。
13
+
14
+ ## [0.8.33] - 2026-07-31
15
+
16
+ ### Features
17
+
18
+ - **dws 发消息成功后注入 outbound_message 上下文** — 与 `message` 工具 OC-4 一致:当 agent 通过 `dws chat message send|send-by-bot|reply|send-by-webhook` 发消息成功时,自动向目标会话写入模型可见的出站上下文(来源/调用人/正文/可复制 ID)。
19
+ - 配置项 `channels.dingtalk-connector.dwsDeliveryContext`:`target`(默认)/ `source` / `both` / `off`
20
+ - **仅叠加**:不影响普通回复与 message 工具;解析失败或非发消息命令时 no-op
21
+ - 实现:`src/services/dws-delivery-context.ts` + `onCommandOutput` 钩子;从命令行解析 `--group`/`--user` 后经 `resolveOutboundSessionRoute` 映射 sessionKey
22
+ - 依赖 OpenClaw(≥ 本配套版本)`plugin-sdk/outbound-runtime` 导出 `appendOutboundMessageDeliveryContext`
23
+
24
+ ### Docs
25
+
26
+ - **CHANGELOG / schema / openclaw.plugin.json** — 同步 `dwsDeliveryContext` 字段说明与 UI help。
27
+
8
28
  ## [0.8.32] - 2026-07-29
9
29
 
10
30
  ### Features
@@ -0,0 +1,2 @@
1
+ import { r as rememberCardContent } from "./card-content-cache-CGggfgCg.mjs";
2
+ export { rememberCardContent };
@@ -3,7 +3,7 @@ import { a as resolveDingtalkAccount, c as addWildcardAllowFrom, d as hasConfigu
3
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-CK0Wz3Ze.mjs";
6
+ import { S as finalClean, f as getActiveCardForConversation, n as sendMediaToDingTalk, o as sendTextToDingTalk } from "./messaging-CwQLykl6.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
@@ -111,6 +111,20 @@ const DingtalkSharedConfigShape = {
111
111
  */
112
112
  messageAnswerCard: z$1.boolean().optional(),
113
113
  groupReplyMode: GroupReplyModeSchema,
114
+ /**
115
+ * dws CLI 发消息成功后,是否写入与 message 工具一致的 outbound_message 上下文。
116
+ * - target(默认):写入目标会话(群/好友),对方会话后续能看见「谁让 bot 发了什么」
117
+ * - source:写入发起会话
118
+ * - both:两边都写
119
+ * - off:关闭(完全不改行为)
120
+ * 仅叠加:不影响普通回复 / message 工具;解析失败或 dws 非发消息命令时 no-op。
121
+ */
122
+ dwsDeliveryContext: z$1.enum([
123
+ "off",
124
+ "target",
125
+ "source",
126
+ "both"
127
+ ]).optional(),
114
128
  /** AI Card 模板 ID,不填则使用官方默认模板 */
115
129
  cardTemplateId: z$1.string().optional(),
116
130
  /** AI Card 内容变量名,对应卡片模板中的变量字段,不填默认 content(与默认增强模板一致) */
@@ -956,8 +970,8 @@ async function monitorDingtalkProvider(opts = {}) {
956
970
  const log = createLogger(cfg.channels?.["dingtalk-connector"]?.debug ?? false);
957
971
  const [accountsModule, monitorAccountModule, monitorSingleModule] = await Promise.all([
958
972
  import("./accounts-BQptOmgB.mjs"),
959
- import("./message-handler-BeTQQCUe.mjs"),
960
- import("./connection-obtrPkj4.mjs")
973
+ import("./message-handler-Dsyk414y.mjs"),
974
+ import("./connection-Bh3U9I4x.mjs")
961
975
  ]);
962
976
  const { resolveDingtalkAccount, listEnabledDingtalkAccounts } = accountsModule;
963
977
  const { handleDingTalkMessage } = monitorAccountModule;
@@ -1445,29 +1459,4 @@ function initDingtalkPluginConfigSchema() {
1445
1459
  dingtalkPlugin.configSchema = buildChannelConfigSchema(DingtalkConfigBaseSchema);
1446
1460
  }
1447
1461
  //#endregion
1448
- //#region src/runtime.ts
1449
- /**
1450
- * 自实现的运行时存储工厂,避免依赖特定版本 openclaw 是否导出 createPluginRuntimeStore。
1451
- * 旧版 openclaw 没有导出该函数,直接 import 会导致 TypeError,因此在此处内联实现。
1452
- */
1453
- function createRuntimeStore(errorMessage) {
1454
- let runtimeValue = null;
1455
- return {
1456
- setRuntime: (next) => {
1457
- runtimeValue = next;
1458
- },
1459
- clearRuntime: () => {
1460
- runtimeValue = null;
1461
- },
1462
- tryGetRuntime: () => {
1463
- return runtimeValue;
1464
- },
1465
- getRuntime: () => {
1466
- if (runtimeValue === null) throw new Error(errorMessage);
1467
- return runtimeValue;
1468
- }
1469
- };
1470
- }
1471
- const { setRuntime: setDingtalkRuntime, getRuntime: getDingtalkRuntime } = createRuntimeStore("DingTalk runtime not initialized");
1472
- //#endregion
1473
- export { initDingtalkPluginConfigSchema as a, dingtalkPlugin as i, setDingtalkRuntime as n, CHANNEL_ID as r, getDingtalkRuntime as t };
1462
+ export { dingtalkPlugin as n, initDingtalkPluginConfigSchema as r, CHANNEL_ID as t };
@@ -0,0 +1,2 @@
1
+ import { a as uploadLargeFileByChunks } from "./chunk-upload-z185bTqH.mjs";
2
+ export { uploadLargeFileByChunks };
@@ -1,6 +1,6 @@
1
1
  import { t as createLogger } from "./logger-CnBTcwyq.mjs";
2
2
  import { r as dingtalkUploadHttp } from "./http-client-DFWZgO1n.mjs";
3
- import { t as CHUNK_CONFIG } from "./chunk-upload-CIU5G_ay.mjs";
3
+ import { t as CHUNK_CONFIG } from "./chunk-upload-z185bTqH.mjs";
4
4
  import * as fs from "fs";
5
5
  import * as path from "path";
6
6
  import FormData from "form-data";
@@ -48,7 +48,7 @@ async function uploadMediaToDingTalk(filePath, mediaType, oapiToken, maxSize = 2
48
48
  if ((mediaType === "video" || mediaType === "file") && fileSize > CHUNK_CONFIG.SIZE_THRESHOLD) {
49
49
  log?.info?.(`文件超过 20MB,使用分块上传:${absPath} (${fileSizeMB}MB)`);
50
50
  try {
51
- const { uploadLargeFileByChunks } = await import("./chunk-upload-BKM1y4Ba.mjs");
51
+ const { uploadLargeFileByChunks } = await import("./chunk-upload-CNlZg1SF.mjs");
52
52
  const downloadCode = await uploadLargeFileByChunks(absPath, mediaType, oapiToken, debugEnabled);
53
53
  if (downloadCode) {
54
54
  log?.info?.(`分块上传成功:${absPath}, download_code: ${downloadCode}`);
@@ -1,2 +1,2 @@
1
- import { i as VIDEO_MARKER_PATTERN, r as FILE_MARKER_PATTERN, t as AUDIO_MARKER_PATTERN } from "./common-BfIYs1yC.mjs";
1
+ import { i as VIDEO_MARKER_PATTERN, r as FILE_MARKER_PATTERN, t as AUDIO_MARKER_PATTERN } from "./common-1-GDZ2h3.mjs";
2
2
  export { AUDIO_MARKER_PATTERN, FILE_MARKER_PATTERN, VIDEO_MARKER_PATTERN };
@@ -1,5 +1,5 @@
1
- import { i as checkAndMarkDingtalkMessage } from "./utils-legacy-DQ3HeGHh.mjs";
2
- import { n as onBackgroundWorkCountChange, t as getActiveBackgroundWorkCount } from "./background-work-DkaSDqWk.mjs";
1
+ import { i as checkAndMarkDingtalkMessage } from "./utils-legacy-CGEa8tg2.mjs";
2
+ import { n as onBackgroundWorkCountChange, t as getActiveBackgroundWorkCount } from "./background-work-874-aK4-.mjs";
3
3
  import * as fs from "fs";
4
4
  //#region src/core/connection.ts
5
5
  /**
@@ -0,0 +1,272 @@
1
+ import { t as CHANNEL_ID } from "./channel-HfPzJGwO.mjs";
2
+ //#region src/services/dws-delivery-context.ts
3
+ /**
4
+ * DWS 发消息成功后注入 outbound_message 上下文(对齐 OpenClaw message 工具 OC-4)。
5
+ *
6
+ * - 不修改 dws 本体:仅解析 OpenClaw exec/shell 回调里看到的命令行
7
+ * - 默认开启(target):只在 dws 发消息成功时追加上下文,不影响普通回复 / message 工具
8
+ * - 可配置 off | target | source | both
9
+ */
10
+ const SEND_COMMAND_RE = /\bdws\b(?:\s+[^\s]+)*\s+chat\s+message\s+(send-by-bot|send-by-webhook|send|reply)\b/i;
11
+ /** 从命令行提取 --flag value / --flag=value / 布尔 --flag */
12
+ function extractFlag(cmd, name) {
13
+ const eq = new RegExp(`--${name}(?:=|\\s+)(?:"([^"]*)"|'([^']*)'|(\\S+))`, "i");
14
+ const m = cmd.match(eq);
15
+ if (!m) return void 0;
16
+ return (m[1] ?? m[2] ?? m[3] ?? "").trim() || void 0;
17
+ }
18
+ function hasBoolFlag(cmd, name) {
19
+ return new RegExp(`(?:^|\\s)--${name}(?:\\s|=|$)`, "i").test(cmd);
20
+ }
21
+ function splitCsv(raw) {
22
+ if (!raw) return void 0;
23
+ const parts = raw.split(",").map((s) => s.trim()).filter(Boolean);
24
+ return parts.length ? parts : void 0;
25
+ }
26
+ /**
27
+ * 判断是否为 dws 发消息类命令,并解析关键参数。
28
+ * 解析失败返回 null(调用方 no-op,不影响现有体验)。
29
+ */
30
+ /** 折叠 shell 续行 `\` + 换行,便于从 tool title/output 里解析 */
31
+ function normalizeShellCommandText(raw) {
32
+ return String(raw || "").replace(/\\\r?\n/g, " ").replace(/\r?\n/g, " ").replace(/[ \t]+/g, " ").trim();
33
+ }
34
+ function parseDwsSendCommand(commandText) {
35
+ const cmd = normalizeShellCommandText(commandText);
36
+ if (!cmd) return null;
37
+ const m = cmd.match(SEND_COMMAND_RE);
38
+ if (!m) return null;
39
+ const sub = m[1].toLowerCase();
40
+ let kind;
41
+ if (sub === "send-by-bot") kind = "bot-send";
42
+ else if (sub === "send-by-webhook") kind = "webhook-send";
43
+ else if (sub === "reply") kind = "reply";
44
+ else kind = "user-send";
45
+ const group = extractFlag(cmd, "group") || extractFlag(cmd, "conversation-id") || extractFlag(cmd, "id") || extractFlag(cmd, "chat");
46
+ const user = extractFlag(cmd, "user") || extractFlag(cmd, "open-dingtalk-id") || splitCsv(extractFlag(cmd, "users"))?.[0] || splitCsv(extractFlag(cmd, "open-dingtalk-ids"))?.[0];
47
+ const text = extractFlag(cmd, "text") || extractFlag(cmd, "content") || extractFlag(cmd, "body") || extractFlag(cmd, "message") || extractFlag(cmd, "markdown");
48
+ let target;
49
+ let rawTarget;
50
+ let targetChatType;
51
+ if (group) {
52
+ rawTarget = group.replace(/^(group:|channel:)/i, "");
53
+ target = `group:${rawTarget}`;
54
+ targetChatType = "group";
55
+ } else if (user) {
56
+ rawTarget = user.replace(/^(user:|dm:)/i, "");
57
+ target = `user:${rawTarget}`;
58
+ targetChatType = "direct";
59
+ }
60
+ if (kind === "webhook-send" && !target) return {
61
+ kind,
62
+ message: text,
63
+ title: extractFlag(cmd, "title"),
64
+ atAll: hasBoolFlag(cmd, "at-all"),
65
+ atUserIds: splitCsv(extractFlag(cmd, "at-users")),
66
+ atMobiles: splitCsv(extractFlag(cmd, "at-mobiles")),
67
+ command: cmd.slice(0, 2e3)
68
+ };
69
+ return {
70
+ kind,
71
+ target,
72
+ rawTarget,
73
+ targetChatType,
74
+ message: text,
75
+ title: extractFlag(cmd, "title"),
76
+ atAll: hasBoolFlag(cmd, "at-all"),
77
+ atUserIds: splitCsv(extractFlag(cmd, "at-user-ids")) || splitCsv(extractFlag(cmd, "at-users")),
78
+ atOpenDingTalkIds: splitCsv(extractFlag(cmd, "at-open-dingtalk-ids")),
79
+ atMobiles: splitCsv(extractFlag(cmd, "at-mobiles")),
80
+ robotCode: extractFlag(cmd, "robot-code"),
81
+ command: cmd.slice(0, 2e3)
82
+ };
83
+ }
84
+ function resolveDwsDeliveryContextMode(config) {
85
+ const raw = config?.dwsDeliveryContext;
86
+ if (raw === "off" || raw === false) return "off";
87
+ if (raw === "source" || raw === "both" || raw === "target") return raw;
88
+ if (raw === true || raw === void 0 || raw === null) return "target";
89
+ return "off";
90
+ }
91
+ let warnedMissingApi = false;
92
+ /** 始终打到 console,不依赖 debug 开关(否则生产无法排障) */
93
+ function alwaysLog(level, msg) {
94
+ const line = `[DingTalk][dwsDeliveryContext] ${msg}`;
95
+ if (level === "error") console.error(line);
96
+ else if (level === "warn") console.warn(line);
97
+ else console.log(line);
98
+ }
99
+ function resolveModeFromConfigs(cfg, accountConfig) {
100
+ if (accountConfig?.dwsDeliveryContext !== void 0) return resolveDwsDeliveryContextMode(accountConfig);
101
+ const channelCfg = cfg?.channels?.[CHANNEL_ID];
102
+ if (channelCfg?.dwsDeliveryContext !== void 0) return resolveDwsDeliveryContextMode(channelCfg);
103
+ return "target";
104
+ }
105
+ /**
106
+ * 加载 OC-4 append API。
107
+ *
108
+ * 注意:钉钉插件装在 ~/.openclaw/npm/projects/... 下,Node 从插件路径
109
+ * 解析不到名为 openclaw/jeikclaw 的 package(包名还可能是 jeikclaw)。
110
+ * 必须用 gateway 进程入口(process.argv[1])定位 dist/plugin-sdk。
111
+ */
112
+ async function loadOutboundRuntime() {
113
+ const { pathToFileURL } = await import("node:url");
114
+ const path = await import("node:path");
115
+ const fs = await import("node:fs");
116
+ const { createRequire } = await import("node:module");
117
+ const candidates = [
118
+ "openclaw/plugin-sdk/outbound-runtime",
119
+ "jeikclaw/plugin-sdk/outbound-runtime",
120
+ "jeikclaw/dist/plugin-sdk/outbound-runtime.js",
121
+ "openclaw/dist/plugin-sdk/outbound-runtime.js"
122
+ ];
123
+ const pushIfExists = (filePath) => {
124
+ try {
125
+ if (filePath && fs.existsSync(filePath)) candidates.push(pathToFileURL(filePath).href);
126
+ } catch {}
127
+ };
128
+ const entry = typeof process.argv[1] === "string" ? process.argv[1] : "";
129
+ if (entry) {
130
+ const entryDir = path.dirname(path.resolve(entry));
131
+ pushIfExists(path.join(entryDir, "plugin-sdk", "outbound-runtime.js"));
132
+ pushIfExists(path.join(entryDir, "dist", "plugin-sdk", "outbound-runtime.js"));
133
+ let dir = entryDir;
134
+ for (let i = 0; i < 5; i++) {
135
+ const pkgJson = path.join(dir, "package.json");
136
+ if (fs.existsSync(pkgJson)) {
137
+ pushIfExists(path.join(dir, "dist", "plugin-sdk", "outbound-runtime.js"));
138
+ pushIfExists(path.join(dir, "plugin-sdk", "outbound-runtime.js"));
139
+ break;
140
+ }
141
+ const parent = path.dirname(dir);
142
+ if (parent === dir) break;
143
+ dir = parent;
144
+ }
145
+ try {
146
+ const req = createRequire(pathToFileURL(path.resolve(entry)).href);
147
+ for (const name of ["jeikclaw", "openclaw"]) try {
148
+ const pkgJson = req.resolve(`${name}/package.json`);
149
+ const root = path.dirname(pkgJson);
150
+ pushIfExists(path.join(root, "dist", "plugin-sdk", "outbound-runtime.js"));
151
+ } catch {}
152
+ } catch {}
153
+ }
154
+ try {
155
+ const execDir = path.dirname(process.execPath);
156
+ pushIfExists(path.join(execDir, "..", "lib", "node_modules", "jeikclaw", "dist", "plugin-sdk", "outbound-runtime.js"));
157
+ pushIfExists(path.join(execDir, "..", "lib", "node_modules", "openclaw", "dist", "plugin-sdk", "outbound-runtime.js"));
158
+ } catch {}
159
+ const errors = [];
160
+ const seen = /* @__PURE__ */ new Set();
161
+ for (const id of candidates) {
162
+ if (!id || seen.has(id)) continue;
163
+ seen.add(id);
164
+ try {
165
+ const oc = await import(id);
166
+ if (typeof oc.appendOutboundMessageDeliveryContext === "function") return {
167
+ append: oc.appendOutboundMessageDeliveryContext.bind(oc),
168
+ resolveRoute: typeof oc.resolveOutboundSessionRoute === "function" ? oc.resolveOutboundSessionRoute.bind(oc) : void 0,
169
+ ensureEntry: typeof oc.ensureOutboundSessionEntry === "function" ? oc.ensureOutboundSessionEntry.bind(oc) : void 0,
170
+ via: id
171
+ };
172
+ errors.push(`${id}: no append export`);
173
+ } catch (e) {
174
+ errors.push(`${id}: ${e?.message || e}`);
175
+ }
176
+ }
177
+ if (!warnedMissingApi) {
178
+ warnedMissingApi = true;
179
+ alwaysLog("warn", `无法加载 outbound-runtime。argv1=${entry || "-"} tried=${seen.size} err=${errors.slice(0, 4).join(" | ")}`);
180
+ }
181
+ return null;
182
+ }
183
+ /**
184
+ * 在 dws 发消息命令成功结束后,向目标/来源会话写入 outbound_message。
185
+ * 全程 best-effort,失败只打日志。
186
+ */
187
+ async function maybeInjectDwsOutboundContext(params) {
188
+ const log = params.log;
189
+ const effectiveMode = resolveModeFromConfigs(params.cfg, params.accountConfig);
190
+ if (effectiveMode === "off") return false;
191
+ if (params.phase && params.phase !== "end") return false;
192
+ if (params.exitCode !== void 0 && params.exitCode !== null && params.exitCode !== 0) return false;
193
+ const parsed = parseDwsSendCommand(params.commandText);
194
+ if (!parsed) return false;
195
+ if (!parsed.target && effectiveMode === "target") {
196
+ alwaysLog("warn", `解析到 dws ${parsed.kind} 但无 --group/--user 目标,跳过 target 注入。cmd=${parsed.command.slice(0, 160)}`);
197
+ return false;
198
+ }
199
+ try {
200
+ const oc = await loadOutboundRuntime();
201
+ if (!oc?.append) return false;
202
+ let targetRoute = null;
203
+ const targetTo = parsed.target;
204
+ if (targetTo && typeof oc.resolveRoute === "function") try {
205
+ targetRoute = await oc.resolveRoute({
206
+ cfg: params.cfg,
207
+ channel: CHANNEL_ID,
208
+ agentId: params.agentId,
209
+ accountId: params.accountId,
210
+ target: targetTo,
211
+ currentSessionKey: params.sourceSessionKey
212
+ });
213
+ if (targetRoute && typeof oc.ensureEntry === "function") await oc.ensureEntry({
214
+ cfg: params.cfg,
215
+ channel: CHANNEL_ID,
216
+ accountId: params.accountId,
217
+ route: targetRoute
218
+ });
219
+ } catch (err) {
220
+ alwaysLog("warn", `resolveOutboundSessionRoute 失败: ${err?.message || err}`);
221
+ log?.warn?.(`[DingTalk][dwsDeliveryContext] resolveOutboundSessionRoute 失败: ${err?.message || err}`);
222
+ }
223
+ if (effectiveMode === "target" && !targetRoute?.sessionKey) {
224
+ alwaysLog("warn", `target 模式但未解析到 sessionKey,无法写入目标会话。targetTo=${targetTo} via=${oc.via}`);
225
+ return false;
226
+ }
227
+ const action = parsed.kind === "bot-send" ? "dws.chat.message.send-by-bot" : parsed.kind === "webhook-send" ? "dws.chat.message.send-by-webhook" : parsed.kind === "reply" ? "dws.chat.message.reply" : "dws.chat.message.send";
228
+ const actionParams = {
229
+ action: "send",
230
+ via: "dws",
231
+ dwsKind: parsed.kind,
232
+ message: parsed.message ?? "",
233
+ title: parsed.title,
234
+ target: targetTo,
235
+ to: parsed.rawTarget,
236
+ channel: CHANNEL_ID,
237
+ accountId: params.accountId,
238
+ atAll: parsed.atAll || void 0,
239
+ atUserIds: parsed.atUserIds,
240
+ atOpenDingTalkIds: parsed.atOpenDingTalkIds,
241
+ atMobiles: parsed.atMobiles,
242
+ robotCode: parsed.robotCode,
243
+ command: parsed.command
244
+ };
245
+ const idempotencyKey = params.toolCallId?.trim() || `dws:${params.sourceSessionKey}:${Date.now()}:${Math.random().toString(36).slice(2, 8)}`;
246
+ await oc.append({
247
+ cfg: params.cfg,
248
+ mode: effectiveMode,
249
+ agentId: params.agentId,
250
+ sourceSessionKey: params.sourceSessionKey,
251
+ sourceAccountId: params.accountId,
252
+ invokerId: params.invokerId,
253
+ invokerName: params.invokerName,
254
+ targetTo: targetTo || params.sourceSessionKey,
255
+ targetChannel: CHANNEL_ID,
256
+ targetAccountId: params.accountId,
257
+ targetRoute,
258
+ actionParams,
259
+ action,
260
+ idempotencyKey
261
+ });
262
+ alwaysLog("info", `已注入 outbound_message mode=${effectiveMode} kind=${parsed.kind} target=${targetTo || "-"} route=${targetRoute?.sessionKey || "-"} via=${oc.via}`);
263
+ log?.info?.(`[DingTalk][dwsDeliveryContext] 已注入 outbound_message mode=${effectiveMode} kind=${parsed.kind} target=${targetTo || "-"} route=${targetRoute?.sessionKey || "-"}`);
264
+ return true;
265
+ } catch (err) {
266
+ alwaysLog("warn", `注入失败(已忽略,不影响发消息): ${err?.message || err}`);
267
+ log?.warn?.(`[DingTalk][dwsDeliveryContext] 注入失败(已忽略,不影响发消息): ${err?.message || err}`);
268
+ return false;
269
+ }
270
+ }
271
+ //#endregion
272
+ export { maybeInjectDwsOutboundContext };
@@ -23,7 +23,7 @@ var entry_bundled_default = defineBundledChannelEntry({
23
23
  exportName: "setDingtalkRuntime"
24
24
  },
25
25
  async registerFull(api) {
26
- const { registerGatewayMethods } = await import("./gateway-methods-DQ6TwOAr.mjs");
26
+ const { registerGatewayMethods } = await import("./gateway-methods-CV8y0xGw.mjs");
27
27
  registerGatewayMethods(api);
28
28
  }
29
29
  });
@@ -0,0 +1,2 @@
1
+ import { t as registerGatewayMethods } from "./gateway-methods-Ce1FXmCx.mjs";
2
+ export { registerGatewayMethods };
@@ -1,8 +1,8 @@
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-CK0Wz3Ze.mjs";
5
- import { d as getUnionId, h as recallEmotionReply } from "./utils-legacy-DQ3HeGHh.mjs";
4
+ import { c as prepareMultiBotMentions, d as finishAICard, i as sendProactive, s as buildBotMentionTable } from "./messaging-CwQLykl6.mjs";
5
+ import { d as getUnionId, h as recallEmotionReply } from "./utils-legacy-CGEa8tg2.mjs";
6
6
  //#region src/docs.ts
7
7
  var DingtalkDocsClient = class {
8
8
  config;
@@ -592,7 +592,7 @@ function registerGatewayMethods(api) {
592
592
  });
593
593
  const results = {};
594
594
  if (cardInstanceId) try {
595
- const { getAccessToken } = await import("./utils-legacy-BOqY3zE7.mjs");
595
+ const { getAccessToken } = await import("./utils-legacy-Bx5izlVF.mjs");
596
596
  const token = await getAccessToken(account.config);
597
597
  await finishAICard({
598
598
  cardInstanceId: String(cardInstanceId),
@@ -752,7 +752,7 @@ function registerGatewayMethods(api) {
752
752
  try {
753
753
  const account = resolveDingtalkAccount({ cfg });
754
754
  if (!account.config?.clientId || !account.config?.clientSecret) return respond(false, { error: "Not configured" });
755
- const { getAccessToken } = await import("./utils-legacy-BOqY3zE7.mjs");
755
+ const { getAccessToken } = await import("./utils-legacy-Bx5izlVF.mjs");
756
756
  await getAccessToken(account.config);
757
757
  respond(true, {
758
758
  ok: true,
package/dist/index.d.mts CHANGED
@@ -76,6 +76,12 @@ declare const DingtalkConfigSchema: z$1.ZodObject<{
76
76
  text: "text";
77
77
  markdown: "markdown";
78
78
  }>>;
79
+ dwsDeliveryContext: z$1.ZodOptional<z$1.ZodEnum<{
80
+ source: "source";
81
+ off: "off";
82
+ target: "target";
83
+ both: "both";
84
+ }>>;
79
85
  cardTemplateId: z$1.ZodOptional<z$1.ZodString>;
80
86
  cardContentVar: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodString>>;
81
87
  answerCard: z$1.ZodOptional<z$1.ZodBoolean>;
@@ -134,6 +140,12 @@ declare const DingtalkConfigSchema: z$1.ZodObject<{
134
140
  text: "text";
135
141
  markdown: "markdown";
136
142
  }>>;
143
+ dwsDeliveryContext: z$1.ZodOptional<z$1.ZodEnum<{
144
+ source: "source";
145
+ off: "off";
146
+ target: "target";
147
+ both: "both";
148
+ }>>;
137
149
  cardTemplateId: z$1.ZodOptional<z$1.ZodString>;
138
150
  cardContentVar: z$1.ZodDefault<z$1.ZodOptional<z$1.ZodString>>;
139
151
  answerCard: z$1.ZodOptional<z$1.ZodBoolean>;
package/dist/index.mjs CHANGED
@@ -1,5 +1,6 @@
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";
1
+ import { n as dingtalkPlugin, r as initDingtalkPluginConfigSchema } from "./channel-HfPzJGwO.mjs";
2
+ import { n as setDingtalkRuntime } from "./runtime-DDMzayvA.mjs";
3
+ import { t as registerGatewayMethods } from "./gateway-methods-Ce1FXmCx.mjs";
3
4
  //#region index.ts
4
5
  /**
5
6
  * 检测同一 plugin id 在多个路径被加载的情况。
@@ -1,15 +1,16 @@
1
1
  import { a as resolveDingtalkAccount } from "./accounts-BAzdqkAV.mjs";
2
- import { r as CHANNEL_ID, t as getDingtalkRuntime } from "./runtime-CjE4Z9G5.mjs";
2
+ import { t as CHANNEL_ID } from "./channel-HfPzJGwO.mjs";
3
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-CK0Wz3Ze.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-CwQLykl6.mjs";
7
7
  import { a as QUEUE_BUSY_ACK_PHRASES, n as normalizeSlashCommand, t as buildSessionContext } from "./session-DJ4jYqPv.mjs";
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-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";
8
+ import { t as getDingtalkRuntime } from "./runtime-DDMzayvA.mjs";
9
+ 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-CGEa8tg2.mjs";
10
+ import { r as trackBackgroundWork } from "./background-work-874-aK4-.mjs";
11
+ import "./chunk-upload-z185bTqH.mjs";
12
+ import { a as toLocalPath, i as VIDEO_MARKER_PATTERN, o as uploadMediaToDingTalk, r as FILE_MARKER_PATTERN, t as AUDIO_MARKER_PATTERN } from "./common-1-GDZ2h3.mjs";
13
+ import { n as lookupCardContent, t as collectCardLookupIds } from "./card-content-cache-CGggfgCg.mjs";
13
14
  import * as fs from "fs";
14
15
  import * as path from "path";
15
16
  import * as os from "node:os";
@@ -250,7 +251,7 @@ function readPayloadRawError(payload) {
250
251
  }
251
252
  function createDingtalkReplyDispatcher(params) {
252
253
  const core = getDingtalkRuntime();
253
- const { cfg, agentId, conversationId, senderId, isDirect, accountId, sessionWebhook, asyncMode = false, preCreatedCard } = params;
254
+ const { cfg, agentId, conversationId, senderId, isDirect, accountId, sessionWebhook, asyncMode = false, preCreatedCard, sessionKey: sourceSessionKey, senderName } = params;
254
255
  const account = resolveDingtalkAccount({
255
256
  cfg,
256
257
  accountId
@@ -633,7 +634,7 @@ function createDingtalkReplyDispatcher(params) {
633
634
  }
634
635
  } else log.info(`[DingTalk][closeStreaming] 养成系统:onCommandOutput 监听到 ${productsToProcess.size} 个 dws 产品: ${[...productsToProcess].join(", ")}`);
635
636
  if (productsToProcess.size > 0) {
636
- const { GamificationEngine } = await import("./game-xiyou-uwsv23AC.mjs");
637
+ const { GamificationEngine } = await import("./game-xiyou-CKoWJobs.mjs");
637
638
  const engine = GamificationEngine.getInstanceForUser(senderId);
638
639
  if (engine.isEnabled()) {
639
640
  const primaryProduct = [...productsToProcess][0];
@@ -978,7 +979,7 @@ function createDingtalkReplyDispatcher(params) {
978
979
  currentToolLine = "";
979
980
  await startStreaming();
980
981
  if (currentCardTarget) try {
981
- const { FILE_MARKER_PATTERN, VIDEO_MARKER_PATTERN, AUDIO_MARKER_PATTERN } = await import("./common-C0Oaf0z-.mjs");
982
+ const { FILE_MARKER_PATTERN, VIDEO_MARKER_PATTERN, AUDIO_MARKER_PATTERN } = await import("./common-DCVdWMiZ.mjs");
982
983
  await enqueueCardStream(buildCardContent().replace(FILE_MARKER_PATTERN, "").replace(VIDEO_MARKER_PATTERN, "").replace(AUDIO_MARKER_PATTERN, "").trim(), { force: true });
983
984
  } catch (err) {
984
985
  if (!isQpsLimitError(err)) log.error(`[DingTalk][onPartialReply] final-marker 刷卡失败:${err.message}`);
@@ -991,7 +992,7 @@ function createDingtalkReplyDispatcher(params) {
991
992
  if (payload.text?.trim()) hasModelStreamText = true;
992
993
  currentToolLine = "";
993
994
  try {
994
- const { FILE_MARKER_PATTERN, VIDEO_MARKER_PATTERN, AUDIO_MARKER_PATTERN } = await import("./common-C0Oaf0z-.mjs");
995
+ const { FILE_MARKER_PATTERN, VIDEO_MARKER_PATTERN, AUDIO_MARKER_PATTERN } = await import("./common-DCVdWMiZ.mjs");
995
996
  await enqueueCardStream(buildCardContent().replace(FILE_MARKER_PATTERN, "").replace(VIDEO_MARKER_PATTERN, "").replace(AUDIO_MARKER_PATTERN, "").trim());
996
997
  } catch (err) {
997
998
  if (isQpsLimitError(err)) log.warn(`[DingTalk][onPartialReply] QPS 限流,已排队尾随重试 latestLen=${latestCardContent.length}`);
@@ -1019,7 +1020,8 @@ function createDingtalkReplyDispatcher(params) {
1019
1020
  }
1020
1021
  },
1021
1022
  onCommandOutput: (payload) => {
1022
- const dwsMatch = (payload.title || payload.name || "").match(DWS_PRODUCT_PATTERN) || payload.output?.match(DWS_PRODUCT_PATTERN);
1023
+ const commandText = payload.title || payload.name || "";
1024
+ const dwsMatch = commandText.match(DWS_PRODUCT_PATTERN) || payload.output?.match(DWS_PRODUCT_PATTERN);
1023
1025
  if (dwsMatch) {
1024
1026
  const product = dwsMatch[1];
1025
1027
  if (!(payload.phase === "end" && payload.exitCode !== null && payload.exitCode !== 0)) {
@@ -1027,6 +1029,31 @@ function createDingtalkReplyDispatcher(params) {
1027
1029
  log.info(`[DingTalk][onCommandOutput] 检测到 dws 产品: ${product},phase=${payload.phase}, exitCode=${payload.exitCode}`);
1028
1030
  } else log.info(`[DingTalk][onCommandOutput] dws 命令执行失败,跳过: ${product},exitCode=${payload.exitCode}`);
1029
1031
  }
1032
+ if ((payload.phase || (payload.exitCode !== void 0 ? "end" : void 0)) === "end" && sourceSessionKey) {
1033
+ const fullCmd = [
1034
+ payload.title,
1035
+ payload.name,
1036
+ payload.output,
1037
+ commandText
1038
+ ].filter(Boolean).join("\n") || "";
1039
+ if (/\bdws\b/i.test(fullCmd)) import("./dws-delivery-context-DLR1ghNQ.mjs").then(({ maybeInjectDwsOutboundContext }) => maybeInjectDwsOutboundContext({
1040
+ cfg,
1041
+ accountConfig: account.config,
1042
+ agentId,
1043
+ accountId,
1044
+ sourceSessionKey,
1045
+ invokerId: senderId,
1046
+ invokerName: senderName,
1047
+ commandText: fullCmd,
1048
+ exitCode: payload.exitCode ?? 0,
1049
+ phase: "end",
1050
+ toolCallId: payload.toolCallId,
1051
+ log
1052
+ })).catch((err) => {
1053
+ console.warn(`[DingTalk][dwsDeliveryContext] onCommandOutput 钩子异常: ${err?.message || err}`);
1054
+ log.warn?.(`[DingTalk][dwsDeliveryContext] onCommandOutput 钩子异常: ${err?.message || err}`);
1055
+ });
1056
+ }
1030
1057
  }
1031
1058
  },
1032
1059
  markDispatchIdle,
@@ -2008,7 +2035,7 @@ async function handleDingTalkMessageInternal(params) {
2008
2035
  const rawText = content.text || "";
2009
2036
  let userContent = normalizeSlashCommand(rawText) || (content.imageUrls.length > 0 ? "请描述这张图片" : "");
2010
2037
  try {
2011
- const { GamificationEngine, isGamificationCommand } = await import("./game-xiyou-uwsv23AC.mjs");
2038
+ const { GamificationEngine, isGamificationCommand } = await import("./game-xiyou-CKoWJobs.mjs");
2012
2039
  if (isGamificationCommand(rawText)) {
2013
2040
  const engine = GamificationEngine.getInstanceForUser(senderId);
2014
2041
  if (rawText.trim().startsWith("/西游") || engine.isEnabled()) {
@@ -2277,7 +2304,9 @@ async function handleDingTalkMessageInternal(params) {
2277
2304
  messageCreateTimeMs: Date.now(),
2278
2305
  sessionWebhook: data.sessionWebhook,
2279
2306
  asyncMode,
2280
- preCreatedCard: earlyCard
2307
+ preCreatedCard: earlyCard,
2308
+ sessionKey,
2309
+ senderName
2281
2310
  });
2282
2311
  finalizeDispatchError = finalizeErr;
2283
2312
  if (config.clientId) {
@@ -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-C7-XHhHw.mjs");
374
+ const { rememberCardContent } = await import("./card-content-cache-BsFPIwOr.mjs");
375
375
  rememberCardContent({
376
376
  text: fixedContent,
377
377
  outTrackId: card.cardInstanceId,
@@ -0,0 +1,26 @@
1
+ //#region src/runtime.ts
2
+ /**
3
+ * 自实现的运行时存储工厂,避免依赖特定版本 openclaw 是否导出 createPluginRuntimeStore。
4
+ * 旧版 openclaw 没有导出该函数,直接 import 会导致 TypeError,因此在此处内联实现。
5
+ */
6
+ function createRuntimeStore(errorMessage) {
7
+ let runtimeValue = null;
8
+ return {
9
+ setRuntime: (next) => {
10
+ runtimeValue = next;
11
+ },
12
+ clearRuntime: () => {
13
+ runtimeValue = null;
14
+ },
15
+ tryGetRuntime: () => {
16
+ return runtimeValue;
17
+ },
18
+ getRuntime: () => {
19
+ if (runtimeValue === null) throw new Error(errorMessage);
20
+ return runtimeValue;
21
+ }
22
+ };
23
+ }
24
+ const { setRuntime: setDingtalkRuntime, getRuntime: getDingtalkRuntime } = createRuntimeStore("DingTalk runtime not initialized");
25
+ //#endregion
26
+ export { setDingtalkRuntime as n, getDingtalkRuntime as t };
@@ -0,0 +1,3 @@
1
+ import "./session-DJ4jYqPv.mjs";
2
+ import { l as getAccessToken } from "./utils-legacy-CGEa8tg2.mjs";
3
+ export { getAccessToken };