opencode-feishu 1.3.0 → 1.3.2
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.js +87 -36
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -112525,6 +112525,7 @@ var FeishuConfigSchema = external_exports.object({
|
|
|
112525
112525
|
pollInterval: external_exports.number().int().positive().default(1e3),
|
|
112526
112526
|
stablePolls: external_exports.number().int().positive().default(3),
|
|
112527
112527
|
dedupTtl: external_exports.number().int().positive().default(10 * 60 * 1e3),
|
|
112528
|
+
maxResourceSize: external_exports.number().int().positive().max(500 * 1024 * 1024).default(500 * 1024 * 1024),
|
|
112528
112529
|
directory: external_exports.string().optional(),
|
|
112529
112530
|
autoPrompt: AutoPromptSchema.default(() => AutoPromptSchema.parse({}))
|
|
112530
112531
|
});
|
|
@@ -112602,6 +112603,29 @@ var CardKitClient = class {
|
|
|
112602
112603
|
});
|
|
112603
112604
|
}
|
|
112604
112605
|
}
|
|
112606
|
+
/**
|
|
112607
|
+
* 在卡片末尾追加新组件(用于流式卡片动态添加元素)
|
|
112608
|
+
*/
|
|
112609
|
+
async addElement(cardId, elements, sequence) {
|
|
112610
|
+
const res = await this.larkClient.cardkit.v1.cardElement.create({
|
|
112611
|
+
data: {
|
|
112612
|
+
type: "append",
|
|
112613
|
+
elements: JSON.stringify(elements),
|
|
112614
|
+
sequence
|
|
112615
|
+
},
|
|
112616
|
+
path: {
|
|
112617
|
+
card_id: cardId
|
|
112618
|
+
}
|
|
112619
|
+
});
|
|
112620
|
+
if (res?.code !== 0) {
|
|
112621
|
+
this.log?.("warn", "CardKit addElement \u5931\u8D25", {
|
|
112622
|
+
cardId,
|
|
112623
|
+
code: res?.code,
|
|
112624
|
+
msg: res?.msg
|
|
112625
|
+
});
|
|
112626
|
+
throw new Error(`CardKit addElement \u5931\u8D25: ${res?.msg ?? "unknown"} (code: ${res?.code})`);
|
|
112627
|
+
}
|
|
112628
|
+
}
|
|
112605
112629
|
/**
|
|
112606
112630
|
* 关闭卡片流式模式
|
|
112607
112631
|
*/
|
|
@@ -112818,6 +112842,10 @@ function createSendCardTool(deps) {
|
|
|
112818
112842
|
async execute(args, context) {
|
|
112819
112843
|
const chatId = getChatIdBySession(context.sessionID);
|
|
112820
112844
|
if (!chatId) {
|
|
112845
|
+
deps.log("warn", "Agent \u5361\u7247\u53D1\u9001\u8DF3\u8FC7\uFF1AsessionID \u65E0\u98DE\u4E66\u804A\u5929\u6620\u5C04", {
|
|
112846
|
+
sessionId: context.sessionID,
|
|
112847
|
+
title: args.title
|
|
112848
|
+
});
|
|
112821
112849
|
return "\u9519\u8BEF\uFF1A\u5F53\u524D\u4F1A\u8BDD\u4E0D\u5173\u8054\u98DE\u4E66\u804A\u5929\uFF0C\u65E0\u6CD5\u53D1\u9001\u5361\u7247";
|
|
112822
112850
|
}
|
|
112823
112851
|
const chatInfo = getChatInfoBySession(context.sessionID);
|
|
@@ -113062,8 +113090,7 @@ function isDuplicate(messageId) {
|
|
|
113062
113090
|
}
|
|
113063
113091
|
|
|
113064
113092
|
// src/feishu/resource.ts
|
|
113065
|
-
|
|
113066
|
-
async function downloadMessageResource(client, messageId, fileKey, type, log) {
|
|
113093
|
+
async function downloadMessageResource(client, messageId, fileKey, type, log, maxSize) {
|
|
113067
113094
|
try {
|
|
113068
113095
|
const res = await client.im.messageResource.get({
|
|
113069
113096
|
path: { message_id: messageId, file_key: fileKey },
|
|
@@ -113071,7 +113098,7 @@ async function downloadMessageResource(client, messageId, fileKey, type, log) {
|
|
|
113071
113098
|
});
|
|
113072
113099
|
if (!res) {
|
|
113073
113100
|
log("warn", "\u8D44\u6E90\u4E0B\u8F7D\u8FD4\u56DE\u7A7A\u6570\u636E", { messageId, fileKey, type });
|
|
113074
|
-
return null;
|
|
113101
|
+
return { resource: null, reason: "error" };
|
|
113075
113102
|
}
|
|
113076
113103
|
const stream4 = res.getReadableStream();
|
|
113077
113104
|
const chunks = [];
|
|
@@ -113079,10 +113106,10 @@ async function downloadMessageResource(client, messageId, fileKey, type, log) {
|
|
|
113079
113106
|
for await (const chunk of stream4) {
|
|
113080
113107
|
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
113081
113108
|
totalSize += buf.length;
|
|
113082
|
-
if (totalSize >
|
|
113083
|
-
log("warn", "\u8D44\u6E90\u8FC7\u5927\uFF0C\u8DF3\u8FC7\u4E0B\u8F7D", { messageId, fileKey, totalSize });
|
|
113109
|
+
if (totalSize > maxSize) {
|
|
113110
|
+
log("warn", "\u8D44\u6E90\u8FC7\u5927\uFF0C\u8DF3\u8FC7\u4E0B\u8F7D", { messageId, fileKey, totalSize, maxSize });
|
|
113084
113111
|
stream4.destroy();
|
|
113085
|
-
return null;
|
|
113112
|
+
return { resource: null, reason: "too_large", totalSize };
|
|
113086
113113
|
}
|
|
113087
113114
|
chunks.push(buf);
|
|
113088
113115
|
}
|
|
@@ -113091,7 +113118,7 @@ async function downloadMessageResource(client, messageId, fileKey, type, log) {
|
|
|
113091
113118
|
const contentType = headers?.["content-type"] ?? guessMimeByType(type);
|
|
113092
113119
|
const base643 = buffer.toString("base64");
|
|
113093
113120
|
const dataUrl = `data:${contentType};base64,${base643}`;
|
|
113094
|
-
return { dataUrl, mime: contentType };
|
|
113121
|
+
return { resource: { dataUrl, mime: contentType }, reason: "ok" };
|
|
113095
113122
|
} catch (err) {
|
|
113096
113123
|
log("warn", "\u8D44\u6E90\u4E0B\u8F7D\u5931\u8D25", {
|
|
113097
113124
|
messageId,
|
|
@@ -113099,7 +113126,7 @@ async function downloadMessageResource(client, messageId, fileKey, type, log) {
|
|
|
113099
113126
|
type,
|
|
113100
113127
|
error: err instanceof Error ? err.message : String(err)
|
|
113101
113128
|
});
|
|
113102
|
-
return null;
|
|
113129
|
+
return { resource: null, reason: "error" };
|
|
113103
113130
|
}
|
|
113104
113131
|
}
|
|
113105
113132
|
function guessMimeByType(type) {
|
|
@@ -113139,19 +113166,19 @@ function guessMimeByFilename(filename) {
|
|
|
113139
113166
|
}
|
|
113140
113167
|
|
|
113141
113168
|
// src/feishu/content-extractor.ts
|
|
113142
|
-
async function extractParts(feishuClient, messageId, messageType, rawContent, log) {
|
|
113169
|
+
async function extractParts(feishuClient, messageId, messageType, rawContent, log, maxResourceSize) {
|
|
113143
113170
|
try {
|
|
113144
113171
|
switch (messageType) {
|
|
113145
113172
|
case "text":
|
|
113146
113173
|
return extractText(rawContent);
|
|
113147
113174
|
case "image":
|
|
113148
|
-
return await extractImage(feishuClient, messageId, rawContent, log);
|
|
113175
|
+
return await extractImage(feishuClient, messageId, rawContent, log, maxResourceSize);
|
|
113149
113176
|
case "post":
|
|
113150
113177
|
return extractPost(rawContent);
|
|
113151
113178
|
case "file":
|
|
113152
|
-
return await extractFile(feishuClient, messageId, rawContent, log);
|
|
113179
|
+
return await extractFile(feishuClient, messageId, rawContent, log, maxResourceSize);
|
|
113153
113180
|
case "audio":
|
|
113154
|
-
return await extractAudio(feishuClient, messageId, rawContent, log);
|
|
113181
|
+
return await extractAudio(feishuClient, messageId, rawContent, log, maxResourceSize);
|
|
113155
113182
|
case "media":
|
|
113156
113183
|
return extractMediaFallback();
|
|
113157
113184
|
case "sticker":
|
|
@@ -113222,13 +113249,15 @@ function extractText(rawContent) {
|
|
|
113222
113249
|
if (!text) return [];
|
|
113223
113250
|
return [{ type: "text", text }];
|
|
113224
113251
|
}
|
|
113225
|
-
async function extractImage(client, messageId, rawContent, log) {
|
|
113252
|
+
async function extractImage(client, messageId, rawContent, log, maxResourceSize) {
|
|
113226
113253
|
const parsed = JSON.parse(rawContent);
|
|
113227
113254
|
const imageKey = parsed.image_key;
|
|
113228
113255
|
if (!imageKey) return [{ type: "text", text: "[\u56FE\u7247: \u65E0\u6CD5\u83B7\u53D6]" }];
|
|
113229
|
-
const
|
|
113230
|
-
if (!resource)
|
|
113231
|
-
|
|
113256
|
+
const result = await downloadMessageResource(client, messageId, imageKey, "image", log, maxResourceSize);
|
|
113257
|
+
if (!result.resource) {
|
|
113258
|
+
return [{ type: "text", text: formatDownloadFailure("\u56FE\u7247", result, maxResourceSize) }];
|
|
113259
|
+
}
|
|
113260
|
+
return [{ type: "file", mime: result.resource.mime, url: result.resource.dataUrl }];
|
|
113232
113261
|
}
|
|
113233
113262
|
function extractPost(rawContent) {
|
|
113234
113263
|
const text = extractPostText(rawContent);
|
|
@@ -113263,27 +113292,39 @@ function extractPostText(rawContent) {
|
|
|
113263
113292
|
return "";
|
|
113264
113293
|
}
|
|
113265
113294
|
}
|
|
113266
|
-
async function extractFile(client, messageId, rawContent, log) {
|
|
113295
|
+
async function extractFile(client, messageId, rawContent, log, maxResourceSize) {
|
|
113267
113296
|
const parsed = JSON.parse(rawContent);
|
|
113268
113297
|
const fileKey = parsed.file_key;
|
|
113269
113298
|
const fileName = parsed.file_name ?? "\u672A\u77E5\u6587\u4EF6";
|
|
113270
113299
|
if (!fileKey) return [{ type: "text", text: `[\u6587\u4EF6: ${fileName}]` }];
|
|
113271
113300
|
const mime = guessMimeByFilename(fileName);
|
|
113272
|
-
const
|
|
113273
|
-
if (!resource)
|
|
113274
|
-
|
|
113301
|
+
const result = await downloadMessageResource(client, messageId, fileKey, "file", log, maxResourceSize);
|
|
113302
|
+
if (!result.resource) {
|
|
113303
|
+
return [{ type: "text", text: formatDownloadFailure(fileName, result, maxResourceSize) }];
|
|
113304
|
+
}
|
|
113305
|
+
return [{ type: "file", mime: result.resource.mime || mime, url: result.resource.dataUrl, filename: fileName }];
|
|
113275
113306
|
}
|
|
113276
|
-
async function extractAudio(client, messageId, rawContent, log) {
|
|
113307
|
+
async function extractAudio(client, messageId, rawContent, log, maxResourceSize) {
|
|
113277
113308
|
const parsed = JSON.parse(rawContent);
|
|
113278
113309
|
const fileKey = parsed.file_key;
|
|
113279
113310
|
if (!fileKey) return [{ type: "text", text: "[\u8BED\u97F3: \u65E0\u6CD5\u83B7\u53D6]" }];
|
|
113280
|
-
const
|
|
113281
|
-
if (!resource)
|
|
113282
|
-
|
|
113311
|
+
const result = await downloadMessageResource(client, messageId, fileKey, "file", log, maxResourceSize);
|
|
113312
|
+
if (!result.resource) {
|
|
113313
|
+
return [{ type: "text", text: formatDownloadFailure("\u8BED\u97F3", result, maxResourceSize) }];
|
|
113314
|
+
}
|
|
113315
|
+
return [{ type: "file", mime: result.resource.mime || "audio/opus", url: result.resource.dataUrl }];
|
|
113283
113316
|
}
|
|
113284
113317
|
function extractMediaFallback() {
|
|
113285
113318
|
return [{ type: "text", text: "[\u89C6\u9891\u6D88\u606F]" }];
|
|
113286
113319
|
}
|
|
113320
|
+
function formatDownloadFailure(label, result, maxSize) {
|
|
113321
|
+
if (result.reason === "too_large" && result.totalSize) {
|
|
113322
|
+
const sizeMB = (result.totalSize / (1024 * 1024)).toFixed(1);
|
|
113323
|
+
const limitMB = (maxSize / (1024 * 1024)).toFixed(0);
|
|
113324
|
+
return `[\u6587\u4EF6\u8FC7\u5927: ${label}, \u5DF2\u4E0B\u8F7D ${sizeMB}MB \u65F6\u8D85\u51FA ${limitMB}MB \u9650\u5236]`;
|
|
113325
|
+
}
|
|
113326
|
+
return `[\u4E0B\u8F7D\u5931\u8D25: ${label}]`;
|
|
113327
|
+
}
|
|
113287
113328
|
function extractInteractive(rawContent) {
|
|
113288
113329
|
try {
|
|
113289
113330
|
const parsed = JSON.parse(rawContent);
|
|
@@ -113905,6 +113946,7 @@ var StreamingCard = class {
|
|
|
113905
113946
|
textBuffer = "";
|
|
113906
113947
|
toolStates = /* @__PURE__ */ new Map();
|
|
113907
113948
|
closed = false;
|
|
113949
|
+
toolsElementAdded = false;
|
|
113908
113950
|
/**
|
|
113909
113951
|
* 创建卡片 + 发送 interactive 消息 → messageId
|
|
113910
113952
|
*/
|
|
@@ -113912,15 +113954,14 @@ var StreamingCard = class {
|
|
|
113912
113954
|
const schema = {
|
|
113913
113955
|
data: {
|
|
113914
113956
|
schema: "2.0",
|
|
113915
|
-
config: { streaming_mode: true
|
|
113957
|
+
config: { streaming_mode: true },
|
|
113916
113958
|
header: {
|
|
113917
113959
|
title: { tag: "plain_text", content: "AI \u56DE\u590D" },
|
|
113918
113960
|
template: "blue"
|
|
113919
113961
|
},
|
|
113920
113962
|
body: {
|
|
113921
113963
|
elements: [
|
|
113922
|
-
{ tag: "markdown", element_id: "content", content: "\u6B63\u5728\u601D\u8003..." }
|
|
113923
|
-
{ tag: "markdown", element_id: "tools", content: "" }
|
|
113964
|
+
{ tag: "markdown", element_id: "content", content: "\u6B63\u5728\u601D\u8003..." }
|
|
113924
113965
|
]
|
|
113925
113966
|
}
|
|
113926
113967
|
}
|
|
@@ -114010,12 +114051,22 @@ var StreamingCard = class {
|
|
|
114010
114051
|
const icon = ts.state === "completed" ? "\u2705" : ts.state === "error" ? "\u274C" : "\u{1F504}";
|
|
114011
114052
|
lines.push(`${icon} ${ts.tool}`);
|
|
114012
114053
|
}
|
|
114013
|
-
|
|
114014
|
-
|
|
114015
|
-
|
|
114016
|
-
|
|
114017
|
-
|
|
114018
|
-
|
|
114054
|
+
const content = lines.join("\n");
|
|
114055
|
+
if (!this.toolsElementAdded) {
|
|
114056
|
+
this.toolsElementAdded = true;
|
|
114057
|
+
await this.cardkit.addElement(
|
|
114058
|
+
this.cardId,
|
|
114059
|
+
[{ tag: "markdown", element_id: "tools", content }],
|
|
114060
|
+
++this.seq
|
|
114061
|
+
);
|
|
114062
|
+
} else {
|
|
114063
|
+
await this.cardkit.updateElement(
|
|
114064
|
+
this.cardId,
|
|
114065
|
+
"tools",
|
|
114066
|
+
content,
|
|
114067
|
+
++this.seq
|
|
114068
|
+
);
|
|
114069
|
+
}
|
|
114019
114070
|
}
|
|
114020
114071
|
};
|
|
114021
114072
|
|
|
@@ -114043,7 +114094,7 @@ async function handleChat(ctx, deps, signal) {
|
|
|
114043
114094
|
const sessionKey = buildSessionKey(chatType, chatType === "p2p" ? senderId : chatId);
|
|
114044
114095
|
const session = await getOrCreateSession(client, sessionKey, directory);
|
|
114045
114096
|
registerSessionChat(session.id, chatId, chatType);
|
|
114046
|
-
const parts = await buildPromptParts(feishuClient, messageId, messageType, rawContent, content, chatType, senderId, log);
|
|
114097
|
+
const parts = await buildPromptParts(feishuClient, messageId, messageType, rawContent, content, chatType, senderId, log, config2.maxResourceSize);
|
|
114047
114098
|
if (!parts.length) return void 0;
|
|
114048
114099
|
log("info", "\u6536\u5230\u7528\u6237\u6D88\u606F", {
|
|
114049
114100
|
sessionKey,
|
|
@@ -114217,7 +114268,7 @@ async function handleChat(ctx, deps, signal) {
|
|
|
114217
114268
|
unregisterPending(activeSessionId);
|
|
114218
114269
|
}
|
|
114219
114270
|
}
|
|
114220
|
-
async function buildPromptParts(feishuClient, messageId, messageType, rawContent, textContent, chatType, senderId, log) {
|
|
114271
|
+
async function buildPromptParts(feishuClient, messageId, messageType, rawContent, textContent, chatType, senderId, log, maxResourceSize) {
|
|
114221
114272
|
if (messageType === "text") {
|
|
114222
114273
|
let promptText = textContent;
|
|
114223
114274
|
if (chatType === "group" && senderId) {
|
|
@@ -114225,7 +114276,7 @@ async function buildPromptParts(feishuClient, messageId, messageType, rawContent
|
|
|
114225
114276
|
}
|
|
114226
114277
|
return [{ type: "text", text: promptText }];
|
|
114227
114278
|
}
|
|
114228
|
-
const parts = await extractParts(feishuClient, messageId, messageType, rawContent, log);
|
|
114279
|
+
const parts = await extractParts(feishuClient, messageId, messageType, rawContent, log, maxResourceSize);
|
|
114229
114280
|
if (chatType === "group" && senderId && parts.length > 0) {
|
|
114230
114281
|
return [{ type: "text", text: `[${senderId}]:` }, ...parts];
|
|
114231
114282
|
}
|