opencode-feishu 0.6.0 → 0.7.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.
- package/dist/index.js +120 -97
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -99176,64 +99176,27 @@ async function updateMessage(client, messageId, text2) {
|
|
|
99176
99176
|
}
|
|
99177
99177
|
}
|
|
99178
99178
|
|
|
99179
|
-
// src/handler/event.ts
|
|
99180
|
-
var pendingBySession = /* @__PURE__ */ new Map();
|
|
99181
|
-
function registerPending(sessionId, payload) {
|
|
99182
|
-
pendingBySession.set(sessionId, { ...payload, textBuffer: "" });
|
|
99183
|
-
}
|
|
99184
|
-
function unregisterPending(sessionId) {
|
|
99185
|
-
pendingBySession.delete(sessionId);
|
|
99186
|
-
}
|
|
99187
|
-
async function handleEvent(event) {
|
|
99188
|
-
switch (event.type) {
|
|
99189
|
-
case "message.part.updated": {
|
|
99190
|
-
const part = event.properties.part;
|
|
99191
|
-
if (!part) break;
|
|
99192
|
-
const sessionId = part.sessionID;
|
|
99193
|
-
if (!sessionId) break;
|
|
99194
|
-
const payload = pendingBySession.get(sessionId);
|
|
99195
|
-
if (!payload) break;
|
|
99196
|
-
const delta = event.properties.delta;
|
|
99197
|
-
if (delta) {
|
|
99198
|
-
payload.textBuffer += delta;
|
|
99199
|
-
} else {
|
|
99200
|
-
const fullText = extractPartText(part);
|
|
99201
|
-
if (fullText) {
|
|
99202
|
-
payload.textBuffer = fullText;
|
|
99203
|
-
}
|
|
99204
|
-
}
|
|
99205
|
-
if (payload.textBuffer) {
|
|
99206
|
-
const res = await updateMessage(payload.feishuClient, payload.placeholderId, payload.textBuffer.trim());
|
|
99207
|
-
if (!res.ok) ;
|
|
99208
|
-
}
|
|
99209
|
-
break;
|
|
99210
|
-
}
|
|
99211
|
-
case "session.error": {
|
|
99212
|
-
const props = event.properties;
|
|
99213
|
-
const sessionId = props.sessionID;
|
|
99214
|
-
if (!sessionId) break;
|
|
99215
|
-
const payload = pendingBySession.get(sessionId);
|
|
99216
|
-
if (!payload) break;
|
|
99217
|
-
const errMsg = props.error?.message ?? String(props.error);
|
|
99218
|
-
const updateRes = await updateMessage(payload.feishuClient, payload.placeholderId, `\u274C \u4F1A\u8BDD\u9519\u8BEF: ${errMsg}`);
|
|
99219
|
-
if (!updateRes.ok) {
|
|
99220
|
-
await sendTextMessage(payload.feishuClient, payload.chatId, `\u274C \u4F1A\u8BDD\u9519\u8BEF: ${errMsg}`);
|
|
99221
|
-
}
|
|
99222
|
-
break;
|
|
99223
|
-
}
|
|
99224
|
-
}
|
|
99225
|
-
}
|
|
99226
|
-
function extractPartText(part) {
|
|
99227
|
-
if (part.type === "text") return part.text ?? "";
|
|
99228
|
-
if (part.type === "reasoning" && part.text) return `\u{1F914} \u601D\u8003: ${part.text}
|
|
99229
|
-
|
|
99230
|
-
`;
|
|
99231
|
-
return "";
|
|
99232
|
-
}
|
|
99233
|
-
|
|
99234
99179
|
// src/session.ts
|
|
99235
99180
|
var SESSION_KEY_PREFIX = "feishu";
|
|
99236
99181
|
var TITLE_PREFIX = "Feishu";
|
|
99182
|
+
var sessionCache = /* @__PURE__ */ new Map();
|
|
99183
|
+
var sessionIdToKeyCache = /* @__PURE__ */ new Map();
|
|
99184
|
+
function setCachedSession(sessionKey, session) {
|
|
99185
|
+
const oldSession = sessionCache.get(sessionKey);
|
|
99186
|
+
if (oldSession && oldSession.id !== session.id) {
|
|
99187
|
+
sessionIdToKeyCache.delete(oldSession.id);
|
|
99188
|
+
}
|
|
99189
|
+
sessionCache.set(sessionKey, session);
|
|
99190
|
+
sessionIdToKeyCache.set(session.id, sessionKey);
|
|
99191
|
+
}
|
|
99192
|
+
function invalidateCachedSession(sessionId) {
|
|
99193
|
+
const sessionKey = sessionIdToKeyCache.get(sessionId);
|
|
99194
|
+
if (sessionKey) {
|
|
99195
|
+
sessionCache.delete(sessionKey);
|
|
99196
|
+
sessionIdToKeyCache.delete(sessionId);
|
|
99197
|
+
}
|
|
99198
|
+
return sessionKey;
|
|
99199
|
+
}
|
|
99237
99200
|
function buildSessionKey(chatType, id) {
|
|
99238
99201
|
return `${SESSION_KEY_PREFIX}-${chatType}-${id}`;
|
|
99239
99202
|
}
|
|
@@ -99241,6 +99204,8 @@ function generateSessionTitle(sessionKey) {
|
|
|
99241
99204
|
return `${TITLE_PREFIX}-${sessionKey}-${Date.now()}`;
|
|
99242
99205
|
}
|
|
99243
99206
|
async function getOrCreateSession(client, sessionKey, directory) {
|
|
99207
|
+
const cached = sessionCache.get(sessionKey);
|
|
99208
|
+
if (cached) return cached;
|
|
99244
99209
|
const titlePrefix = `${TITLE_PREFIX}-${sessionKey}-`;
|
|
99245
99210
|
const query = directory ? { directory } : void 0;
|
|
99246
99211
|
const { data: sessions } = await client.session.list({ query });
|
|
@@ -99255,7 +99220,11 @@ async function getOrCreateSession(client, sessionKey, directory) {
|
|
|
99255
99220
|
return cb - ca;
|
|
99256
99221
|
});
|
|
99257
99222
|
const best = candidates[0];
|
|
99258
|
-
if (best?.id)
|
|
99223
|
+
if (best?.id) {
|
|
99224
|
+
const session2 = { id: best.id, title: best.title };
|
|
99225
|
+
setCachedSession(sessionKey, session2);
|
|
99226
|
+
return session2;
|
|
99227
|
+
}
|
|
99259
99228
|
}
|
|
99260
99229
|
}
|
|
99261
99230
|
const title = generateSessionTitle(sessionKey);
|
|
@@ -99266,7 +99235,9 @@ async function getOrCreateSession(client, sessionKey, directory) {
|
|
|
99266
99235
|
`\u521B\u5EFA OpenCode \u4F1A\u8BDD\u5931\u8D25: ${err ? JSON.stringify(err) : "unknown"}`
|
|
99267
99236
|
);
|
|
99268
99237
|
}
|
|
99269
|
-
|
|
99238
|
+
const session = { id: createResp.data.id, title: createResp.data.title };
|
|
99239
|
+
setCachedSession(sessionKey, session);
|
|
99240
|
+
return session;
|
|
99270
99241
|
}
|
|
99271
99242
|
async function forkSession(client, oldSessionId, sessionKey, directory) {
|
|
99272
99243
|
const query = directory ? { directory } : void 0;
|
|
@@ -99296,6 +99267,92 @@ async function forkSession(client, oldSessionId, sessionKey, directory) {
|
|
|
99296
99267
|
return { id: resp.data.id, title };
|
|
99297
99268
|
}
|
|
99298
99269
|
|
|
99270
|
+
// src/handler/event.ts
|
|
99271
|
+
var pendingBySession = /* @__PURE__ */ new Map();
|
|
99272
|
+
function registerPending(sessionId, payload) {
|
|
99273
|
+
pendingBySession.set(sessionId, { ...payload, textBuffer: "" });
|
|
99274
|
+
}
|
|
99275
|
+
function unregisterPending(sessionId) {
|
|
99276
|
+
pendingBySession.delete(sessionId);
|
|
99277
|
+
}
|
|
99278
|
+
function migratePending(oldSessionId, newSessionId) {
|
|
99279
|
+
const payload = pendingBySession.get(oldSessionId);
|
|
99280
|
+
if (payload) {
|
|
99281
|
+
pendingBySession.delete(oldSessionId);
|
|
99282
|
+
pendingBySession.set(newSessionId, payload);
|
|
99283
|
+
}
|
|
99284
|
+
}
|
|
99285
|
+
function isModelError(errMsg) {
|
|
99286
|
+
return errMsg.includes("ModelNotFound") || errMsg.includes("ProviderModelNotFound");
|
|
99287
|
+
}
|
|
99288
|
+
async function handleEvent(event, deps) {
|
|
99289
|
+
switch (event.type) {
|
|
99290
|
+
case "message.part.updated": {
|
|
99291
|
+
const part = event.properties.part;
|
|
99292
|
+
if (!part) break;
|
|
99293
|
+
const sessionId = part.sessionID;
|
|
99294
|
+
if (!sessionId) break;
|
|
99295
|
+
const payload = pendingBySession.get(sessionId);
|
|
99296
|
+
if (!payload) break;
|
|
99297
|
+
const delta = event.properties.delta;
|
|
99298
|
+
if (delta) {
|
|
99299
|
+
payload.textBuffer += delta;
|
|
99300
|
+
} else {
|
|
99301
|
+
const fullText = extractPartText(part);
|
|
99302
|
+
if (fullText) {
|
|
99303
|
+
payload.textBuffer = fullText;
|
|
99304
|
+
}
|
|
99305
|
+
}
|
|
99306
|
+
if (payload.textBuffer) {
|
|
99307
|
+
const res = await updateMessage(payload.feishuClient, payload.placeholderId, payload.textBuffer.trim());
|
|
99308
|
+
if (!res.ok) ;
|
|
99309
|
+
}
|
|
99310
|
+
break;
|
|
99311
|
+
}
|
|
99312
|
+
case "session.error": {
|
|
99313
|
+
const props = event.properties;
|
|
99314
|
+
const sessionId = props.sessionID;
|
|
99315
|
+
if (!sessionId) break;
|
|
99316
|
+
const errMsg = props.error?.message ?? String(props.error);
|
|
99317
|
+
if (isModelError(errMsg)) {
|
|
99318
|
+
const sessionKey = invalidateCachedSession(sessionId);
|
|
99319
|
+
if (sessionKey) {
|
|
99320
|
+
try {
|
|
99321
|
+
const newSession = await forkSession(deps.client, sessionId, sessionKey, deps.directory);
|
|
99322
|
+
setCachedSession(sessionKey, newSession);
|
|
99323
|
+
deps.log("warn", "\u6A21\u578B\u4E0D\u517C\u5BB9\uFF0C\u5DF2\u4E3B\u52A8 fork \u4F1A\u8BDD", {
|
|
99324
|
+
oldSessionId: sessionId,
|
|
99325
|
+
newSessionId: newSession.id,
|
|
99326
|
+
sessionKey
|
|
99327
|
+
});
|
|
99328
|
+
migratePending(sessionId, newSession.id);
|
|
99329
|
+
} catch (forkErr) {
|
|
99330
|
+
deps.log("error", "\u4E3B\u52A8 fork \u5931\u8D25", {
|
|
99331
|
+
sessionId,
|
|
99332
|
+
sessionKey,
|
|
99333
|
+
error: forkErr instanceof Error ? forkErr.message : String(forkErr)
|
|
99334
|
+
});
|
|
99335
|
+
}
|
|
99336
|
+
}
|
|
99337
|
+
}
|
|
99338
|
+
const payload = pendingBySession.get(sessionId);
|
|
99339
|
+
if (!payload) break;
|
|
99340
|
+
const updateRes = await updateMessage(payload.feishuClient, payload.placeholderId, `\u274C \u4F1A\u8BDD\u9519\u8BEF: ${errMsg}`);
|
|
99341
|
+
if (!updateRes.ok) {
|
|
99342
|
+
await sendTextMessage(payload.feishuClient, payload.chatId, `\u274C \u4F1A\u8BDD\u9519\u8BEF: ${errMsg}`);
|
|
99343
|
+
}
|
|
99344
|
+
break;
|
|
99345
|
+
}
|
|
99346
|
+
}
|
|
99347
|
+
}
|
|
99348
|
+
function extractPartText(part) {
|
|
99349
|
+
if (part.type === "text") return part.text ?? "";
|
|
99350
|
+
if (part.type === "reasoning" && part.text) return `\u{1F914} \u601D\u8003: ${part.text}
|
|
99351
|
+
|
|
99352
|
+
`;
|
|
99353
|
+
return "";
|
|
99354
|
+
}
|
|
99355
|
+
|
|
99299
99356
|
// src/handler/chat.ts
|
|
99300
99357
|
var activeAutoPrompts = /* @__PURE__ */ new Map();
|
|
99301
99358
|
async function handleChat(ctx, deps) {
|
|
@@ -99310,18 +99367,14 @@ async function handleChat(ctx, deps) {
|
|
|
99310
99367
|
activeAutoPrompts.delete(sessionKey);
|
|
99311
99368
|
log("info", "\u7528\u6237\u4ECB\u5165\uFF0C\u81EA\u52A8\u63D0\u793A\u5DF2\u4E2D\u65AD", { sessionKey });
|
|
99312
99369
|
}
|
|
99313
|
-
|
|
99370
|
+
const session = await getOrCreateSession(client, sessionKey, directory);
|
|
99314
99371
|
const parts = await buildPromptParts(feishuClient, messageId, messageType, rawContent, content, chatType, senderId, log);
|
|
99315
99372
|
if (!parts.length) return;
|
|
99316
99373
|
if (!shouldReply) {
|
|
99317
99374
|
try {
|
|
99318
|
-
|
|
99319
|
-
|
|
99320
|
-
session,
|
|
99321
|
-
sessionKey,
|
|
99322
|
-
directory,
|
|
99375
|
+
await client.session.prompt({
|
|
99376
|
+
path: { id: session.id },
|
|
99323
99377
|
query,
|
|
99324
|
-
log,
|
|
99325
99378
|
body: { parts, noReply: true }
|
|
99326
99379
|
});
|
|
99327
99380
|
} catch (err) {
|
|
@@ -99337,7 +99390,6 @@ async function handleChat(ctx, deps) {
|
|
|
99337
99390
|
const stablePolls = config.stablePolls;
|
|
99338
99391
|
let placeholderId = "";
|
|
99339
99392
|
let done = false;
|
|
99340
|
-
let oldSessionId = session.id;
|
|
99341
99393
|
const timer = thinkingDelay > 0 ? setTimeout(async () => {
|
|
99342
99394
|
if (done) return;
|
|
99343
99395
|
try {
|
|
@@ -99355,21 +99407,11 @@ async function handleChat(ctx, deps) {
|
|
|
99355
99407
|
}
|
|
99356
99408
|
}, thinkingDelay) : null;
|
|
99357
99409
|
try {
|
|
99358
|
-
|
|
99359
|
-
|
|
99360
|
-
session,
|
|
99361
|
-
sessionKey,
|
|
99362
|
-
directory,
|
|
99410
|
+
await client.session.prompt({
|
|
99411
|
+
path: { id: session.id },
|
|
99363
99412
|
query,
|
|
99364
|
-
log,
|
|
99365
99413
|
body: { parts }
|
|
99366
99414
|
});
|
|
99367
|
-
if (session.id !== oldSessionId) {
|
|
99368
|
-
unregisterPending(oldSessionId);
|
|
99369
|
-
if (placeholderId) {
|
|
99370
|
-
registerPending(session.id, { chatId, placeholderId, feishuClient });
|
|
99371
|
-
}
|
|
99372
|
-
}
|
|
99373
99415
|
const finalText = await pollForResponse(client, session.id, { timeout, pollInterval, stablePolls, query });
|
|
99374
99416
|
await replyOrUpdate(feishuClient, chatId, placeholderId, finalText || "\u26A0\uFE0F \u54CD\u5E94\u8D85\u65F6");
|
|
99375
99417
|
const { autoPrompt } = config;
|
|
@@ -99415,7 +99457,6 @@ async function handleChat(ctx, deps) {
|
|
|
99415
99457
|
done = true;
|
|
99416
99458
|
if (timer) clearTimeout(timer);
|
|
99417
99459
|
unregisterPending(session.id);
|
|
99418
|
-
if (session.id !== oldSessionId) unregisterPending(oldSessionId);
|
|
99419
99460
|
}
|
|
99420
99461
|
}
|
|
99421
99462
|
async function buildPromptParts(feishuClient, messageId, messageType, rawContent, textContent, chatType, senderId, log) {
|
|
@@ -99487,24 +99528,6 @@ function abortableSleep(ms, signal) {
|
|
|
99487
99528
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
99488
99529
|
});
|
|
99489
99530
|
}
|
|
99490
|
-
function isModelNotFoundError(err) {
|
|
99491
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
99492
|
-
return msg.includes("ProviderModelNotFound") || msg.includes("ModelNotFound");
|
|
99493
|
-
}
|
|
99494
|
-
async function promptWithForkRecovery(opts) {
|
|
99495
|
-
const { client, sessionKey, directory, query, log, body } = opts;
|
|
99496
|
-
let { session } = opts;
|
|
99497
|
-
const doPrompt = (s) => client.session.prompt({ path: { id: s.id }, query, body });
|
|
99498
|
-
try {
|
|
99499
|
-
await doPrompt(session);
|
|
99500
|
-
} catch (err) {
|
|
99501
|
-
if (!isModelNotFoundError(err)) throw err;
|
|
99502
|
-
log("warn", "\u4F1A\u8BDD\u6A21\u578B\u4E0D\u517C\u5BB9\uFF0Cfork \u65E7\u4F1A\u8BDD\u91CD\u8BD5", { oldSessionId: session.id });
|
|
99503
|
-
session = await forkSession(client, session.id, sessionKey, directory);
|
|
99504
|
-
await doPrompt(session);
|
|
99505
|
-
}
|
|
99506
|
-
return session;
|
|
99507
|
-
}
|
|
99508
99531
|
function extractLastAssistantText(messages) {
|
|
99509
99532
|
const assistant = messages.filter((m) => m.info?.role === "assistant").pop();
|
|
99510
99533
|
const parts = assistant?.parts ?? [];
|
|
@@ -99707,7 +99730,7 @@ var FeishuPlugin = async (ctx) => {
|
|
|
99707
99730
|
const hooks = {
|
|
99708
99731
|
event: async ({ event }) => {
|
|
99709
99732
|
if (!gateway) return;
|
|
99710
|
-
await handleEvent(event);
|
|
99733
|
+
await handleEvent(event, { client, log, directory: resolvedConfig.directory });
|
|
99711
99734
|
}
|
|
99712
99735
|
};
|
|
99713
99736
|
return hooks;
|