@xgjktech/xg_cwork_im 1.16.3 → 1.16.5
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.
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/src/block-streaming-config.test.js +7 -1
- package/dist/src/block-streaming-config.test.js.map +1 -1
- package/dist/src/channel-config.d.ts +7 -0
- package/dist/src/channel-config.d.ts.map +1 -1
- package/dist/src/channel-config.js +5 -0
- package/dist/src/channel-config.js.map +1 -1
- package/dist/src/channel.js.map +1 -1
- package/dist/src/dispatch-mentioned-reply.d.ts.map +1 -1
- package/dist/src/dispatch-mentioned-reply.js +127 -9
- package/dist/src/dispatch-mentioned-reply.js.map +1 -1
- package/dist/src/recommended-system-prompt.d.ts +1 -1
- package/dist/src/recommended-system-prompt.d.ts.map +1 -1
- package/dist/src/recommended-system-prompt.js +6 -1
- package/dist/src/recommended-system-prompt.js.map +1 -1
- package/dist/src/resource-file.d.ts +26 -0
- package/dist/src/resource-file.d.ts.map +1 -1
- package/dist/src/resource-file.js +59 -5
- package/dist/src/resource-file.js.map +1 -1
- package/dist/src/send-file-tool.d.ts +81 -0
- package/dist/src/send-file-tool.d.ts.map +1 -0
- package/dist/src/send-file-tool.js +354 -0
- package/dist/src/send-file-tool.js.map +1 -0
- package/dist/src/send-file-tool.test.d.ts +2 -0
- package/dist/src/send-file-tool.test.d.ts.map +1 -0
- package/dist/src/send-file-tool.test.js +176 -0
- package/dist/src/send-file-tool.test.js.map +1 -0
- package/dist/src/send-group-message-tool.js +1 -1
- package/dist/src/send-group-message-tool.js.map +1 -1
- package/dist/src/send-service.d.ts +43 -4
- package/dist/src/send-service.d.ts.map +1 -1
- package/dist/src/send-service.js +62 -38
- package/dist/src/send-service.js.map +1 -1
- package/dist/src/send-service.test.d.ts +2 -0
- package/dist/src/send-service.test.d.ts.map +1 -0
- package/dist/src/send-service.test.js +40 -0
- package/dist/src/send-service.test.js.map +1 -0
- package/dist/src/subagent-progress-hooks.d.ts +11 -0
- package/dist/src/subagent-progress-hooks.d.ts.map +1 -0
- package/dist/src/subagent-progress-hooks.js +41 -0
- package/dist/src/subagent-progress-hooks.js.map +1 -0
- package/dist/src/turn-deliver-buffer.d.ts +5 -0
- package/dist/src/turn-deliver-buffer.d.ts.map +1 -1
- package/dist/src/turn-deliver-buffer.js +13 -0
- package/dist/src/turn-deliver-buffer.js.map +1 -1
- package/dist/src/turn-deliver-buffer.test.js +20 -1
- package/dist/src/turn-deliver-buffer.test.js.map +1 -1
- package/dist/src/types.d.ts +14 -2
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js.map +1 -1
- package/openclaw.plugin.json +2 -1
- package/package.json +2 -2
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XG-IM 文件发送工具(`xg_cwork_im_send_file`)
|
|
3
|
+
*
|
|
4
|
+
* 直接发送当前 Agent workspace 内的文件到**当前 IM 会话**:校验 workspace 路径与文件 → 上传资源中心 →
|
|
5
|
+
* 发送 FILE 消息。避免 HTML/SVG 等经 `MEDIA:` + OpenClaw 本地媒体安全规则被拦截。
|
|
6
|
+
*
|
|
7
|
+
* 与 `xg_cwork_im_send_group_message`(仅纯文本、需模型给 groupId)不同:
|
|
8
|
+
* - groupId / accountId 从注入的会话上下文(deliveryContext)获取,模型不可指定;
|
|
9
|
+
* - 上传或发送失败一律返回 `ok:false`,绝不降级成文本后谎报成功。
|
|
10
|
+
*/
|
|
11
|
+
import { Type } from "@sinclair/typebox";
|
|
12
|
+
import { readFile, realpath, stat } from "node:fs/promises";
|
|
13
|
+
import path from "node:path";
|
|
14
|
+
import { getToken } from "./auth.js";
|
|
15
|
+
import { resolveAccountConfig } from "./group-history-tool.js";
|
|
16
|
+
import { formatFromFilename, resolveMaxAttachmentBytes, resolveResourceUploadUrl, uploadWholeResourceFileWithRetry, } from "./resource-file.js";
|
|
17
|
+
import { generateClientMsgId, sendUploadedFileMessage } from "./send-service.js";
|
|
18
|
+
import { toolJsonResult } from "./tool-json-result.js";
|
|
19
|
+
// ─── 工具参数 Schema ─────────────────────────────────────────────────────────
|
|
20
|
+
const SendFileParams = Type.Object({
|
|
21
|
+
path: Type.String({
|
|
22
|
+
minLength: 1,
|
|
23
|
+
description: "当前 Agent workspace 内的文件相对路径,例如 report.html 或 output/report.pdf;" +
|
|
24
|
+
"可选前缀 ./ 或 /workspace/。禁止绝对路径、URL、UNC、workspace 外或 .. 越界路径。",
|
|
25
|
+
}),
|
|
26
|
+
caption: Type.Optional(Type.String({
|
|
27
|
+
description: "附件说明文字(可选)",
|
|
28
|
+
maxLength: 2000,
|
|
29
|
+
})),
|
|
30
|
+
fileName: Type.Optional(Type.String({
|
|
31
|
+
description: "用户看到的文件名(可选);省略时使用源文件名。扩展名以源文件为准,不可修改。",
|
|
32
|
+
})),
|
|
33
|
+
atUserIds: Type.Optional(Type.Array(Type.String(), { maxItems: 50 })),
|
|
34
|
+
});
|
|
35
|
+
export class SendFileError extends Error {
|
|
36
|
+
code;
|
|
37
|
+
constructor(code, message) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.name = "SendFileError";
|
|
40
|
+
this.code = code;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** 默认拒绝的可执行 / 脚本扩展名(去掉点、小写) */
|
|
44
|
+
const BLOCKED_EXTENSIONS = new Set(["exe", "dll", "bat", "cmd", "msi", "ps1", "sh"]);
|
|
45
|
+
// ─── 路径安全解析 ─────────────────────────────────────────────────────────────
|
|
46
|
+
function isUrlLike(ref) {
|
|
47
|
+
return /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(ref) || ref.startsWith("file:");
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 将模型传入的 path 解析为 workspace 内的宿主机绝对路径。
|
|
51
|
+
*
|
|
52
|
+
* 校验顺序与 docs/send-file-tool-design.md §3 一致:
|
|
53
|
+
* 去前缀 → 拒绝绝对/URL/UNC/.. → resolve 后仍在 workspace → realpath 防符号链接逃逸 →
|
|
54
|
+
* isFile → 存在/非空/不超限 → 拒绝可执行扩展名。
|
|
55
|
+
*
|
|
56
|
+
* @returns 解析后的真实路径、字节数与源文件名。
|
|
57
|
+
*/
|
|
58
|
+
export async function resolveWorkspaceFile(workspaceDir, inputPath, maxBytes) {
|
|
59
|
+
const raw = inputPath.trim();
|
|
60
|
+
const posix = raw.replace(/\\/g, "/");
|
|
61
|
+
if (isUrlLike(raw)) {
|
|
62
|
+
throw new SendFileError("PATH_OUTSIDE_WORKSPACE", "不支持 URL 或 file:// 路径,只能发送 workspace 内文件。");
|
|
63
|
+
}
|
|
64
|
+
// Windows 盘符绝对路径 / UNC
|
|
65
|
+
if (/^[a-zA-Z]:[/\\]/.test(raw) || posix.startsWith("//")) {
|
|
66
|
+
throw new SendFileError("PATH_OUTSIDE_WORKSPACE", "不支持宿主机绝对路径或 UNC 路径。");
|
|
67
|
+
}
|
|
68
|
+
// 去掉可选前缀:/workspace/、./
|
|
69
|
+
let rel;
|
|
70
|
+
if (posix === "/workspace" || posix === "/workspace/") {
|
|
71
|
+
rel = "";
|
|
72
|
+
}
|
|
73
|
+
else if (posix.startsWith("/workspace/")) {
|
|
74
|
+
rel = posix.slice("/workspace/".length);
|
|
75
|
+
}
|
|
76
|
+
else if (posix.startsWith("./")) {
|
|
77
|
+
rel = posix.slice(2);
|
|
78
|
+
}
|
|
79
|
+
else if (posix.startsWith("/")) {
|
|
80
|
+
// 其它绝对路径(如 /tmp/openclaw/...)一律拒绝
|
|
81
|
+
throw new SendFileError("PATH_OUTSIDE_WORKSPACE", "不支持 workspace 外的绝对路径。");
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
rel = posix;
|
|
85
|
+
}
|
|
86
|
+
rel = rel.replace(/^\/+/, "").trim();
|
|
87
|
+
if (!rel || rel === ".") {
|
|
88
|
+
throw new SendFileError("PATH_IS_DIRECTORY", "指定路径是 workspace 根目录,不是可发送文件。");
|
|
89
|
+
}
|
|
90
|
+
if (rel.split("/").some((seg) => seg === "..")) {
|
|
91
|
+
throw new SendFileError("PATH_OUTSIDE_WORKSPACE", "路径包含 .. ,越出 workspace。");
|
|
92
|
+
}
|
|
93
|
+
const absolutePath = path.resolve(workspaceDir, rel);
|
|
94
|
+
const relToRoot = path.relative(workspaceDir, absolutePath);
|
|
95
|
+
if (relToRoot === ".." || relToRoot.startsWith(`..${path.sep}`) || path.isAbsolute(relToRoot)) {
|
|
96
|
+
throw new SendFileError("PATH_OUTSIDE_WORKSPACE", "解析后的路径不在当前 Agent workspace 内。");
|
|
97
|
+
}
|
|
98
|
+
// realpath 防止符号链接逃逸;workspace 自身也 realpath 以对齐比较基准。
|
|
99
|
+
let realWorkspace;
|
|
100
|
+
try {
|
|
101
|
+
realWorkspace = await realpath(workspaceDir);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
realWorkspace = path.resolve(workspaceDir);
|
|
105
|
+
}
|
|
106
|
+
let realPath;
|
|
107
|
+
try {
|
|
108
|
+
realPath = await realpath(absolutePath);
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
if (err?.code === "ENOENT") {
|
|
112
|
+
throw new SendFileError("FILE_NOT_FOUND", "文件不存在。请先在 workspace 内创建该文件。");
|
|
113
|
+
}
|
|
114
|
+
throw new SendFileError("READ_FAILED", `无法解析文件路径:${String(err)}`);
|
|
115
|
+
}
|
|
116
|
+
const realRel = path.relative(realWorkspace, realPath);
|
|
117
|
+
if (realRel === ".." || realRel.startsWith(`..${path.sep}`) || path.isAbsolute(realRel)) {
|
|
118
|
+
throw new SendFileError("SYMLINK_ESCAPE", "符号链接指向 workspace 之外,已拒绝。");
|
|
119
|
+
}
|
|
120
|
+
const st = await stat(realPath);
|
|
121
|
+
if (st.isDirectory()) {
|
|
122
|
+
throw new SendFileError("PATH_IS_DIRECTORY", "指定路径是目录,不是可发送文件。");
|
|
123
|
+
}
|
|
124
|
+
if (!st.isFile()) {
|
|
125
|
+
throw new SendFileError("FILE_NOT_FOUND", "指定路径不是普通文件。");
|
|
126
|
+
}
|
|
127
|
+
if (st.size <= 0) {
|
|
128
|
+
throw new SendFileError("EMPTY_FILE", "文件为空,无法发送。");
|
|
129
|
+
}
|
|
130
|
+
if (st.size > maxBytes) {
|
|
131
|
+
throw new SendFileError("FILE_TOO_LARGE", `文件大小 ${st.size} 字节超过上限 ${maxBytes} 字节。`);
|
|
132
|
+
}
|
|
133
|
+
const sourceName = path.basename(realPath) || "file";
|
|
134
|
+
const ext = path.extname(sourceName).replace(/^\./, "").toLowerCase();
|
|
135
|
+
if (ext && BLOCKED_EXTENSIONS.has(ext)) {
|
|
136
|
+
throw new SendFileError("EXTENSION_NOT_ALLOWED", `默认禁止发送 .${ext} 可执行/脚本文件。`);
|
|
137
|
+
}
|
|
138
|
+
return { realPath, size: st.size, sourceName };
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* 依据可选的 fileName 生成用户可见文件名:
|
|
142
|
+
* 去掉路径部分与非法字符,并强制沿用源文件扩展名(不允许模型改扩展名)。
|
|
143
|
+
*/
|
|
144
|
+
export function resolveDisplayFileName(sourceName, fileNameParam) {
|
|
145
|
+
if (!fileNameParam || !fileNameParam.trim())
|
|
146
|
+
return sourceName;
|
|
147
|
+
const sourceExt = path.extname(sourceName); // 含点,可能为空
|
|
148
|
+
const base = path.basename(fileNameParam.replace(/\\/g, "/"));
|
|
149
|
+
// 去掉调用方自带扩展名,统一使用源扩展名
|
|
150
|
+
const baseNoExt = sourceExt && base.toLowerCase().endsWith(sourceExt.toLowerCase())
|
|
151
|
+
? base.slice(0, base.length - sourceExt.length)
|
|
152
|
+
: base.replace(/\.[^.]*$/, "");
|
|
153
|
+
// 过滤控制字符与文件系统非法字符
|
|
154
|
+
const cleaned = baseNoExt
|
|
155
|
+
.replace(/[\x00-\x1f\x7f/\\:*?"<>|]/g, "")
|
|
156
|
+
.replace(/\s+/g, " ")
|
|
157
|
+
.trim();
|
|
158
|
+
if (!cleaned)
|
|
159
|
+
return sourceName;
|
|
160
|
+
return `${cleaned}${sourceExt}`;
|
|
161
|
+
}
|
|
162
|
+
// ─── 账户严格解析 ─────────────────────────────────────────────────────────────
|
|
163
|
+
/**
|
|
164
|
+
* 从两个可信来源(deliveryContext.accountId、agentAccountId)确定账户 ID:
|
|
165
|
+
* 两者都存在且不一致时视为上下文冲突,拒绝执行(避免用错机器人)。
|
|
166
|
+
*/
|
|
167
|
+
export function selectTrustedAccountId(deliveryAccountId, channelAccountId) {
|
|
168
|
+
const a = deliveryAccountId?.trim() || undefined;
|
|
169
|
+
const b = channelAccountId?.trim() || undefined;
|
|
170
|
+
if (a && b && a !== b)
|
|
171
|
+
return { conflict: { a, b } };
|
|
172
|
+
return { accountId: a ?? b };
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* 严格解析账户配置:
|
|
176
|
+
* - 单账户模式(无 accounts):直接返回顶层配置。
|
|
177
|
+
* - 多账户模式:指定账户必须存在;未指定账户时仅当只有一个非 default 账户才可自动选定,
|
|
178
|
+
* 否则视为无法确定账户而拒绝。**不会**像 {@link resolveAccountConfig} 那样静默回退到第一个账户。
|
|
179
|
+
*/
|
|
180
|
+
export function resolveAccountConfigStrict(base, accountId) {
|
|
181
|
+
const accounts = base.accounts;
|
|
182
|
+
if (!accounts || typeof accounts !== "object") {
|
|
183
|
+
return { config: base, accountId };
|
|
184
|
+
}
|
|
185
|
+
const map = accounts;
|
|
186
|
+
const nonDefaultKeys = Object.keys(map).filter((k) => k !== "default" && map[k]);
|
|
187
|
+
if (accountId && accountId !== "default") {
|
|
188
|
+
if (!map[accountId]) {
|
|
189
|
+
return {
|
|
190
|
+
error: "ACCOUNT_NOT_FOUND",
|
|
191
|
+
message: `指定账户 ${accountId} 不存在,已拒绝以免用错机器人发送。`,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
return { config: resolveAccountConfig(base, accountId), accountId };
|
|
195
|
+
}
|
|
196
|
+
// 未提供明确账户:只有唯一非 default 账户时才可自动选定。
|
|
197
|
+
if (nonDefaultKeys.length === 1) {
|
|
198
|
+
return { config: resolveAccountConfig(base, nonDefaultKeys[0]), accountId: nonDefaultKeys[0] };
|
|
199
|
+
}
|
|
200
|
+
if (nonDefaultKeys.length === 0) {
|
|
201
|
+
// 仅有 default:交给 resolveAccountConfig 合并 default 到顶层。
|
|
202
|
+
return { config: resolveAccountConfig(base, undefined), accountId };
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
error: "ACCOUNT_NOT_FOUND",
|
|
206
|
+
message: "存在多个账户但当前会话未提供明确账户,无法安全选择,已拒绝。",
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
// ─── 工具构建函数 ─────────────────────────────────────────────────────────────
|
|
210
|
+
export function buildSendFileTool(config, hints) {
|
|
211
|
+
return {
|
|
212
|
+
name: "xg_cwork_im_send_file",
|
|
213
|
+
label: "【xg_cwork_im】发送 workspace 文件到当前会话",
|
|
214
|
+
description: [
|
|
215
|
+
"【用途】把**当前 Agent workspace 内的文件**发送到**当前 IM 会话**(生成/返回/发送报告、HTML、PDF 等文件时使用)。",
|
|
216
|
+
"【为何用它】HTML、SVG 等经 MEDIA 会被 OpenClaw 本地媒体安全规则拦截;本工具直接上传资源中心并发 FILE 消息,用户可直接下载。",
|
|
217
|
+
"【流程】先在 workspace 内创建具体文件 → 用相对路径调用本工具(path 支持 ./ 或 /workspace/ 前缀)。",
|
|
218
|
+
"【约束】groupId / 账户由会话上下文自动决定,不用也不能传;只能发 workspace 内文件,禁止 URL、宿主机绝对路径、workspace 外或 .. 路径。",
|
|
219
|
+
"【判成功】仅当返回 ok:true 才能告知用户已发送;ok:false 时按 code 修正文件或路径,不得谎报成功。",
|
|
220
|
+
].join("\n"),
|
|
221
|
+
parameters: SendFileParams,
|
|
222
|
+
async execute(_toolCallId, params) {
|
|
223
|
+
const log = makeConsoleLog();
|
|
224
|
+
const delivery = hints?.deliveryContext ?? null;
|
|
225
|
+
const channel = delivery?.channel ?? undefined;
|
|
226
|
+
const groupId = (delivery?.to ?? "").trim();
|
|
227
|
+
const workspaceDir = hints?.workspaceDir?.trim();
|
|
228
|
+
const fail = (code, error) => {
|
|
229
|
+
console.warn(`[cwork_im][tool] xg_cwork_im_send_file rejected code=${code}: ${error}`);
|
|
230
|
+
return toolJsonResult({ ok: false, code, error });
|
|
231
|
+
};
|
|
232
|
+
// 该工具限定「当前 xg_cwork_im 会话」:channel 必须严格等于 xg_cwork_im(缺失也拒绝)。
|
|
233
|
+
if (channel !== "xg_cwork_im") {
|
|
234
|
+
return fail("NO_XG_IM_CONTEXT", "当前会话不是 xg_cwork_im 通道(或缺少通道标识),无法使用本工具。");
|
|
235
|
+
}
|
|
236
|
+
if (!groupId) {
|
|
237
|
+
return fail("NO_DELIVERY_TARGET", "缺少当前 IM 会话目标(groupId),无法发送。");
|
|
238
|
+
}
|
|
239
|
+
if (!workspaceDir) {
|
|
240
|
+
return fail("NO_WORKSPACE", "缺少当前 Agent workspace,无法解析文件路径。");
|
|
241
|
+
}
|
|
242
|
+
// 账户:两个可信来源冲突时拒绝;随后严格解析(不存在/无法确定时拒绝,绝不回退到第一个账户)。
|
|
243
|
+
const selected = selectTrustedAccountId(delivery?.accountId, hints?.channelAccountId);
|
|
244
|
+
if ("conflict" in selected) {
|
|
245
|
+
return fail("ACCOUNT_MISMATCH", `会话上下文账户不一致(${selected.conflict.a} ≠ ${selected.conflict.b}),已拒绝以免用错机器人。`);
|
|
246
|
+
}
|
|
247
|
+
const resolvedAccount = resolveAccountConfigStrict(config, selected.accountId);
|
|
248
|
+
if ("error" in resolvedAccount) {
|
|
249
|
+
return fail(resolvedAccount.error, resolvedAccount.message);
|
|
250
|
+
}
|
|
251
|
+
const accountConfig = resolvedAccount.config;
|
|
252
|
+
const effectiveAccountId = resolvedAccount.accountId;
|
|
253
|
+
// 账户级 maxAttachmentBytes 优先于顶层,避免账户级限制被绕过。
|
|
254
|
+
const maxBytes = resolveMaxAttachmentBytes(accountConfig);
|
|
255
|
+
// 1) 路径与文件安全校验
|
|
256
|
+
let resolved;
|
|
257
|
+
try {
|
|
258
|
+
resolved = await resolveWorkspaceFile(workspaceDir, params.path, maxBytes);
|
|
259
|
+
}
|
|
260
|
+
catch (err) {
|
|
261
|
+
if (err instanceof SendFileError)
|
|
262
|
+
return fail(err.code, err.message);
|
|
263
|
+
return fail("READ_FAILED", `路径校验失败:${String(err)}`);
|
|
264
|
+
}
|
|
265
|
+
const displayName = resolveDisplayFileName(resolved.sourceName, params.fileName);
|
|
266
|
+
const displayRel = path.relative(workspaceDir, resolved.realPath).split(path.sep).join("/");
|
|
267
|
+
console.log(`[cwork_im][tool] xg_cwork_im_send_file start groupId=${groupId} agentId=${hints?.agentId ?? "?"} ` +
|
|
268
|
+
`file=${displayName} bytes=${resolved.size} accountId=${effectiveAccountId ?? "0"}`);
|
|
269
|
+
// 2) 鉴权
|
|
270
|
+
let identity;
|
|
271
|
+
try {
|
|
272
|
+
identity = await getToken(accountConfig, log);
|
|
273
|
+
}
|
|
274
|
+
catch (err) {
|
|
275
|
+
return fail("AUTH_FAILED", `获取鉴权 token 失败:${String(err)}`);
|
|
276
|
+
}
|
|
277
|
+
// 3) 读取文件,并按实际字节数复核(防止校验后文件继续写入导致超限/清空)
|
|
278
|
+
let buffer;
|
|
279
|
+
try {
|
|
280
|
+
buffer = await readFile(resolved.realPath);
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
return fail("READ_FAILED", `读取文件失败:${String(err)}`);
|
|
284
|
+
}
|
|
285
|
+
if (buffer.length <= 0) {
|
|
286
|
+
return fail("EMPTY_FILE", "读取到空文件,无法发送。");
|
|
287
|
+
}
|
|
288
|
+
if (buffer.length > maxBytes) {
|
|
289
|
+
return fail("FILE_TOO_LARGE", `读取后文件大小 ${buffer.length} 字节超过上限 ${maxBytes} 字节(可能校验后仍在写入)。`);
|
|
290
|
+
}
|
|
291
|
+
// 4) 上传资源中心(带重试)
|
|
292
|
+
const formField = accountConfig.fileUploadFormField?.trim() || "file";
|
|
293
|
+
let fileId;
|
|
294
|
+
try {
|
|
295
|
+
const uploaded = await uploadWholeResourceFileWithRetry({
|
|
296
|
+
fileUploadUrl: resolveResourceUploadUrl(accountConfig.baseUrl),
|
|
297
|
+
token: identity.token,
|
|
298
|
+
buffer,
|
|
299
|
+
filename: displayName,
|
|
300
|
+
formField,
|
|
301
|
+
log,
|
|
302
|
+
});
|
|
303
|
+
fileId = uploaded.fileId;
|
|
304
|
+
}
|
|
305
|
+
catch (err) {
|
|
306
|
+
return fail("UPLOAD_FAILED", `上传文件失败:${String(err)}`);
|
|
307
|
+
}
|
|
308
|
+
// 5) 发送 FILE 消息(失败即失败,绝不降级为文本谎报成功)
|
|
309
|
+
const attachment = {
|
|
310
|
+
fileId,
|
|
311
|
+
fileType: "FILE",
|
|
312
|
+
format: formatFromFilename(displayName),
|
|
313
|
+
name: displayName,
|
|
314
|
+
size: buffer.length,
|
|
315
|
+
source: "resource",
|
|
316
|
+
};
|
|
317
|
+
const clientMsgId = generateClientMsgId();
|
|
318
|
+
try {
|
|
319
|
+
const sent = await sendUploadedFileMessage({
|
|
320
|
+
config: accountConfig,
|
|
321
|
+
token: identity.token,
|
|
322
|
+
groupId,
|
|
323
|
+
caption: params.caption,
|
|
324
|
+
attachment,
|
|
325
|
+
atUserIds: params.atUserIds ?? [],
|
|
326
|
+
clientMsgId,
|
|
327
|
+
log,
|
|
328
|
+
});
|
|
329
|
+
console.log(`[cwork_im][tool] xg_cwork_im_send_file success groupId=${groupId} fileId=${fileId} ` +
|
|
330
|
+
`serverMsgId=${sent.serverMsgId ?? "unknown"}`);
|
|
331
|
+
return toolJsonResult({
|
|
332
|
+
ok: true,
|
|
333
|
+
path: displayRel,
|
|
334
|
+
fileName: displayName,
|
|
335
|
+
size: buffer.length,
|
|
336
|
+
fileId,
|
|
337
|
+
clientMsgId: sent.clientMsgId,
|
|
338
|
+
...(sent.serverMsgId ? { serverMsgId: sent.serverMsgId } : {}),
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
catch (err) {
|
|
342
|
+
return fail("MESSAGE_SEND_FAILED", `发送文件消息失败:${String(err)}`);
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
function makeConsoleLog() {
|
|
348
|
+
return {
|
|
349
|
+
info: (m) => console.log(`[cwork_im] ${m}`),
|
|
350
|
+
warn: (m) => console.warn(`[cwork_im] ${m}`),
|
|
351
|
+
error: (m) => console.error(`[cwork_im] ${m}`),
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
//# sourceMappingURL=send-file-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send-file-tool.js","sourceRoot":"","sources":["../../src/send-file-tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send-file-tool.test.d.ts","sourceRoot":"","sources":["../../src/send-file-tool.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mkdir, symlink, writeFile } from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { describe, it } from "node:test";
|
|
6
|
+
import { resolveAccountConfigStrict, resolveDisplayFileName, resolveWorkspaceFile, selectTrustedAccountId, SendFileError, } from "./send-file-tool.js";
|
|
7
|
+
async function makeWorkspace() {
|
|
8
|
+
const root = path.join(os.tmpdir(), `cwork-sendfile-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
9
|
+
await mkdir(root, { recursive: true });
|
|
10
|
+
return root;
|
|
11
|
+
}
|
|
12
|
+
async function writeWorkspaceFile(root, rel, content = "hello") {
|
|
13
|
+
const abs = path.join(root, rel);
|
|
14
|
+
await mkdir(path.dirname(abs), { recursive: true });
|
|
15
|
+
await writeFile(abs, content, "utf8");
|
|
16
|
+
return abs;
|
|
17
|
+
}
|
|
18
|
+
async function expectCode(promise, code) {
|
|
19
|
+
await assert.rejects(promise, (err) => {
|
|
20
|
+
assert.ok(err instanceof SendFileError, `expected SendFileError, got ${String(err)}`);
|
|
21
|
+
assert.equal(err.code, code);
|
|
22
|
+
return true;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
const MAX = 50 * 1024 * 1024;
|
|
26
|
+
describe("resolveWorkspaceFile - accepts", () => {
|
|
27
|
+
it("accepts plain relative path", async () => {
|
|
28
|
+
const root = await makeWorkspace();
|
|
29
|
+
await writeWorkspaceFile(root, "report.html", "<h1>ok</h1>");
|
|
30
|
+
const r = await resolveWorkspaceFile(root, "report.html", MAX);
|
|
31
|
+
assert.equal(r.sourceName, "report.html");
|
|
32
|
+
assert.ok(r.size > 0);
|
|
33
|
+
});
|
|
34
|
+
it("accepts ./ prefix and nested output path", async () => {
|
|
35
|
+
const root = await makeWorkspace();
|
|
36
|
+
await writeWorkspaceFile(root, "output/report.pdf", "pdfbytes");
|
|
37
|
+
const r = await resolveWorkspaceFile(root, "./output/report.pdf", MAX);
|
|
38
|
+
assert.equal(r.sourceName, "report.pdf");
|
|
39
|
+
});
|
|
40
|
+
it("accepts /workspace/ logical prefix", async () => {
|
|
41
|
+
const root = await makeWorkspace();
|
|
42
|
+
await writeWorkspaceFile(root, "xg-cwork-im/NORMAL/202607/项目报告.html", "<p>报告</p>");
|
|
43
|
+
const r = await resolveWorkspaceFile(root, "/workspace/xg-cwork-im/NORMAL/202607/项目报告.html", MAX);
|
|
44
|
+
assert.equal(r.sourceName, "项目报告.html");
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe("resolveWorkspaceFile - rejects", () => {
|
|
48
|
+
it("rejects .. traversal", async () => {
|
|
49
|
+
const root = await makeWorkspace();
|
|
50
|
+
await expectCode(resolveWorkspaceFile(root, "../secret.txt", MAX), "PATH_OUTSIDE_WORKSPACE");
|
|
51
|
+
});
|
|
52
|
+
it("rejects windows drive absolute path", async () => {
|
|
53
|
+
const root = await makeWorkspace();
|
|
54
|
+
await expectCode(resolveWorkspaceFile(root, "C:\\Users\\x\\secret.txt", MAX), "PATH_OUTSIDE_WORKSPACE");
|
|
55
|
+
});
|
|
56
|
+
it("rejects UNC path", async () => {
|
|
57
|
+
const root = await makeWorkspace();
|
|
58
|
+
await expectCode(resolveWorkspaceFile(root, "\\\\server\\share\\file.html", MAX), "PATH_OUTSIDE_WORKSPACE");
|
|
59
|
+
});
|
|
60
|
+
it("rejects non-workspace absolute path", async () => {
|
|
61
|
+
const root = await makeWorkspace();
|
|
62
|
+
await expectCode(resolveWorkspaceFile(root, "/tmp/openclaw/file.html", MAX), "PATH_OUTSIDE_WORKSPACE");
|
|
63
|
+
});
|
|
64
|
+
it("rejects file:// and http(s) urls", async () => {
|
|
65
|
+
const root = await makeWorkspace();
|
|
66
|
+
await expectCode(resolveWorkspaceFile(root, "file:///etc/passwd", MAX), "PATH_OUTSIDE_WORKSPACE");
|
|
67
|
+
await expectCode(resolveWorkspaceFile(root, "https://example.com/file.html", MAX), "PATH_OUTSIDE_WORKSPACE");
|
|
68
|
+
});
|
|
69
|
+
it("rejects nonexistent file", async () => {
|
|
70
|
+
const root = await makeWorkspace();
|
|
71
|
+
await expectCode(resolveWorkspaceFile(root, "nope.pdf", MAX), "FILE_NOT_FOUND");
|
|
72
|
+
});
|
|
73
|
+
it("rejects a directory", async () => {
|
|
74
|
+
const root = await makeWorkspace();
|
|
75
|
+
await mkdir(path.join(root, "subdir"), { recursive: true });
|
|
76
|
+
await expectCode(resolveWorkspaceFile(root, "subdir", MAX), "PATH_IS_DIRECTORY");
|
|
77
|
+
});
|
|
78
|
+
it("rejects empty file", async () => {
|
|
79
|
+
const root = await makeWorkspace();
|
|
80
|
+
await writeWorkspaceFile(root, "empty.txt", "");
|
|
81
|
+
await expectCode(resolveWorkspaceFile(root, "empty.txt", MAX), "EMPTY_FILE");
|
|
82
|
+
});
|
|
83
|
+
it("rejects oversized file", async () => {
|
|
84
|
+
const root = await makeWorkspace();
|
|
85
|
+
await writeWorkspaceFile(root, "big.bin", "0123456789");
|
|
86
|
+
await expectCode(resolveWorkspaceFile(root, "big.bin", 4), "FILE_TOO_LARGE");
|
|
87
|
+
});
|
|
88
|
+
it("rejects blocked executable extensions", async () => {
|
|
89
|
+
const root = await makeWorkspace();
|
|
90
|
+
for (const ext of ["exe", "dll", "bat", "cmd", "msi", "ps1", "sh"]) {
|
|
91
|
+
await writeWorkspaceFile(root, `evil.${ext}`, "x");
|
|
92
|
+
await expectCode(resolveWorkspaceFile(root, `evil.${ext}`, MAX), "EXTENSION_NOT_ALLOWED");
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
it("rejects symlink escaping workspace", async (t) => {
|
|
96
|
+
const root = await makeWorkspace();
|
|
97
|
+
const outside = await makeWorkspace();
|
|
98
|
+
const secret = await writeWorkspaceFile(outside, "secret.txt", "top secret");
|
|
99
|
+
const linkPath = path.join(root, "link.txt");
|
|
100
|
+
try {
|
|
101
|
+
await symlink(secret, linkPath);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
t.skip("symlink not permitted on this platform");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
await expectCode(resolveWorkspaceFile(root, "link.txt", MAX), "SYMLINK_ESCAPE");
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
describe("selectTrustedAccountId", () => {
|
|
111
|
+
it("returns single provided id", () => {
|
|
112
|
+
assert.deepEqual(selectTrustedAccountId("main", undefined), { accountId: "main" });
|
|
113
|
+
assert.deepEqual(selectTrustedAccountId(undefined, "main"), { accountId: "main" });
|
|
114
|
+
});
|
|
115
|
+
it("returns undefined when neither provided", () => {
|
|
116
|
+
assert.deepEqual(selectTrustedAccountId(undefined, undefined), { accountId: undefined });
|
|
117
|
+
});
|
|
118
|
+
it("accepts consistent ids from both sources", () => {
|
|
119
|
+
assert.deepEqual(selectTrustedAccountId("main", "main"), { accountId: "main" });
|
|
120
|
+
});
|
|
121
|
+
it("flags conflict when the two trusted ids differ", () => {
|
|
122
|
+
const r = selectTrustedAccountId("main", "backup");
|
|
123
|
+
assert.ok("conflict" in r);
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
describe("resolveAccountConfigStrict", () => {
|
|
127
|
+
const base = (accounts) => ({ baseUrl: "https://im.example.com", ...(accounts ? { accounts } : {}) });
|
|
128
|
+
it("single-account mode returns top-level config", () => {
|
|
129
|
+
const r = resolveAccountConfigStrict(base(), "whatever");
|
|
130
|
+
assert.ok("config" in r);
|
|
131
|
+
});
|
|
132
|
+
it("rejects a non-existent account instead of falling back to first", () => {
|
|
133
|
+
const r = resolveAccountConfigStrict(base({ main: { robotKey: "k1" }, backup: { robotKey: "k2" } }), "ghost");
|
|
134
|
+
assert.ok("error" in r);
|
|
135
|
+
assert.equal(r.error, "ACCOUNT_NOT_FOUND");
|
|
136
|
+
});
|
|
137
|
+
it("resolves an existing account", () => {
|
|
138
|
+
const r = resolveAccountConfigStrict(base({ main: { robotKey: "k1" }, backup: { robotKey: "k2" } }), "main");
|
|
139
|
+
assert.ok("config" in r);
|
|
140
|
+
assert.equal(r.accountId, "main");
|
|
141
|
+
});
|
|
142
|
+
it("auto-selects the only non-default account when id omitted", () => {
|
|
143
|
+
const r = resolveAccountConfigStrict(base({ default: { maxAttachmentBytes: 5 }, only: { robotKey: "k" } }));
|
|
144
|
+
assert.ok("config" in r);
|
|
145
|
+
assert.equal(r.accountId, "only");
|
|
146
|
+
});
|
|
147
|
+
it("rejects when multiple accounts but no id provided (ambiguous)", () => {
|
|
148
|
+
const r = resolveAccountConfigStrict(base({ main: { robotKey: "k1" }, backup: { robotKey: "k2" } }));
|
|
149
|
+
assert.ok("error" in r);
|
|
150
|
+
});
|
|
151
|
+
it("applies account-level maxAttachmentBytes over top-level", () => {
|
|
152
|
+
const cfg = base({ main: { robotKey: "k1", maxAttachmentBytes: 1024 } });
|
|
153
|
+
cfg.maxAttachmentBytes = 50 * 1024 * 1024;
|
|
154
|
+
const r = resolveAccountConfigStrict(cfg, "main");
|
|
155
|
+
assert.ok("config" in r);
|
|
156
|
+
assert.equal(r.config.maxAttachmentBytes, 1024);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
describe("resolveDisplayFileName", () => {
|
|
160
|
+
it("keeps source name when fileName omitted (no UUID/suffix)", () => {
|
|
161
|
+
assert.equal(resolveDisplayFileName("项目报告.html"), "项目报告.html");
|
|
162
|
+
});
|
|
163
|
+
it("strips path parts from fileName and keeps source extension", () => {
|
|
164
|
+
assert.equal(resolveDisplayFileName("report.pdf", "../../evil/我的报告"), "我的报告.pdf");
|
|
165
|
+
});
|
|
166
|
+
it("does not let fileName change the source extension", () => {
|
|
167
|
+
assert.equal(resolveDisplayFileName("report.pdf", "report.exe"), "report.pdf");
|
|
168
|
+
});
|
|
169
|
+
it("removes illegal filesystem characters", () => {
|
|
170
|
+
assert.equal(resolveDisplayFileName("a.md", 'na:me*?"<>|'), "name.md");
|
|
171
|
+
});
|
|
172
|
+
it("falls back to source name when sanitized result is empty", () => {
|
|
173
|
+
assert.equal(resolveDisplayFileName("report.pdf", "///"), "report.pdf");
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
//# sourceMappingURL=send-file-tool.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"send-file-tool.test.js","sourceRoot":"","sources":["../../src/send-file-tool.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -39,7 +39,7 @@ export function buildSendGroupMessageTool(config, hints) {
|
|
|
39
39
|
description: [
|
|
40
40
|
"【用途】仅用于 OpenClaw **主动推消息**:例如定时任务 / Cron / 后台任务完成后,经 xg_cwork_im 向群里发一条**纯文字**结果或通知。",
|
|
41
41
|
"【不要用错】用户正在 IM 里和助手**对话**时,**不要**用本工具回话——对话回复由 OpenClaw 正常走 channel,**用不上**本工具。",
|
|
42
|
-
"
|
|
42
|
+
"【发文件】本工具**只能发纯文本,不能发附件**。用户要「发报告/发文件」时,必须改用 `xg_cwork_im_send_file` 工具(传 workspace 相对路径);禁止用 MEDIA 或上传到外站。",
|
|
43
43
|
"【参数】groupId 为 IM 群唯一 ID;主动推送时若需告知某人,可填 atUserIds。",
|
|
44
44
|
].join("\n"),
|
|
45
45
|
parameters: SendGroupMessageParams,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"send-group-message-tool.js","sourceRoot":"","sources":["../../src/send-group-message-tool.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAe,MAAM,mBAAmB,CAAC;AAEtD,OAAO,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,oBAAoB,EAA6B,MAAM,yBAAyB,CAAC;AAE1F,4EAA4E;AAE5E,MAAM,sBAAsB,GAAG,IAAI,CAAC,MAAM,CAAC;IACvC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC;QACjB,WAAW,EAAE,wCAAwC;KACxD,CAAC;IACF,SAAS,EAAE,IAAI,CAAC,QAAQ,CACpB,IAAI,CAAC,MAAM,CAAC;QACR,WAAW,EACP,0EAA0E;KACjF,CAAC,CACL;IACD,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;QACd,WAAW,EAAE,yBAAyB;KACzC,CAAC;IACF,SAAS,EAAE,IAAI,CAAC,QAAQ,CACpB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE;QACtB,WAAW,EAAE,sBAAsB;KACtC,CAAC,CACL;CACJ,CAAC,CAAC;AAEH,2EAA2E;AAE3E;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,MAAkB,EAAE,KAA4B;IACtF,OAAO;QACH,IAAI,EAAE,gCAAgC;QACtC,KAAK,EAAE,2BAA2B;QAClC,WAAW,EAAE;YACT,sFAAsF;YACtF,gFAAgF;YAChF,
|
|
1
|
+
{"version":3,"file":"send-group-message-tool.js","sourceRoot":"","sources":["../../src/send-group-message-tool.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,IAAI,EAAe,MAAM,mBAAmB,CAAC;AAEtD,OAAO,EAAE,2BAA2B,EAAE,MAAM,gCAAgC,CAAC;AAC7E,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,oBAAoB,EAA6B,MAAM,yBAAyB,CAAC;AAE1F,4EAA4E;AAE5E,MAAM,sBAAsB,GAAG,IAAI,CAAC,MAAM,CAAC;IACvC,OAAO,EAAE,IAAI,CAAC,MAAM,CAAC;QACjB,WAAW,EAAE,wCAAwC;KACxD,CAAC;IACF,SAAS,EAAE,IAAI,CAAC,QAAQ,CACpB,IAAI,CAAC,MAAM,CAAC;QACR,WAAW,EACP,0EAA0E;KACjF,CAAC,CACL;IACD,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;QACd,WAAW,EAAE,yBAAyB;KACzC,CAAC;IACF,SAAS,EAAE,IAAI,CAAC,QAAQ,CACpB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE;QACtB,WAAW,EAAE,sBAAsB;KACtC,CAAC,CACL;CACJ,CAAC,CAAC;AAEH,2EAA2E;AAE3E;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CAAC,MAAkB,EAAE,KAA4B;IACtF,OAAO;QACH,IAAI,EAAE,gCAAgC;QACtC,KAAK,EAAE,2BAA2B;QAClC,WAAW,EAAE;YACT,sFAAsF;YACtF,gFAAgF;YAChF,4GAA4G;YAC5G,mDAAmD;SACtD,CAAC,IAAI,CAAC,IAAI,CAAC;QACZ,UAAU,EAAE,sBAAsB;QAClC,KAAK,CAAC,OAAO,CAAC,WAAmB,EAAE,MAA6C;YAC5E,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,GAAG,EAAE,EAAE,GAAG,MAAM,CAAC;YACjD,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,KAAK,EAAE,gBAAgB,CAAC;YAE9D,OAAO,CAAC,GAAG,CAAC,mEAAmE,OAAO,YAAY,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YAE/H,IAAI,QAAqB,CAAC;YAC1B,IAAI,CAAC;gBACD,MAAM,aAAa,GAAG,oBAAoB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;gBAC9D,QAAQ,GAAG,MAAM,QAAQ,CAAC,aAAa,CAAC,CAAC;gBACzC,OAAO,CAAC,GAAG,CACP,6EAA6E,QAAQ,CAAC,MAAM,oBAAoB,SAAS,IAAI,GAAG,EAAE,CACrI,CAAC;YACN,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACpB,OAAO,cAAc,CAAC;oBAClB,EAAE,EAAE,KAAK;oBACT,KAAK,EAAE,kCAAkC,MAAM,CAAC,GAAG,CAAC,EAAE;iBACzD,CAAC,CAAC;YACP,CAAC;YAED,IAAI,CAAC;gBACD,MAAM,aAAa,GAAG,oBAAoB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;gBAC9D,MAAM,YAAY,GAAG,2BAA2B,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;gBAC/D,MAAM,eAAe,CAAC,aAAa,EAAE,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;gBACvF,OAAO,CAAC,GAAG,CACP,oEAAoE,OAAO,iCAAiC,SAAS,IAAI,GAAG,EAAE,CACjI,CAAC;YACN,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACpB,OAAO,cAAc,CAAC;oBAClB,EAAE,EAAE,KAAK;oBACT,KAAK,EAAE,mCAAmC,OAAO,KAAK,MAAM,CAAC,GAAG,CAAC,EAAE;iBACtE,CAAC,CAAC;YACP,CAAC;YAED,OAAO,cAAc,CAAC;gBAClB,EAAE,EAAE,IAAI;gBACR,OAAO;gBACP,MAAM,EAAE,QAAQ,CAAC,MAAM;aAC1B,CAAC,CAAC;QACP,CAAC;KACJ,CAAC;AACN,CAAC"}
|