opencode-feishu 1.3.0 → 1.3.1
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 +83 -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
|
*/
|
|
@@ -113062,8 +113086,7 @@ function isDuplicate(messageId) {
|
|
|
113062
113086
|
}
|
|
113063
113087
|
|
|
113064
113088
|
// src/feishu/resource.ts
|
|
113065
|
-
|
|
113066
|
-
async function downloadMessageResource(client, messageId, fileKey, type, log) {
|
|
113089
|
+
async function downloadMessageResource(client, messageId, fileKey, type, log, maxSize) {
|
|
113067
113090
|
try {
|
|
113068
113091
|
const res = await client.im.messageResource.get({
|
|
113069
113092
|
path: { message_id: messageId, file_key: fileKey },
|
|
@@ -113071,7 +113094,7 @@ async function downloadMessageResource(client, messageId, fileKey, type, log) {
|
|
|
113071
113094
|
});
|
|
113072
113095
|
if (!res) {
|
|
113073
113096
|
log("warn", "\u8D44\u6E90\u4E0B\u8F7D\u8FD4\u56DE\u7A7A\u6570\u636E", { messageId, fileKey, type });
|
|
113074
|
-
return null;
|
|
113097
|
+
return { resource: null, reason: "error" };
|
|
113075
113098
|
}
|
|
113076
113099
|
const stream4 = res.getReadableStream();
|
|
113077
113100
|
const chunks = [];
|
|
@@ -113079,10 +113102,10 @@ async function downloadMessageResource(client, messageId, fileKey, type, log) {
|
|
|
113079
113102
|
for await (const chunk of stream4) {
|
|
113080
113103
|
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
113081
113104
|
totalSize += buf.length;
|
|
113082
|
-
if (totalSize >
|
|
113083
|
-
log("warn", "\u8D44\u6E90\u8FC7\u5927\uFF0C\u8DF3\u8FC7\u4E0B\u8F7D", { messageId, fileKey, totalSize });
|
|
113105
|
+
if (totalSize > maxSize) {
|
|
113106
|
+
log("warn", "\u8D44\u6E90\u8FC7\u5927\uFF0C\u8DF3\u8FC7\u4E0B\u8F7D", { messageId, fileKey, totalSize, maxSize });
|
|
113084
113107
|
stream4.destroy();
|
|
113085
|
-
return null;
|
|
113108
|
+
return { resource: null, reason: "too_large", totalSize };
|
|
113086
113109
|
}
|
|
113087
113110
|
chunks.push(buf);
|
|
113088
113111
|
}
|
|
@@ -113091,7 +113114,7 @@ async function downloadMessageResource(client, messageId, fileKey, type, log) {
|
|
|
113091
113114
|
const contentType = headers?.["content-type"] ?? guessMimeByType(type);
|
|
113092
113115
|
const base643 = buffer.toString("base64");
|
|
113093
113116
|
const dataUrl = `data:${contentType};base64,${base643}`;
|
|
113094
|
-
return { dataUrl, mime: contentType };
|
|
113117
|
+
return { resource: { dataUrl, mime: contentType }, reason: "ok" };
|
|
113095
113118
|
} catch (err) {
|
|
113096
113119
|
log("warn", "\u8D44\u6E90\u4E0B\u8F7D\u5931\u8D25", {
|
|
113097
113120
|
messageId,
|
|
@@ -113099,7 +113122,7 @@ async function downloadMessageResource(client, messageId, fileKey, type, log) {
|
|
|
113099
113122
|
type,
|
|
113100
113123
|
error: err instanceof Error ? err.message : String(err)
|
|
113101
113124
|
});
|
|
113102
|
-
return null;
|
|
113125
|
+
return { resource: null, reason: "error" };
|
|
113103
113126
|
}
|
|
113104
113127
|
}
|
|
113105
113128
|
function guessMimeByType(type) {
|
|
@@ -113139,19 +113162,19 @@ function guessMimeByFilename(filename) {
|
|
|
113139
113162
|
}
|
|
113140
113163
|
|
|
113141
113164
|
// src/feishu/content-extractor.ts
|
|
113142
|
-
async function extractParts(feishuClient, messageId, messageType, rawContent, log) {
|
|
113165
|
+
async function extractParts(feishuClient, messageId, messageType, rawContent, log, maxResourceSize) {
|
|
113143
113166
|
try {
|
|
113144
113167
|
switch (messageType) {
|
|
113145
113168
|
case "text":
|
|
113146
113169
|
return extractText(rawContent);
|
|
113147
113170
|
case "image":
|
|
113148
|
-
return await extractImage(feishuClient, messageId, rawContent, log);
|
|
113171
|
+
return await extractImage(feishuClient, messageId, rawContent, log, maxResourceSize);
|
|
113149
113172
|
case "post":
|
|
113150
113173
|
return extractPost(rawContent);
|
|
113151
113174
|
case "file":
|
|
113152
|
-
return await extractFile(feishuClient, messageId, rawContent, log);
|
|
113175
|
+
return await extractFile(feishuClient, messageId, rawContent, log, maxResourceSize);
|
|
113153
113176
|
case "audio":
|
|
113154
|
-
return await extractAudio(feishuClient, messageId, rawContent, log);
|
|
113177
|
+
return await extractAudio(feishuClient, messageId, rawContent, log, maxResourceSize);
|
|
113155
113178
|
case "media":
|
|
113156
113179
|
return extractMediaFallback();
|
|
113157
113180
|
case "sticker":
|
|
@@ -113222,13 +113245,15 @@ function extractText(rawContent) {
|
|
|
113222
113245
|
if (!text) return [];
|
|
113223
113246
|
return [{ type: "text", text }];
|
|
113224
113247
|
}
|
|
113225
|
-
async function extractImage(client, messageId, rawContent, log) {
|
|
113248
|
+
async function extractImage(client, messageId, rawContent, log, maxResourceSize) {
|
|
113226
113249
|
const parsed = JSON.parse(rawContent);
|
|
113227
113250
|
const imageKey = parsed.image_key;
|
|
113228
113251
|
if (!imageKey) return [{ type: "text", text: "[\u56FE\u7247: \u65E0\u6CD5\u83B7\u53D6]" }];
|
|
113229
|
-
const
|
|
113230
|
-
if (!resource)
|
|
113231
|
-
|
|
113252
|
+
const result = await downloadMessageResource(client, messageId, imageKey, "image", log, maxResourceSize);
|
|
113253
|
+
if (!result.resource) {
|
|
113254
|
+
return [{ type: "text", text: formatDownloadFailure("\u56FE\u7247", result, maxResourceSize) }];
|
|
113255
|
+
}
|
|
113256
|
+
return [{ type: "file", mime: result.resource.mime, url: result.resource.dataUrl }];
|
|
113232
113257
|
}
|
|
113233
113258
|
function extractPost(rawContent) {
|
|
113234
113259
|
const text = extractPostText(rawContent);
|
|
@@ -113263,27 +113288,39 @@ function extractPostText(rawContent) {
|
|
|
113263
113288
|
return "";
|
|
113264
113289
|
}
|
|
113265
113290
|
}
|
|
113266
|
-
async function extractFile(client, messageId, rawContent, log) {
|
|
113291
|
+
async function extractFile(client, messageId, rawContent, log, maxResourceSize) {
|
|
113267
113292
|
const parsed = JSON.parse(rawContent);
|
|
113268
113293
|
const fileKey = parsed.file_key;
|
|
113269
113294
|
const fileName = parsed.file_name ?? "\u672A\u77E5\u6587\u4EF6";
|
|
113270
113295
|
if (!fileKey) return [{ type: "text", text: `[\u6587\u4EF6: ${fileName}]` }];
|
|
113271
113296
|
const mime = guessMimeByFilename(fileName);
|
|
113272
|
-
const
|
|
113273
|
-
if (!resource)
|
|
113274
|
-
|
|
113297
|
+
const result = await downloadMessageResource(client, messageId, fileKey, "file", log, maxResourceSize);
|
|
113298
|
+
if (!result.resource) {
|
|
113299
|
+
return [{ type: "text", text: formatDownloadFailure(fileName, result, maxResourceSize) }];
|
|
113300
|
+
}
|
|
113301
|
+
return [{ type: "file", mime: result.resource.mime || mime, url: result.resource.dataUrl, filename: fileName }];
|
|
113275
113302
|
}
|
|
113276
|
-
async function extractAudio(client, messageId, rawContent, log) {
|
|
113303
|
+
async function extractAudio(client, messageId, rawContent, log, maxResourceSize) {
|
|
113277
113304
|
const parsed = JSON.parse(rawContent);
|
|
113278
113305
|
const fileKey = parsed.file_key;
|
|
113279
113306
|
if (!fileKey) return [{ type: "text", text: "[\u8BED\u97F3: \u65E0\u6CD5\u83B7\u53D6]" }];
|
|
113280
|
-
const
|
|
113281
|
-
if (!resource)
|
|
113282
|
-
|
|
113307
|
+
const result = await downloadMessageResource(client, messageId, fileKey, "file", log, maxResourceSize);
|
|
113308
|
+
if (!result.resource) {
|
|
113309
|
+
return [{ type: "text", text: formatDownloadFailure("\u8BED\u97F3", result, maxResourceSize) }];
|
|
113310
|
+
}
|
|
113311
|
+
return [{ type: "file", mime: result.resource.mime || "audio/opus", url: result.resource.dataUrl }];
|
|
113283
113312
|
}
|
|
113284
113313
|
function extractMediaFallback() {
|
|
113285
113314
|
return [{ type: "text", text: "[\u89C6\u9891\u6D88\u606F]" }];
|
|
113286
113315
|
}
|
|
113316
|
+
function formatDownloadFailure(label, result, maxSize) {
|
|
113317
|
+
if (result.reason === "too_large" && result.totalSize) {
|
|
113318
|
+
const sizeMB = (result.totalSize / (1024 * 1024)).toFixed(1);
|
|
113319
|
+
const limitMB = (maxSize / (1024 * 1024)).toFixed(0);
|
|
113320
|
+
return `[\u6587\u4EF6\u8FC7\u5927: ${label}, \u5DF2\u4E0B\u8F7D ${sizeMB}MB \u65F6\u8D85\u51FA ${limitMB}MB \u9650\u5236]`;
|
|
113321
|
+
}
|
|
113322
|
+
return `[\u4E0B\u8F7D\u5931\u8D25: ${label}]`;
|
|
113323
|
+
}
|
|
113287
113324
|
function extractInteractive(rawContent) {
|
|
113288
113325
|
try {
|
|
113289
113326
|
const parsed = JSON.parse(rawContent);
|
|
@@ -113905,6 +113942,7 @@ var StreamingCard = class {
|
|
|
113905
113942
|
textBuffer = "";
|
|
113906
113943
|
toolStates = /* @__PURE__ */ new Map();
|
|
113907
113944
|
closed = false;
|
|
113945
|
+
toolsElementAdded = false;
|
|
113908
113946
|
/**
|
|
113909
113947
|
* 创建卡片 + 发送 interactive 消息 → messageId
|
|
113910
113948
|
*/
|
|
@@ -113912,15 +113950,14 @@ var StreamingCard = class {
|
|
|
113912
113950
|
const schema = {
|
|
113913
113951
|
data: {
|
|
113914
113952
|
schema: "2.0",
|
|
113915
|
-
config: { streaming_mode: true
|
|
113953
|
+
config: { streaming_mode: true },
|
|
113916
113954
|
header: {
|
|
113917
113955
|
title: { tag: "plain_text", content: "AI \u56DE\u590D" },
|
|
113918
113956
|
template: "blue"
|
|
113919
113957
|
},
|
|
113920
113958
|
body: {
|
|
113921
113959
|
elements: [
|
|
113922
|
-
{ tag: "markdown", element_id: "content", content: "\u6B63\u5728\u601D\u8003..." }
|
|
113923
|
-
{ tag: "markdown", element_id: "tools", content: "" }
|
|
113960
|
+
{ tag: "markdown", element_id: "content", content: "\u6B63\u5728\u601D\u8003..." }
|
|
113924
113961
|
]
|
|
113925
113962
|
}
|
|
113926
113963
|
}
|
|
@@ -114010,12 +114047,22 @@ var StreamingCard = class {
|
|
|
114010
114047
|
const icon = ts.state === "completed" ? "\u2705" : ts.state === "error" ? "\u274C" : "\u{1F504}";
|
|
114011
114048
|
lines.push(`${icon} ${ts.tool}`);
|
|
114012
114049
|
}
|
|
114013
|
-
|
|
114014
|
-
|
|
114015
|
-
|
|
114016
|
-
|
|
114017
|
-
|
|
114018
|
-
|
|
114050
|
+
const content = lines.join("\n");
|
|
114051
|
+
if (!this.toolsElementAdded) {
|
|
114052
|
+
this.toolsElementAdded = true;
|
|
114053
|
+
await this.cardkit.addElement(
|
|
114054
|
+
this.cardId,
|
|
114055
|
+
[{ tag: "markdown", element_id: "tools", content }],
|
|
114056
|
+
++this.seq
|
|
114057
|
+
);
|
|
114058
|
+
} else {
|
|
114059
|
+
await this.cardkit.updateElement(
|
|
114060
|
+
this.cardId,
|
|
114061
|
+
"tools",
|
|
114062
|
+
content,
|
|
114063
|
+
++this.seq
|
|
114064
|
+
);
|
|
114065
|
+
}
|
|
114019
114066
|
}
|
|
114020
114067
|
};
|
|
114021
114068
|
|
|
@@ -114043,7 +114090,7 @@ async function handleChat(ctx, deps, signal) {
|
|
|
114043
114090
|
const sessionKey = buildSessionKey(chatType, chatType === "p2p" ? senderId : chatId);
|
|
114044
114091
|
const session = await getOrCreateSession(client, sessionKey, directory);
|
|
114045
114092
|
registerSessionChat(session.id, chatId, chatType);
|
|
114046
|
-
const parts = await buildPromptParts(feishuClient, messageId, messageType, rawContent, content, chatType, senderId, log);
|
|
114093
|
+
const parts = await buildPromptParts(feishuClient, messageId, messageType, rawContent, content, chatType, senderId, log, config2.maxResourceSize);
|
|
114047
114094
|
if (!parts.length) return void 0;
|
|
114048
114095
|
log("info", "\u6536\u5230\u7528\u6237\u6D88\u606F", {
|
|
114049
114096
|
sessionKey,
|
|
@@ -114217,7 +114264,7 @@ async function handleChat(ctx, deps, signal) {
|
|
|
114217
114264
|
unregisterPending(activeSessionId);
|
|
114218
114265
|
}
|
|
114219
114266
|
}
|
|
114220
|
-
async function buildPromptParts(feishuClient, messageId, messageType, rawContent, textContent, chatType, senderId, log) {
|
|
114267
|
+
async function buildPromptParts(feishuClient, messageId, messageType, rawContent, textContent, chatType, senderId, log, maxResourceSize) {
|
|
114221
114268
|
if (messageType === "text") {
|
|
114222
114269
|
let promptText = textContent;
|
|
114223
114270
|
if (chatType === "group" && senderId) {
|
|
@@ -114225,7 +114272,7 @@ async function buildPromptParts(feishuClient, messageId, messageType, rawContent
|
|
|
114225
114272
|
}
|
|
114226
114273
|
return [{ type: "text", text: promptText }];
|
|
114227
114274
|
}
|
|
114228
|
-
const parts = await extractParts(feishuClient, messageId, messageType, rawContent, log);
|
|
114275
|
+
const parts = await extractParts(feishuClient, messageId, messageType, rawContent, log, maxResourceSize);
|
|
114229
114276
|
if (chatType === "group" && senderId && parts.length > 0) {
|
|
114230
114277
|
return [{ type: "text", text: `[${senderId}]:` }, ...parts];
|
|
114231
114278
|
}
|