@wecom/wecom-openclaw-plugin 1.0.9-beta.1 → 1.0.10-beta.0

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.
@@ -52,3 +52,15 @@ export declare const PLUGIN_JSON_PATH: string;
52
52
  export declare const DEFAULT_MEDIA_MAX_MB = 5;
53
53
  /** 文本分块大小上限 */
54
54
  export declare const TEXT_CHUNK_LIMIT = 4000;
55
+ /** 图片大小上限(字节):10MB */
56
+ export declare const IMAGE_MAX_BYTES: number;
57
+ /** 视频大小上限(字节):10MB */
58
+ export declare const VIDEO_MAX_BYTES: number;
59
+ /** 语音大小上限(字节):2MB */
60
+ export declare const VOICE_MAX_BYTES: number;
61
+ /** 文件大小上限(字节):20MB */
62
+ export declare const FILE_MAX_BYTES: number;
63
+ /** 文件绝对上限(字节):超过此值无法发送,等于 FILE_MAX_BYTES */
64
+ export declare const ABSOLUTE_MAX_BYTES: number;
65
+ /** 上传分片大小(字节,Base64 编码前):512KB */
66
+ export declare const UPLOAD_CHUNK_SIZE: number;
@@ -20,6 +20,16 @@ export interface MessageState {
20
20
  accumulatedText: string;
21
21
  /** 流式回复的 streamId,用于保持同一个流式回复使用相同的 streamId */
22
22
  streamId?: string;
23
+ /** 是否有用户可见的文本内容(不包括 <think>...</think> 标签) */
24
+ hasText?: boolean;
25
+ /** 是否已成功发送过媒体文件 */
26
+ hasMedia?: boolean;
27
+ /** 是否有媒体发送失败(权限不足、文件过大等) */
28
+ hasMediaFailed?: boolean;
29
+ /** 媒体发送失败时的纯文本错误摘要(用于替换 thinking 流展示给用户) */
30
+ mediaErrorSummary?: string;
31
+ /** deliver 回调是否被调用过(用于区分"核心无回复"和"核心回复了空内容") */
32
+ deliverCalled?: boolean;
23
33
  }
24
34
  /**
25
35
  * MCP 配置响应体
@@ -0,0 +1,131 @@
1
+ /**
2
+ * 企业微信出站媒体上传工具模块
3
+ *
4
+ * 负责:
5
+ * - 从 mediaUrl 加载文件 buffer(远程 URL 或本地路径均支持)
6
+ * - 检测 MIME 类型并映射为企微媒体类型
7
+ * - 文件大小检查与降级策略
8
+ */
9
+ import type { WeComMediaType, WSClient, WsFrameHeaders } from "@wecom/aibot-node-sdk";
10
+ /** 媒体文件解析结果 */
11
+ export interface ResolvedMedia {
12
+ /** 文件数据 */
13
+ buffer: Buffer;
14
+ /** 检测到的 MIME 类型 */
15
+ contentType: string;
16
+ /** 文件名(从 URL 提取或默认生成) */
17
+ fileName: string;
18
+ }
19
+ /** 文件大小检查结果 */
20
+ export interface FileSizeCheckResult {
21
+ /** 最终确定的企微媒体类型(可能被降级) */
22
+ finalType: WeComMediaType;
23
+ /** 是否需要拒绝(超过绝对限制) */
24
+ shouldReject: boolean;
25
+ /** 拒绝原因(仅 shouldReject=true 时有值) */
26
+ rejectReason?: string;
27
+ /** 是否发生了降级 */
28
+ downgraded: boolean;
29
+ /** 降级说明(仅 downgraded=true 时有值) */
30
+ downgradeNote?: string;
31
+ }
32
+ /**
33
+ * 根据 MIME 类型检测企微媒体类型
34
+ *
35
+ * @param mimeType - MIME 类型字符串
36
+ * @returns 企微媒体类型
37
+ */
38
+ export declare function detectWeComMediaType(mimeType: string): WeComMediaType;
39
+ /**
40
+ * 从 mediaUrl 加载媒体文件
41
+ *
42
+ * 支持远程 URL(http/https)和本地路径(file:// 或绝对路径),
43
+ * 利用 openclaw plugin-sdk 的 loadOutboundMediaFromUrl 统一处理。
44
+ *
45
+ * @param mediaUrl - 媒体文件的 URL 或本地路径
46
+ * @param mediaLocalRoots - 允许读取本地文件的安全白名单目录
47
+ * @returns 解析后的媒体文件信息
48
+ */
49
+ export declare function resolveMediaFile(mediaUrl: string, mediaLocalRoots?: readonly string[]): Promise<ResolvedMedia>;
50
+ /**
51
+ * 检查文件大小并执行降级策略
52
+ *
53
+ * 降级规则:
54
+ * - voice 非 AMR 格式 → 降级为 file(企微后台仅支持 AMR)
55
+ * - image 超过 10MB → 降级为 file
56
+ * - video 超过 10MB → 降级为 file
57
+ * - voice 超过 2MB → 降级为 file
58
+ * - file 超过 20MB → 拒绝发送
59
+ *
60
+ * @param fileSize - 文件大小(字节)
61
+ * @param detectedType - 检测到的企微媒体类型
62
+ * @param contentType - 文件的 MIME 类型(用于语音格式校验)
63
+ * @returns 大小检查结果
64
+ */
65
+ export declare function applyFileSizeLimits(fileSize: number, detectedType: WeComMediaType, contentType?: string): FileSizeCheckResult;
66
+ /** uploadAndSendMedia 的参数 */
67
+ export interface UploadAndSendMediaOptions {
68
+ /** WSClient 实例 */
69
+ wsClient: WSClient;
70
+ /** 媒体文件的 URL 或本地路径 */
71
+ mediaUrl: string;
72
+ /** 目标会话 ID(用于 aibot_send_msg 主动发送) */
73
+ chatId: string;
74
+ /** 允许读取本地文件的安全白名单目录 */
75
+ mediaLocalRoots?: readonly string[];
76
+ /** 日志函数 */
77
+ log?: (...args: any[]) => void;
78
+ /** 错误日志函数 */
79
+ errorLog?: (...args: any[]) => void;
80
+ }
81
+ /** uploadAndSendMedia 的返回结果 */
82
+ export interface UploadAndSendMediaResult {
83
+ /** 是否发送成功 */
84
+ ok: boolean;
85
+ /** 发送后返回的 messageId */
86
+ messageId?: string;
87
+ /** 最终的企微媒体类型 */
88
+ finalType?: WeComMediaType;
89
+ /** 是否被拒绝(文件过大) */
90
+ rejected?: boolean;
91
+ /** 拒绝原因 */
92
+ rejectReason?: string;
93
+ /** 是否发生了降级 */
94
+ downgraded?: boolean;
95
+ /** 降级说明 */
96
+ downgradeNote?: string;
97
+ /** 错误信息 */
98
+ error?: string;
99
+ }
100
+ /**
101
+ * 公共媒体上传+发送流程
102
+ *
103
+ * 统一处理:resolveMediaFile → detectType → sizeCheck → uploadMedia → sendMediaMessage
104
+ * 媒体消息统一走 aibot_send_msg 主动发送,避免多文件场景下 reqId 只能用一次的问题。
105
+ * channel.ts 的 sendMedia 和 monitor.ts 的 deliver 回调都使用此函数。
106
+ */
107
+ export declare function uploadAndSendMedia(options: UploadAndSendMediaOptions): Promise<UploadAndSendMediaResult>;
108
+ /** uploadAndReplyMedia 的参数 */
109
+ export interface UploadAndReplyMediaOptions {
110
+ /** WSClient 实例 */
111
+ wsClient: WSClient;
112
+ /** 媒体文件的 URL 或本地路径 */
113
+ mediaUrl: string;
114
+ /** 原始 WebSocket 帧(用于 aibot_respond_msg 被动回复,携带 req_id) */
115
+ frame: WsFrameHeaders;
116
+ /** 允许读取本地文件的安全白名单目录 */
117
+ mediaLocalRoots?: readonly string[];
118
+ /** 日志函数 */
119
+ log?: (...args: any[]) => void;
120
+ /** 错误日志函数 */
121
+ errorLog?: (...args: any[]) => void;
122
+ }
123
+ /**
124
+ * 被动回复媒体上传+发送流程
125
+ *
126
+ * 统一处理:resolveMediaFile → detectType → sizeCheck → uploadMedia → replyMedia
127
+ * 通过 aibot_respond_msg 被动回复通道发送媒体消息,可以覆盖之前的 THINKING_MESSAGE。
128
+ *
129
+ * 适用场景:回包只有媒体没有文本时,第一个媒体文件用此方法发送以清理 thinking 状态。
130
+ */
131
+ export declare function uploadAndReplyMedia(options: UploadAndReplyMediaOptions): Promise<UploadAndSendMediaResult>;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * openclaw plugin-sdk 高版本方法兼容层
3
+ *
4
+ * 部分方法(如 loadOutboundMediaFromUrl、detectMime、getDefaultMediaLocalRoots)
5
+ * 仅在较新版本的 openclaw plugin-sdk 中才导出。
6
+ *
7
+ * 本模块在加载时一次性探测 SDK 导出,存在则直接 re-export SDK 版本,
8
+ * 不存在则导出 fallback 实现。其他模块统一从本文件导入,无需关心底层兼容细节。
9
+ */
10
+ /** 与 openclaw plugin-sdk 中 WebMediaResult 兼容的类型 */
11
+ export type WebMediaResult = {
12
+ buffer: Buffer;
13
+ contentType?: string;
14
+ kind?: string;
15
+ fileName?: string;
16
+ };
17
+ export type OutboundMediaLoadOptions = {
18
+ maxBytes?: number;
19
+ mediaLocalRoots?: readonly string[];
20
+ };
21
+ export type DetectMimeOptions = {
22
+ buffer?: Buffer;
23
+ headerMime?: string | null;
24
+ filePath?: string;
25
+ };
26
+ /**
27
+ * 检测 MIME 类型(兼容入口)
28
+ *
29
+ * 支持两种调用签名以兼容不同使用场景:
30
+ * - detectMime(buffer) → 旧式调用
31
+ * - detectMime({ buffer, headerMime, filePath }) → 完整参数
32
+ *
33
+ * 优先使用 SDK 版本,不可用时使用 fallback。
34
+ */
35
+ export declare function detectMime(bufferOrOpts: Buffer | DetectMimeOptions): Promise<string | undefined>;
36
+ /**
37
+ * 从 URL 或本地路径加载媒体文件(兼容入口)
38
+ *
39
+ * 优先使用 SDK 版本,不可用时使用 fallback。
40
+ * SDK 版本抛出的业务异常(如 LocalMediaAccessError)会直接透传。
41
+ */
42
+ export declare function loadOutboundMediaFromUrl(mediaUrl: string, options?: OutboundMediaLoadOptions): Promise<WebMediaResult>;
43
+ /**
44
+ * 获取默认媒体本地路径白名单(兼容入口)
45
+ *
46
+ * 优先使用 SDK 版本,不可用时手动构建白名单(与 weclaw/src/media/local-roots.ts 逻辑一致)。
47
+ */
48
+ export declare function getDefaultMediaLocalRoots(): Promise<readonly string[]>;
@@ -28,6 +28,8 @@ export interface WeComConfig {
28
28
  groups?: Record<string, WeComGroupConfig>;
29
29
  /** 是否发送"思考中"消息,默认为 true */
30
30
  sendThinkingMessage?: boolean;
31
+ /** 额外允许访问的本地媒体路径白名单(支持 ~ 表示 home 目录),如 ["~/Downloads", "~/Documents"] */
32
+ mediaLocalRoots?: string[];
31
33
  }
32
34
  export declare const DefaultWsUrl = "wss://openws.work.weixin.qq.com";
33
35
  export interface ResolvedWeComAccount {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wecom/wecom-openclaw-plugin",
3
- "version": "1.0.9-beta.1",
3
+ "version": "1.0.10-beta.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.esm.js",
@@ -56,7 +56,7 @@
56
56
  }
57
57
  },
58
58
  "dependencies": {
59
- "@wecom/aibot-node-sdk": "^1.0.1",
59
+ "@wecom/aibot-node-sdk": "^1.0.2",
60
60
  "file-type": "^21.3.0"
61
61
  }
62
62
  }