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 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) return { id: best.id, title: best.title };
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
- return { id: createResp.data.id, title: createResp.data.title };
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
- let session = await getOrCreateSession(client, sessionKey, directory);
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
- session = await promptWithForkRecovery({
99319
- client,
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
- session = await promptWithForkRecovery({
99359
- client,
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;