@youdu/openclaw-youdu 2026.5.8-2 → 2026.5.8-3

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.
@@ -4,7 +4,6 @@ import { TEXT_CHUNK_LIMIT, CHANNEL_ID, DEFAULT_DM_POLICY } from './const.js';
4
4
  import { youduSetupAdapter, youduSetupWizard } from './onboarding.js';
5
5
  import { DEFAULT_ACCOUNT_ID, formatPairingApproveHint } from './openclaw-compat.js';
6
6
  import { getYouDuRuntime } from './runtime.js';
7
- import { sendYouDuMediaMessage, sendYouDuTextMessage } from './message-sender.js';
8
7
 
9
8
  /*
10
9
  * Licensed under the MIT License.
@@ -148,26 +147,6 @@ const youduPlugin = {
148
147
  deliveryMode: "gateway",
149
148
  chunker: (text, limit) => getYouDuRuntime().channel.text.chunkMarkdownText(text, limit),
150
149
  textChunkLimit: TEXT_CHUNK_LIMIT,
151
- sendText: async ({ to, text, accountId }) => {
152
- return sendYouDuTextMessage({
153
- to,
154
- text,
155
- accountId,
156
- });
157
- },
158
- sendMedia: async ({ cfg, to, text, mediaUrl, mediaLocalRoots, accountId }) => {
159
- if (!mediaUrl) {
160
- throw new Error("No media URL provided");
161
- }
162
- return sendYouDuMediaMessage({
163
- cfg,
164
- to,
165
- text,
166
- mediaUrl,
167
- mediaLocalRoots,
168
- accountId,
169
- });
170
- },
171
150
  },
172
151
  status: {
173
152
  defaultRuntime: {
@@ -3,7 +3,7 @@ import type { ResolvedYouDuAccount, YouDuConfig } from './utils.js';
3
3
  import { WSClient, WsFrame } from "./sdk/ws-client";
4
4
  import { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
5
5
  export declare namespace Youdu {
6
- const requestTimeoutMs = 30000;
6
+ const RequestTimeoutMs = 30000;
7
7
  type MediaKind = 'image' | 'file' | 'audio' | 'video';
8
8
  interface MediaBody {
9
9
  mediaId?: string;
@@ -34,26 +34,8 @@ export declare namespace Youdu {
34
34
  headers: IncomingHttpHeaders;
35
35
  body: Buffer;
36
36
  }
37
- interface UploadMediaResponse {
38
- kind?: string;
39
- mediaId?: string;
40
- name?: string;
41
- contentType?: string;
42
- size?: number;
43
- width?: number;
44
- height?: number;
45
- duration?: number;
46
- data?: {
47
- mediaId?: string;
48
- name?: string;
49
- contentType?: string;
50
- size?: number;
51
- width?: number;
52
- height?: number;
53
- duration?: number;
54
- };
55
- }
56
37
  }
38
+ export declare const YouDuRequestTimeoutMs = 30000;
57
39
  export declare function inferInboundMessageText(body: {
58
40
  text?: {
59
41
  content?: string;
@@ -67,7 +49,6 @@ export declare function inferInboundMessageText(body: {
67
49
  video?: Youdu.MediaBody;
68
50
  }): string;
69
51
  export declare function extractInboundMedia(body: {
70
- msgType?: string;
71
52
  media?: Array<Youdu.MediaBody & {
72
53
  kind?: string;
73
54
  }>;
@@ -92,8 +73,3 @@ export declare function downloadInboundMedia(params: {
92
73
  config: YouDuConfig;
93
74
  token?: string;
94
75
  }): Promise<Array<Youdu.DownloadedInboundMedia>>;
95
- /**
96
- * 统一构建有度发送媒体时可访问的本地路径白名单。
97
- * 这样无论是回复链路还是官方 message send 链路,都能走同一套目录规则。
98
- */
99
- export declare function resolveYouDuMediaLocalRoots(config?: YouDuConfig): Promise<string[]>;
package/dist/src/media.js CHANGED
@@ -1,18 +1,17 @@
1
- import { getDefaultMediaLocalRoots, resolveStateDir, loadOutboundMediaFromUrl } from './openclaw-compat.js';
1
+ import { loadOutboundMediaFromUrl } from './openclaw-compat.js';
2
2
  import * as path from 'path';
3
3
  import * as https from 'node:https';
4
4
  import { getYouDuRuntime } from './runtime.js';
5
- import { UPLOAD_MEDIA_URL, DEFAULT_MEDIA_MAX_SIZE, DOWNLOAD_MEDIA_URL, HTTP_SCHEME, PORT } from './const.js';
5
+ import { DEFAULT_MEDIA_MAX_SIZE, DOWNLOAD_MEDIA_URL, HTTP_SCHEME, PORT, UPLOAD_MEDIA_URL } from './const.js';
6
6
  import { sendYouDuMediaReply } from './message-sender.js';
7
7
  import { buildYouDuTlsConnectOptions } from './tls.js';
8
- import os from 'os';
9
8
 
10
9
  /*
11
10
  * Licensed under the MIT License.
12
11
  */
13
12
  var Youdu;
14
13
  (function (Youdu) {
15
- Youdu.requestTimeoutMs = 30000;
14
+ Youdu.RequestTimeoutMs = 30000;
16
15
  })(Youdu || (Youdu = {}));
17
16
  function getMediaAuthHeaders(token) {
18
17
  const headers = {};
@@ -78,8 +77,8 @@ function request(url, options) {
78
77
  });
79
78
  });
80
79
  req.on("error", reject);
81
- req.setTimeout(Youdu.requestTimeoutMs, () => {
82
- req.destroy(new Error(`Youdu request timed out after ${Youdu.requestTimeoutMs}ms`));
80
+ req.setTimeout(Youdu.RequestTimeoutMs, () => {
81
+ req.destroy(new Error(`Youdu request timed out after ${Youdu.RequestTimeoutMs}ms`));
83
82
  });
84
83
  if (options.body) {
85
84
  req.write(options.body);
@@ -103,10 +102,6 @@ function fallbackFileNameFromUrl(mediaUrl) {
103
102
  }
104
103
  function kindFromContentType(contentType) {
105
104
  const normalized = String(contentType ?? '').toLowerCase();
106
- // SVG 更适合作为文件发送,避免被客户端按图片类型处理后无法正常展示或接收。
107
- if (normalized === 'image/svg+xml') {
108
- return 'file';
109
- }
110
105
  if (normalized.startsWith('image/')) {
111
106
  return 'image';
112
107
  }
@@ -130,15 +125,6 @@ function kindLabel(kind) {
130
125
  return '[文件]';
131
126
  }
132
127
  }
133
- function readUploadedMediaField(payload, key) {
134
- // 兼容两种上传返回格式:
135
- // 1. 当前协议文档里的根级字段;
136
- // 2. 旧实现里包在 data 下的字段。
137
- if (payload[key] !== undefined && payload[key] !== null) {
138
- return payload[key];
139
- }
140
- return payload.data?.[key];
141
- }
142
128
  function inferInboundMessageText(body) {
143
129
  const text = body.text?.content?.trim();
144
130
  if (text) {
@@ -153,24 +139,6 @@ function inferInboundMessageText(body) {
153
139
  }
154
140
  function extractInboundMedia(body) {
155
141
  const items = [];
156
- // 某些回调里 media[] 只有 mediaId/name/size,没有显式 kind,
157
- // 这里根据顶层 msgType 或具名媒体节点做一次回退推断。
158
- const fallbackKindFromBody = (() => {
159
- const normalizedMsgType = String(body.msgType ?? "").toLowerCase();
160
- if (normalizedMsgType === "image" || body.image?.mediaId) {
161
- return "image";
162
- }
163
- if (normalizedMsgType === "audio" || body.audio?.mediaId) {
164
- return "audio";
165
- }
166
- if (normalizedMsgType === "video" || body.video?.mediaId) {
167
- return "video";
168
- }
169
- if (normalizedMsgType === "file" || body.file?.mediaId) {
170
- return "file";
171
- }
172
- return undefined;
173
- })();
174
142
  const push = (kind, node) => {
175
143
  if (!node?.mediaId) {
176
144
  return;
@@ -195,10 +163,6 @@ function extractInboundMedia(body) {
195
163
  push(kind, media);
196
164
  continue;
197
165
  }
198
- if (!kind && fallbackKindFromBody) {
199
- push(fallbackKindFromBody, media);
200
- continue;
201
- }
202
166
  push('file', media);
203
167
  }
204
168
  if (items.length > 0) {
@@ -251,21 +215,21 @@ async function uploadOutboundMedia(params) {
251
215
  if (response.statusCode >= 400) {
252
216
  throw new Error(`upload media failed: ${response.statusCode} ${response.body.toString("utf8")}`);
253
217
  }
254
- // 上传接口返回成功后,统一抽取 mediaId 和媒体元数据,供 send_msg/respond_msg 复用。
255
218
  const payload = JSON.parse(response.body.toString("utf8"));
256
- const mediaId = String(readUploadedMediaField(payload, "mediaId") ?? "");
219
+ const data = payload?.data ?? {};
220
+ const mediaId = String(data.mediaId ?? '');
257
221
  if (!mediaId) {
258
222
  throw new Error('upload media failed: mediaId is missing');
259
223
  }
260
224
  return {
261
225
  kind,
262
226
  mediaId: mediaId,
263
- name: String(readUploadedMediaField(payload, "name") ?? fileName),
264
- size: Number(readUploadedMediaField(payload, "size") ?? media.buffer.length),
265
- width: Number(readUploadedMediaField(payload, "width") ?? 0) || undefined,
266
- height: Number(readUploadedMediaField(payload, "height") ?? 0) || undefined,
267
- duration: Number(readUploadedMediaField(payload, "duration") ?? 0) || undefined,
268
- contentType: String(readUploadedMediaField(payload, "contentType") ?? media.contentType ?? ''),
227
+ name: String(data.name ?? fileName),
228
+ size: Number(data.size ?? media.buffer.length),
229
+ width: Number(data.width ?? 0) || undefined,
230
+ height: Number(data.height ?? 0) || undefined,
231
+ duration: Number(data.duration ?? 0) || undefined,
232
+ contentType: String(data.contentType ?? media.contentType ?? ''),
269
233
  };
270
234
  }
271
235
  catch (error) {
@@ -284,13 +248,13 @@ function buildMediaMessageBody(media) {
284
248
  };
285
249
  switch (media.kind) {
286
250
  case 'image':
287
- return { msgType: 'image', image: payload };
251
+ return { msgtype: 'image', image: payload };
288
252
  case 'audio':
289
- return { msgType: 'audio', audio: payload };
253
+ return { msgtype: 'audio', audio: payload };
290
254
  case 'video':
291
- return { msgType: 'video', video: payload };
255
+ return { msgtype: 'video', video: payload };
292
256
  default:
293
- return { msgType: 'file', file: payload };
257
+ return { msgtype: 'file', file: payload };
294
258
  }
295
259
  }
296
260
  async function downloadInboundMedia(params) {
@@ -327,24 +291,5 @@ async function downloadInboundMedia(params) {
327
291
  }
328
292
  return mediaList;
329
293
  }
330
- /**
331
- * 统一构建有度发送媒体时可访问的本地路径白名单。
332
- * 这样无论是回复链路还是官方 message send 链路,都能走同一套目录规则。
333
- */
334
- async function resolveYouDuMediaLocalRoots(config) {
335
- const defaults = await getDefaultMediaLocalRoots();
336
- const roots = new Set();
337
- for (const root of defaults) {
338
- roots.add(path.resolve(root));
339
- }
340
- roots.add(path.resolve(resolveStateDir()));
341
- if (config?.mediaLocalRoots) {
342
- for (const root of config.mediaLocalRoots) {
343
- const resolved = path.resolve(root.replace(/^~(?=\/|$)/, os.homedir()));
344
- roots.add(resolved);
345
- }
346
- }
347
- return Array.from(roots);
348
- }
349
294
 
350
- export { Youdu, buildMediaMessageBody, downloadInboundMedia, extractInboundMedia, inferInboundMessageText, resolveYouDuMediaLocalRoots, uploadAndSendMedia, uploadOutboundMedia };
295
+ export { Youdu, buildMediaMessageBody, downloadInboundMedia, extractInboundMedia, inferInboundMessageText, uploadAndSendMedia, uploadOutboundMedia };
@@ -1,6 +1,6 @@
1
1
  import { RuntimeEnv } from 'openclaw/plugin-sdk/runtime-env';
2
- import { type Youdu } from "./media";
3
2
  import { WSClient, WsFrame } from './sdk/ws-client';
3
+ import { type Youdu } from "./media";
4
4
  /**
5
5
  * 发送 YouDu 回复
6
6
  */
@@ -16,30 +16,3 @@ export declare function sendYouDuMediaReply(params: {
16
16
  mediaAttachment: Youdu.InboundMediaAttachment;
17
17
  runtime: RuntimeEnv;
18
18
  }): Promise<void>;
19
- /**
20
- * 主动发送有度文本消息。
21
- */
22
- export declare function sendYouDuTextMessage(params: {
23
- to: string;
24
- text: string;
25
- accountId?: string | null;
26
- }): Promise<{
27
- channel: string;
28
- messageId: string;
29
- chatId: string;
30
- }>;
31
- /**
32
- * 主动发送有度媒体消息。
33
- */
34
- export declare function sendYouDuMediaMessage(params: {
35
- cfg: any;
36
- to: string;
37
- mediaUrl: string;
38
- text?: string;
39
- mediaLocalRoots?: readonly string[];
40
- accountId?: string | null;
41
- }): Promise<{
42
- channel: string;
43
- messageId: string;
44
- chatId: string;
45
- }>;
@@ -1,8 +1,4 @@
1
- import { DEFAULT_ACCOUNT_ID } from './openclaw-compat.js';
2
- import { getDirectChatTarget, getYouDuWebSocket } from './state-manager.js';
3
- import { CHANNEL_ID } from './const.js';
4
- import { resolveYouDuMediaLocalRoots, uploadOutboundMedia, buildMediaMessageBody } from './media.js';
5
- import { resolveYouDuAccount } from './utils.js';
1
+ import { buildMediaMessageBody } from './media.js';
6
2
 
7
3
  /*
8
4
  * Licensed under the MIT License.
@@ -14,7 +10,7 @@ async function sendYouDuReply(params) {
14
10
  const { wsClient, frame, text, runtime } = params;
15
11
  try {
16
12
  await wsClient.reply(frame, {
17
- msgType: 'text',
13
+ msgtype: 'text',
18
14
  text: { content: text },
19
15
  });
20
16
  }
@@ -35,76 +31,5 @@ async function sendYouDuMediaReply(params) {
35
31
  throw err;
36
32
  }
37
33
  }
38
- function getOutboundWsClient(accountId) {
39
- const resolvedAccountId = accountId ?? DEFAULT_ACCOUNT_ID;
40
- const wsClient = getYouDuWebSocket(resolvedAccountId);
41
- if (!wsClient) {
42
- throw new Error(`WSClient not connected for account ${resolvedAccountId}`);
43
- }
44
- return wsClient;
45
- }
46
- function resolveOutboundChatId(target, accountId) {
47
- const normalizedTarget = target.replace(new RegExp(`^${CHANNEL_ID}:`, "i"), "").trim();
48
- if (/^\d+$/.test(normalizedTarget)) {
49
- return normalizedTarget;
50
- }
51
- const resolvedAccountId = accountId ?? DEFAULT_ACCOUNT_ID;
52
- // 私聊主动发送优先复用运行期学习到的 account -> chatId 映射,
53
- // 这样 message send 可以直接使用账号字符串作为目标。
54
- const rememberedChatId = getDirectChatTarget(resolvedAccountId, normalizedTarget);
55
- if (rememberedChatId) {
56
- return rememberedChatId;
57
- }
58
- throw new Error(`Unknown YouDu direct target: ${normalizedTarget}. Send a message from that account to the bot first so the plugin can learn its numeric chatId.`);
59
- }
60
- /**
61
- * 主动发送有度文本消息。
62
- */
63
- async function sendYouDuTextMessage(params) {
64
- const { to, text, accountId } = params;
65
- const chatId = resolveOutboundChatId(to, accountId);
66
- const wsClient = getOutboundWsClient(accountId);
67
- const ack = await wsClient.sendMessage(chatId, {
68
- msgType: "text",
69
- text: { content: text },
70
- });
71
- return {
72
- channel: CHANNEL_ID,
73
- messageId: ack.headers?.reqId ?? `youdu-${Date.now()}`,
74
- chatId,
75
- };
76
- }
77
- /**
78
- * 主动发送有度媒体消息。
79
- */
80
- async function sendYouDuMediaMessage(params) {
81
- const { cfg, to, mediaUrl, text, mediaLocalRoots, accountId } = params;
82
- const chatId = resolveOutboundChatId(to, accountId);
83
- const resolvedAccountId = accountId ?? DEFAULT_ACCOUNT_ID;
84
- const wsClient = getOutboundWsClient(resolvedAccountId);
85
- const account = resolveYouDuAccount(cfg);
86
- const effectiveMediaLocalRoots = mediaLocalRoots && mediaLocalRoots.length > 0
87
- ? [...mediaLocalRoots]
88
- : await resolveYouDuMediaLocalRoots(account.config);
89
- // 先走 CGI 上传拿到 mediaId,再通过 send_msg 发送正式媒体消息。
90
- const mediaAttachment = await uploadOutboundMedia({
91
- account,
92
- mediaUrl,
93
- mediaLocalRoots: effectiveMediaLocalRoots,
94
- token: wsClient.getToken(),
95
- });
96
- if (text?.trim()) {
97
- await wsClient.sendMessage(chatId, {
98
- msgType: "text",
99
- text: { content: text.trim() },
100
- });
101
- }
102
- const ack = await wsClient.sendMessage(chatId, buildMediaMessageBody(mediaAttachment));
103
- return {
104
- channel: CHANNEL_ID,
105
- messageId: ack.headers?.reqId ?? `youdu-${Date.now()}`,
106
- chatId,
107
- };
108
- }
109
34
 
110
- export { sendYouDuMediaMessage, sendYouDuMediaReply, sendYouDuReply, sendYouDuTextMessage };
35
+ export { sendYouDuMediaReply, sendYouDuReply };
@@ -6,17 +6,17 @@ import { type Youdu } from './media.js';
6
6
  * 消息体类型(待用户完善)
7
7
  */
8
8
  export interface YouDuMessageBody {
9
- msgId: string;
10
- chatType: 'single' | 'group';
11
- chatId: string;
9
+ msgid: string;
10
+ chattype: 'single' | 'group';
11
+ chatid: string;
12
12
  from: {
13
13
  userId: string;
14
14
  account: string;
15
15
  name?: string;
16
16
  };
17
- createTime?: number;
18
- responseUrl?: string;
19
- msgType: string;
17
+ create_time?: number;
18
+ response_url?: string;
19
+ msgtype: string;
20
20
  text?: {
21
21
  content: string;
22
22
  };
@@ -3,12 +3,15 @@ import { getYouDuRuntime } from './runtime.js';
3
3
  import { resolveYouDuAccount } from './utils.js';
4
4
  import { WS_MAX_RECONNECT_ATTEMPTS, WS_HEARTBEAT_INTERVAL_MS, PORT, THINKING_MESSAGE, MESSAGE_PROCESS_TIMEOUT_MS, CHANNEL_ID, MEDIA_IMAGE_PLACEHOLDER, MEDIA_DOCUMENT_PLACEHOLDER } from './const.js';
5
5
  import { buildYouDuTlsConnectOptions } from './tls.js';
6
- import { cleanupAccount, startMessageStateCleanup, setYouDuWebSocket, stopMessageStateCleanup, rememberDirectChatTarget, setMessageState, deleteMessageState } from './state-manager.js';
6
+ import { cleanupAccount, startMessageStateCleanup, setYouDuWebSocket, stopMessageStateCleanup, setMessageState, deleteMessageState } from './state-manager.js';
7
7
  import { checkDmPolicy } from './dm-policy.js';
8
8
  import { checkGroupPolicy } from './group-policy.js';
9
- import { extractInboundMedia, inferInboundMessageText, downloadInboundMedia, uploadAndSendMedia, resolveYouDuMediaLocalRoots } from './media.js';
9
+ import { extractInboundMedia, inferInboundMessageText, downloadInboundMedia, uploadAndSendMedia } from './media.js';
10
10
  import { sendYouDuReply } from './message-sender.js';
11
11
  import { generateReqId } from './sdk/ws-utils.js';
12
+ import path__default from 'path';
13
+ import os from 'os';
14
+ import { getDefaultMediaLocalRoots, resolveStateDir } from './openclaw-compat.js';
12
15
 
13
16
  /*
14
17
  * Licensed under the MIT License.
@@ -20,8 +23,8 @@ function buildMessageContext(frame, account, config, text, mediaList) {
20
23
  const senderAccount = String(body.from.account).trim();
21
24
  const senderName = String(body.from.name ?? "").trim();
22
25
  const senderDisplay = senderName ? `${senderAccount} (${senderName})` : senderAccount;
23
- const chatId = body.chatId || senderAccount;
24
- const chatType = body.chatType === 'group' ? 'group' : 'direct';
26
+ const chatId = body.chatid || senderAccount;
27
+ const chatType = body.chattype === 'group' ? 'group' : 'direct';
25
28
  const route = core.channel.routing.resolveAgentRoute({
26
29
  cfg: config,
27
30
  channel: CHANNEL_ID,
@@ -46,7 +49,7 @@ function buildMessageContext(frame, account, config, text, mediaList) {
46
49
  Body: messageBody,
47
50
  RawBody: messageBody,
48
51
  CommandBody: messageBody,
49
- MessageSid: body.msgId,
52
+ MessageSid: body.msgid,
50
53
  From: chatType === 'group' ? `${CHANNEL_ID}:group:${chatId}` : `${CHANNEL_ID}:${senderId}`,
51
54
  To: `${CHANNEL_ID}:${chatId}`,
52
55
  SenderId: senderId,
@@ -55,14 +58,14 @@ function buildMessageContext(frame, account, config, text, mediaList) {
55
58
  AccountId: account.accountId,
56
59
  ChatType: chatType,
57
60
  ConversationLabel: fromLabel,
58
- Timestamp: body.createTime || Date.now(),
61
+ Timestamp: Date.now(),
59
62
  Provider: CHANNEL_ID,
60
63
  Surface: CHANNEL_ID,
61
64
  OriginatingChannel: CHANNEL_ID,
62
65
  OriginatingTo: `${CHANNEL_ID}:${chatId}`,
63
66
  YouDuFrame: frame,
64
67
  CommandAuthorized: true,
65
- ResponseUrl: body.responseUrl,
68
+ ResponseUrl: body.response_url,
66
69
  ReqId: frame.headers.reqId,
67
70
  MediaPath: mediaList[0]?.path,
68
71
  MediaType: mediaList[0]?.contentType,
@@ -80,14 +83,11 @@ function buildMessageContext(frame, account, config, text, mediaList) {
80
83
  async function processYouDuMessage(params) {
81
84
  const { frame, account, config, runtime, wsClient } = params;
82
85
  const body = frame.body;
83
- const chatId = body.chatId;
86
+ const chatId = body.chatid;
84
87
  const youduAccount = String(body.from.account).trim();
85
- const chatType = body.chatType === 'group' ? 'group' : 'direct';
86
- const messageId = body.msgId;
88
+ const chatType = body.chattype === 'group' ? 'group' : 'direct';
89
+ const messageId = body.msgid;
87
90
  const youduConfig = resolveYouDuAccount(config).config;
88
- if (chatType === "direct" && chatId && youduAccount) {
89
- rememberDirectChatTarget(account.accountId, youduAccount, chatId);
90
- }
91
91
  let text = '';
92
92
  const inboundMedia = extractInboundMedia(body);
93
93
  text = inferInboundMessageText(body);
@@ -335,7 +335,23 @@ function createSdkLogger(runtime, accountId) {
335
335
  };
336
336
  }
337
337
  async function getExtendedMediaLocalRoots(config) {
338
- return resolveYouDuMediaLocalRoots(config);
338
+ // 从兼容层获取默认白名单(内部已处理低版本 SDK 的 fallback)
339
+ const defaults = await getDefaultMediaLocalRoots();
340
+ const roots = [...defaults];
341
+ const stateDir = path__default.resolve(resolveStateDir());
342
+ if (!roots.includes(stateDir)) {
343
+ roots.push(stateDir);
344
+ }
345
+ // 合并用户在 YouDu 中配置的自定义路径
346
+ if (config?.mediaLocalRoots) {
347
+ for (const r of config.mediaLocalRoots) {
348
+ const resolved = path__default.resolve(r.replace(/^~(?=\/|$)/, os.homedir()));
349
+ if (!roots.includes(resolved)) {
350
+ roots.push(resolved);
351
+ }
352
+ }
353
+ }
354
+ return roots;
339
355
  }
340
356
  /**
341
357
  * 监听 YouDu WebSocket 连接(主函数)
@@ -14,7 +14,6 @@ export declare class WSClient extends EventEmitter {
14
14
  connect(): this;
15
15
  disconnect(): void;
16
16
  reply(frame: WsFrame, body: any, cmd?: string): Promise<WsFrame>;
17
- sendMessage(chatId: string, body: any): Promise<WsFrame>;
18
17
  getToken(): string;
19
18
  }
20
19
  export type { Logger, WsFrame, WSClientOptions, WSClientEventMap, } from "./types";
@@ -1,7 +1,7 @@
1
1
  import { EventEmitter } from 'events';
2
2
  import { WsConnectionManager } from './ws-manager.js';
3
- import { DefaultLogger, generateReqId, WsCmd } from './ws-utils.js';
4
- export { WsErrCode } from './ws-utils.js';
3
+ import { DefaultLogger } from './ws-utils.js';
4
+ export { WsCmd, WsErrCode } from './ws-utils.js';
5
5
 
6
6
  /*
7
7
  * Licensed under the MIT License.
@@ -72,13 +72,9 @@ class WSClient extends EventEmitter {
72
72
  const reqId = frame.headers?.reqId || "";
73
73
  return this.wsManager.sendReply(reqId, body, cmd);
74
74
  }
75
- sendMessage(chatId, body) {
76
- const reqId = generateReqId(WsCmd.SEND_MSG);
77
- return this.wsManager.sendReply(reqId, { chatId, ...body }, WsCmd.SEND_MSG);
78
- }
79
75
  getToken() {
80
76
  return this.wsManager.getToken();
81
77
  }
82
78
  }
83
79
 
84
- export { DefaultLogger, WSClient, WsCmd };
80
+ export { DefaultLogger, WSClient };
@@ -106,11 +106,11 @@ class WsConnectionManager extends EventEmitter {
106
106
  cmd: WsCmd.CALLBACK,
107
107
  headers: { reqId: generateReqId("raw_text") },
108
108
  body: {
109
- msgId: `msg_${Date.now()}`,
110
- chatId: "default_chat",
111
- chatType: "single",
109
+ msgid: `msg_${Date.now()}`,
110
+ chatid: "default_chat",
111
+ chattype: "single",
112
112
  from: { userid: "default_user" },
113
- msgType: "text",
113
+ msgtype: "text",
114
114
  text: { content: raw },
115
115
  },
116
116
  };
@@ -22,14 +22,6 @@ export declare function setYouDuWebSocket(accountId: string, wsClient: WSClient)
22
22
  * 获取账户的 WebSocket 客户端
23
23
  */
24
24
  export declare function getYouDuWebSocket(accountId: string): WSClient | null;
25
- /**
26
- * 记录私聊账号对应的数值 chatId。
27
- */
28
- export declare function rememberDirectChatTarget(accountId: string, account: string, chatId: string): void;
29
- /**
30
- * 读取私聊账号对应的数值 chatId。
31
- */
32
- export declare function getDirectChatTarget(accountId: string, account: string): string | undefined;
33
25
  /**
34
26
  * 设置消息状态
35
27
  */
@@ -9,8 +9,6 @@ const wsClients = new Map();
9
9
  const messageStates = new Map();
10
10
  /** chatId 到 reqId 的映射 */
11
11
  const reqIdStore = new Map();
12
- /** 私聊账号到数值 chatId 的运行时映射 */
13
- const directChatStore = new Map();
14
12
  /** 清理定时器 */
15
13
  let cleanupTimer = null;
16
14
  /**
@@ -23,33 +21,6 @@ function setYouDuWebSocket(accountId, wsClient) {
23
21
  }
24
22
  wsClients.set(accountId, wsClient);
25
23
  }
26
- /**
27
- * 获取账户的 WebSocket 客户端
28
- */
29
- function getYouDuWebSocket(accountId) {
30
- return wsClients.get(accountId) ?? null;
31
- }
32
- /**
33
- * 记录私聊账号对应的数值 chatId。
34
- */
35
- function rememberDirectChatTarget(accountId, account, chatId) {
36
- const normalizedAccount = String(account).trim().toLowerCase();
37
- const normalizedChatId = String(chatId).trim();
38
- if (!normalizedAccount || !normalizedChatId) {
39
- return;
40
- }
41
- directChatStore.set(`${accountId}:${normalizedAccount}`, normalizedChatId);
42
- }
43
- /**
44
- * 读取私聊账号对应的数值 chatId。
45
- */
46
- function getDirectChatTarget(accountId, account) {
47
- const normalizedAccount = String(account).trim().toLowerCase();
48
- if (!normalizedAccount) {
49
- return undefined;
50
- }
51
- return directChatStore.get(`${accountId}:${normalizedAccount}`);
52
- }
53
24
  /**
54
25
  * 设置消息状态
55
26
  */
@@ -107,11 +78,6 @@ async function cleanupAccount(accountId, expectedClient) {
107
78
  reqIdStore.delete(key);
108
79
  }
109
80
  }
110
- for (const key of directChatStore.keys()) {
111
- if (key.startsWith(`${accountId}:`)) {
112
- directChatStore.delete(key);
113
- }
114
- }
115
81
  }
116
82
 
117
- export { cleanupAccount, deleteMessageState, getDirectChatTarget, getYouDuWebSocket, rememberDirectChatTarget, setMessageState, setYouDuWebSocket, startMessageStateCleanup, stopMessageStateCleanup };
83
+ export { cleanupAccount, deleteMessageState, setMessageState, setYouDuWebSocket, startMessageStateCleanup, stopMessageStateCleanup };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@youdu/openclaw-youdu",
3
- "version": "2026.5.8-2",
3
+ "version": "2026.5.8-3",
4
4
  "type": "module",
5
5
  "main": "dist/index.esm.js",
6
6
  "module": "dist/index.esm.js",