eve-lark 0.2.4 → 0.2.6

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 CHANGED
@@ -173,6 +173,18 @@ interface LarkCard {
173
173
  elements: LarkCardElement[];
174
174
  }
175
175
  type LarkCardElement = {
176
+ tag: "div";
177
+ text: {
178
+ tag: "lark_md";
179
+ content: string;
180
+ };
181
+ } | {
182
+ tag: "div";
183
+ text: {
184
+ tag: "plain_text";
185
+ content: string;
186
+ };
187
+ } | {
176
188
  tag: "markdown";
177
189
  content: string;
178
190
  } | {
package/dist/index.js CHANGED
@@ -524,7 +524,7 @@ var BASE_CONFIG = {
524
524
  function buildTextCard(text) {
525
525
  return {
526
526
  config: { ...BASE_CONFIG },
527
- elements: [{ tag: "markdown", content: text }]
527
+ elements: [{ tag: "div", text: { tag: "lark_md", content: text } }]
528
528
  };
529
529
  }
530
530
  __name(buildTextCard, "buildTextCard");
@@ -536,7 +536,7 @@ function buildStreamingCard(opts) {
536
536
  lines.push(opts.buffer.length > 0 ? opts.buffer : "_\u2026_");
537
537
  return {
538
538
  config: { ...BASE_CONFIG },
539
- elements: [{ tag: "markdown", content: lines.join("\n\n") }]
539
+ elements: [{ tag: "div", text: { tag: "lark_md", content: lines.join("\n\n") } }]
540
540
  };
541
541
  }
542
542
  __name(buildStreamingCard, "buildStreamingCard");
@@ -544,7 +544,7 @@ function buildErrorCard(message) {
544
544
  return {
545
545
  config: { ...BASE_CONFIG },
546
546
  elements: [
547
- { tag: "markdown", content: `<font color='red'>\u26A0 ${message}</font>` }
547
+ { tag: "div", text: { tag: "lark_md", content: `<font color='red'>\u26A0 ${message}</font>` } }
548
548
  ]
549
549
  };
550
550
  }
@@ -901,13 +901,26 @@ function rebuildEnvelopeFromSdkEvent(eventType, data, ctx) {
901
901
  };
902
902
  }
903
903
  __name(rebuildEnvelopeFromSdkEvent, "rebuildEnvelopeFromSdkEvent");
904
- var activeConnections = /* @__PURE__ */ new Map();
904
+ var GLOBAL_KEY = "__eveLarkActiveConnections";
905
+ function getActiveConnections() {
906
+ const g = globalThis;
907
+ if (!g[GLOBAL_KEY]) g[GLOBAL_KEY] = /* @__PURE__ */ new Map();
908
+ return g[GLOBAL_KEY];
909
+ }
910
+ __name(getActiveConnections, "getActiveConnections");
905
911
  async function startLongConnection(args) {
906
912
  const key = `${args.resolved.appId}:${args.eveWebhookUrl}`;
913
+ const activeConnections = getActiveConnections();
907
914
  const existing = activeConnections.get(key);
908
915
  if (existing) {
916
+ console.log(
917
+ `[eve-lark] startLongConnection: skip (already running) key=${key} pid=${process.pid}`
918
+ );
909
919
  return;
910
920
  }
921
+ console.log(
922
+ `[eve-lark] startLongConnection: start new WSClient key=${key} pid=${process.pid}`
923
+ );
911
924
  const promise = (async () => {
912
925
  await doStartLongConnection(args);
913
926
  })().catch((e) => {
@@ -1094,10 +1107,11 @@ function createLarkChannel(optionsInput) {
1094
1107
  const ctrl = controllers.get(sessionId) ?? getController(sessionId, info);
1095
1108
  try {
1096
1109
  await ctrl.finalize(text);
1110
+ console.log(`[eve-lark] delivered via streaming finalize (sessionId=${sessionId})`);
1097
1111
  return;
1098
1112
  } catch (e) {
1099
1113
  console.warn(
1100
- "[eve-lark] streaming finalize failed; falling back to fresh card:",
1114
+ `[eve-lark] streaming finalize failed; falling back to fresh card (sessionId=${sessionId}):`,
1101
1115
  e instanceof Error ? e.message : e
1102
1116
  );
1103
1117
  }
@@ -1109,10 +1123,11 @@ function createLarkChannel(optionsInput) {
1109
1123
  rootId: info.rootId,
1110
1124
  parentId: info.parentId
1111
1125
  });
1126
+ console.log(`[eve-lark] delivered via sendCard (sessionId=${sessionId})`);
1112
1127
  return;
1113
1128
  } catch (cardErr) {
1114
1129
  console.warn(
1115
- "[eve-lark] sendCard failed; falling back to plain text:",
1130
+ `[eve-lark] sendCard failed; falling back to plain text (sessionId=${sessionId}):`,
1116
1131
  cardErr instanceof Error ? cardErr.message : cardErr
1117
1132
  );
1118
1133
  }
@@ -1123,9 +1138,10 @@ function createLarkChannel(optionsInput) {
1123
1138
  rootId: info.rootId,
1124
1139
  parentId: info.parentId
1125
1140
  });
1141
+ console.log(`[eve-lark] delivered via sendText fallback (sessionId=${sessionId})`);
1126
1142
  } catch (textErr) {
1127
1143
  console.error(
1128
- "[eve-lark] sendText fallback ALSO failed; the user will not see this reply:",
1144
+ `[eve-lark] sendText fallback ALSO failed; the user will not see this reply (sessionId=${sessionId}):`,
1129
1145
  textErr instanceof Error ? textErr.message : textErr
1130
1146
  );
1131
1147
  }
@@ -1282,9 +1298,15 @@ function createLarkChannel(optionsInput) {
1282
1298
  async "message.completed"(data, _channel, ctx) {
1283
1299
  const sessionId = ctx.session.id;
1284
1300
  const info = sessionInfoFromCtx(ctx);
1285
- if (!info) return;
1301
+ if (!info) {
1302
+ console.warn(`[eve-lark] message.completed: no session info, cannot deliver (sessionId=${sessionId})`);
1303
+ return;
1304
+ }
1286
1305
  const d = data;
1287
1306
  const rawText = typeof d.message === "string" ? d.message : "";
1307
+ console.log(
1308
+ `[eve-lark] message.completed sessionId=${sessionId} chatId=${info.chatId} msgLen=${rawText.length}`
1309
+ );
1288
1310
  const text = rawText.length > 0 ? rawText : EMPTY_REPLY_TEXT;
1289
1311
  try {
1290
1312
  await deliverReply(sessionId, info, text);
@@ -1295,18 +1317,28 @@ function createLarkChannel(optionsInput) {
1295
1317
  },
1296
1318
  async "turn.failed"(data, _channel, ctx) {
1297
1319
  const sessionId = ctx?.session?.id;
1298
- if (!sessionId) return;
1320
+ if (!sessionId) {
1321
+ console.warn("[eve-lark] turn.failed: no sessionId on ctx");
1322
+ return;
1323
+ }
1299
1324
  const info = sessionInfoFromCtx(ctx);
1300
- if (!info) return;
1325
+ if (!info) {
1326
+ console.warn(`[eve-lark] turn.failed: no session info (sessionId=${sessionId})`);
1327
+ return;
1328
+ }
1301
1329
  const errMsg = errMsgFrom(data, "turn failed");
1330
+ console.warn(
1331
+ `[eve-lark] turn.failed sessionId=${sessionId} chatId=${info.chatId} err="${errMsg.slice(0, 200)}"`
1332
+ );
1302
1333
  const userText = `\u26A0 ${errMsg}`;
1303
1334
  const ctrl = controllers.get(sessionId);
1304
1335
  if (ctrl) {
1305
1336
  try {
1306
1337
  await ctrl.abort(errMsg);
1338
+ console.log(`[eve-lark] error shown via streaming abort (sessionId=${sessionId})`);
1307
1339
  } catch (e) {
1308
1340
  console.warn(
1309
- "[eve-lark] turn.failed: streaming abort failed, will deliver fresh error:",
1341
+ `[eve-lark] turn.failed: streaming abort failed, will deliver fresh error (sessionId=${sessionId}):`,
1310
1342
  e instanceof Error ? e.message : e
1311
1343
  );
1312
1344
  try {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/channel.ts","../src/errors.ts","../src/lark-client.ts","../src/dedup.ts","../src/crypto.ts","../src/parse.ts","../src/card.ts","../src/streaming-controller.ts","../src/options.ts","../src/long-connection.ts"],"sourcesContent":["import {\n defineChannel,\n POST,\n type Channel,\n type RouteHandlerArgs,\n} from \"eve/channels\";\n\nimport { LarkClient } from \"./lark-client.js\";\nimport { DedupMap } from \"./dedup.js\";\nimport { decryptPayload, verifySignature } from \"./crypto.js\";\nimport { parseInbound } from \"./parse.js\";\nimport { StreamingCardController } from \"./streaming-controller.js\";\nimport { buildTextCard } from \"./card.js\";\nimport { resolveOptions } from \"./options.js\";\nimport { startLongConnection } from \"./long-connection.js\";\nimport type {\n LarkChannelOptions,\n LarkContinuationToken,\n LarkEncryptedBody,\n LarkEventBody,\n LarkInboundFile,\n ResolvedLarkOptions,\n} from \"./types.js\";\n\n/** Hard cap on inbound webhook body size. Feishu payloads are <10 KB; this\n * is purely defense against a malicious or buggy peer OOMing the process. */\nconst MAX_BODY_BYTES = 1_000_000;\n\n/** Drop a session's controller if it's been inactive this long. Bounds the\n * closure-scoped `controllers`/`sessionMeta` Maps against crashes that\n * prevent `message.completed`/`turn.failed` from firing. */\nconst STALE_SESSION_MS = 30 * 60 * 1000;\n\n/** How often to sweep stale controllers. */\nconst SWEEP_INTERVAL_MS = 5 * 60 * 1000;\n\n/** Reply text used when the model returns null/empty — guarantees the user\n * always sees *something* so they're not left looking at a typing emoji. */\nconst EMPTY_REPLY_TEXT = \"(model returned no content)\";\n\n/**\n * Continuation token format: `${chatId}:${rootMessageId ?? \"_\"}`.\n * The framework prepends the channel file stem before handing the token to\n * the runtime; consumers should call this helper rather than concatenate.\n */\nexport function larkContinuationToken(\n chatId: string,\n rootMessageId: string | null,\n): LarkContinuationToken {\n return `${chatId}:${rootMessageId ?? \"_\"}` as LarkContinuationToken;\n}\n\ninterface LarkSessionMeta {\n chatId: string;\n rootId?: string | undefined;\n parentId?: string | undefined;\n /** The user message we ack-reacted to; needed to remove the reaction\n * after delivery. Same value as `ctx.session.auth.initiator.attributes.messageId`,\n * mirrored here so terminal handlers don't have to re-extract it. */\n messageId?: string | undefined;\n /** Reaction id returned by `addReaction`. Present once the ack-reaction\n * POST resolves, which may be after the first terminal event fires. */\n ackReactionId?: string | undefined;\n /** When the controller was last touched. Used by the stale-sweep. */\n touchedAt: number;\n}\n\ninterface ResolvedSessionInfo {\n chatId: string;\n rootId?: string | undefined;\n parentId?: string | undefined;\n messageId?: string | undefined;\n}\n\n/**\n * Extract chat + message metadata stashed on `auth.initiator.attributes` at\n * session start. This is the canonical place to read it: closure state\n * doesn't reliably cross eve's process/worker boundary, but auth attributes\n * are persisted with the session.\n */\nfunction sessionInfoFromCtx(ctx: { session?: { auth?: { initiator?: { attributes?: unknown } | null } | null } | null }): ResolvedSessionInfo | null {\n const attrs = (ctx.session?.auth?.initiator?.attributes ?? {}) as {\n chatId?: unknown;\n rootMessageId?: unknown;\n parentId?: unknown;\n messageId?: unknown;\n };\n if (typeof attrs.chatId !== \"string\" || !attrs.chatId) return null;\n return {\n chatId: attrs.chatId,\n rootId: typeof attrs.rootMessageId === \"string\" ? attrs.rootMessageId : undefined,\n parentId: typeof attrs.parentId === \"string\" ? attrs.parentId : undefined,\n messageId: typeof attrs.messageId === \"string\" ? attrs.messageId : undefined,\n };\n}\n\nfunction ackOk(): Response {\n return Response.json({ code: 0 });\n}\n\n/**\n * Resolve the configured `ackReaction` to a single emoji type for this event,\n * or `false` if reactions are disabled. Picks randomly when given an array.\n */\nfunction pickAckEmoji(reaction: string | readonly string[] | false): string | false {\n if (typeof reaction === \"string\") return reaction;\n if (Array.isArray(reaction)) {\n if (reaction.length === 0) return false;\n const idx = Math.floor(Math.random() * reaction.length);\n return reaction[idx] ?? false;\n }\n return false;\n}\n\nfunction resourceUrl(\n options: ResolvedLarkOptions,\n file: LarkInboundFile,\n messageId: string,\n): string {\n const type = file.kind === \"image\" ? \"image\" : \"file\";\n return `${options.baseUrl}/open-apis/im/v1/messages/${encodeURIComponent(messageId)}/resources/${encodeURIComponent(file.fileKey)}?type=${type}`;\n}\n\n/**\n * Build the eve UserContent payload from a parsed inbound event. Text comes\n * first; each inbound image/file becomes a `file` part carrying a URL pointing\n * at the Lark resource endpoint. The channel's `fetchFile` hook will stage\n * those URLs to bytes when the model runs.\n */\nfunction buildUserContent(\n text: string,\n files: LarkInboundFile[],\n options: ResolvedLarkOptions,\n messageId: string,\n): unknown[] {\n const parts: unknown[] = [];\n if (text.length > 0) parts.push({ type: \"text\", text });\n for (const f of files) {\n parts.push({\n type: \"file\",\n data: new URL(resourceUrl(options, f, messageId)),\n mediaType: f.mediaType,\n });\n }\n return parts;\n}\n\nfunction errMsgFrom(data: unknown, fallback: string): string {\n if (typeof data !== \"object\" || data === null) return fallback;\n const err = (data as { error?: unknown }).error;\n if (typeof err === \"string\") return err;\n if (typeof err === \"object\" && err !== null) {\n const msg = (err as { message?: unknown }).message;\n if (typeof msg === \"string\") return msg;\n }\n return fallback;\n}\n\n/**\n * Create a Lark/Feishu channel for the eve agent framework.\n *\n * The channel mounts a single POST webhook that verifies the request,\n * decrypts the body when an encrypt key is configured, deduplicates events\n * by id, parses the inbound message, and starts or resumes an eve session.\n *\n * Streaming happens via eve's native channel events: `message.appended`\n * drives live card patches, `message.completed` finalizes the card, and\n * `turn.failed` aborts it. In `replyMode: \"static\"` the controller is\n * skipped and `message.completed` delivers a single card.\n *\n * **Delivery guarantee**: every terminal event (`message.completed` or\n * `turn.failed`) delivers *something* to the user. If the streaming card\n * path fails, we fall back to a fresh card; if that fails, plain text; if\n * even that fails, the error is logged. The user is never left looking at\n * a typing-emoji reaction with no reply.\n */\nexport function createLarkChannel(\n optionsInput: LarkChannelOptions,\n): Channel<undefined, Record<string, unknown>, Record<string, unknown>> {\n const options = resolveOptions(optionsInput);\n const client = new LarkClient(options);\n const dedup = new DedupMap(options.dedupTtlMs, options.dedupMaxEntries);\n\n // Long-connection side effect: when mode is \"long-connection\" (the\n // default), start a Feishu WSClient in the background. Each inbound event\n // is re-signed and POSTed to this channel's webhook on localhost, where\n // the standard handler runs with full access to send() etc.\n if (options.mode === \"long-connection\") {\n const eveWebhookUrl = `http://127.0.0.1:${options.port}${options.webhookPath}`;\n void startLongConnection({ resolved: options, eveWebhookUrl }).catch((e) => {\n console.error(\"[eve-lark] long-connection startup failed:\", e);\n });\n }\n\n // Channel-scoped (closure) state. Each session has its own controller +\n // chat metadata, keyed by session.id. Bounded by the stale-sweep below.\n const controllers = new Map<string, StreamingCardController>();\n const sessionMeta = new Map<string, LarkSessionMeta>();\n\n function getController(sessionId: string, meta: ResolvedSessionInfo): StreamingCardController {\n let ctrl = controllers.get(sessionId);\n if (!ctrl) {\n ctrl = new StreamingCardController(client, {\n chatId: meta.chatId,\n rootId: meta.rootId,\n parentId: meta.parentId,\n patchIntervalMs: options.streamPatchIntervalMs,\n createThresholdMs: options.streamCreateThresholdMs,\n });\n controllers.set(sessionId, ctrl);\n }\n if (sessionMeta.has(sessionId)) {\n sessionMeta.get(sessionId)!.touchedAt = Date.now();\n } else {\n sessionMeta.set(sessionId, {\n chatId: meta.chatId,\n rootId: meta.rootId,\n parentId: meta.parentId,\n messageId: meta.messageId,\n touchedAt: Date.now(),\n });\n }\n return ctrl;\n }\n\n function dropController(sessionId: string): void {\n controllers.delete(sessionId);\n sessionMeta.delete(sessionId);\n }\n\n /** Best-effort ack-reaction cleanup. Called from terminal handlers. */\n async function cleanupAckReaction(sessionId: string): Promise<void> {\n const meta = sessionMeta.get(sessionId);\n if (!meta?.ackReactionId || !meta.messageId) return;\n try {\n await client.removeReaction({\n messageId: meta.messageId,\n reactionId: meta.ackReactionId,\n });\n } catch (e) {\n console.warn(\n \"[eve-lark] ack reaction cleanup failed:\",\n e instanceof Error ? e.message : e,\n );\n }\n }\n\n /**\n * Cascade-deliver a reply to the user. Tries (in order):\n * 1. streaming controller finalize (patches existing card OR creates one\n * with the full text — used when message.appended already started a\n * card, or as a one-shot in streaming mode for very short turns)\n * 2. fresh sendCard (static mode, or streaming finalize failed)\n * 3. sendText (card POST rejected)\n * Each failure logs; we never throw out of here.\n */\n async function deliverReply(sessionId: string, info: ResolvedSessionInfo, text: string): Promise<void> {\n if (options.replyMode === \"streaming\") {\n const ctrl = controllers.get(sessionId) ?? getController(sessionId, info);\n try {\n await ctrl.finalize(text);\n return;\n } catch (e) {\n console.warn(\n \"[eve-lark] streaming finalize failed; falling back to fresh card:\",\n e instanceof Error ? e.message : e,\n );\n }\n }\n\n try {\n await client.sendCard({\n chatId: info.chatId,\n card: buildTextCard(text),\n rootId: info.rootId,\n parentId: info.parentId,\n });\n return;\n } catch (cardErr) {\n console.warn(\n \"[eve-lark] sendCard failed; falling back to plain text:\",\n cardErr instanceof Error ? cardErr.message : cardErr,\n );\n }\n\n try {\n await client.sendText({\n chatId: info.chatId,\n content: text,\n rootId: info.rootId,\n parentId: info.parentId,\n });\n } catch (textErr) {\n console.error(\n \"[eve-lark] sendText fallback ALSO failed; the user will not see this reply:\",\n textErr instanceof Error ? textErr.message : textErr,\n );\n }\n }\n\n // Lazy sweep: drop controllers whose session hasn't been touched in\n // STALE_SESSION_MS. Guards against the case where eve crashes mid-turn.\n let lastSweepAt = 0;\n function maybeSweep(): void {\n const now = Date.now();\n if (now - lastSweepAt < SWEEP_INTERVAL_MS) return;\n lastSweepAt = now;\n const cutoff = now - STALE_SESSION_MS;\n for (const [id, meta] of sessionMeta) {\n if (meta.touchedAt < cutoff) {\n controllers.delete(id);\n sessionMeta.delete(id);\n }\n }\n }\n\n const webhookHandler = async (\n req: Request,\n helpers: RouteHandlerArgs[\"send\"] extends never ? never : RouteHandlerArgs,\n ): Promise<Response> => {\n maybeSweep();\n\n // 0) Body size cap — refuse gigantic bodies before allocating.\n const contentLength = Number(req.headers.get(\"content-length\") ?? \"0\");\n if (Number.isFinite(contentLength) && contentLength > MAX_BODY_BYTES) {\n return new Response(\"request body too large\", { status: 413 });\n }\n const rawBody = Buffer.from(await req.arrayBuffer());\n if (rawBody.byteLength > MAX_BODY_BYTES) {\n return new Response(\"request body too large\", { status: 413 });\n }\n\n // 1) Skew check (only enforced when a real timestamp header is present)\n const tsHeader = req.headers.get(\"x-lark-request-timestamp\") ?? \"\";\n const ts = Number(tsHeader);\n if (\n tsHeader &&\n Number.isFinite(ts) &&\n ts > 0 &&\n Math.abs(Date.now() / 1000 - ts) > options.signatureSkewMs / 1000\n ) {\n return new Response(\"request timestamp out of skew window\", { status: 408 });\n }\n\n // 2) Signature verify + AES decrypt when encryptKey configured\n let workingBody: Buffer = rawBody;\n if (options.encryptKey) {\n const nonce = req.headers.get(\"x-lark-request-nonce\") ?? \"\";\n const sigHeader = req.headers.get(\"x-lark-signature\");\n if (!sigHeader) return new Response(\"missing signature\", { status: 401 });\n const ok = verifySignature({\n timestamp: tsHeader,\n nonce,\n encryptKey: options.encryptKey,\n rawBody,\n signatureHeader: sigHeader,\n });\n if (!ok) return new Response(\"bad signature\", { status: 401 });\n\n try {\n const envelope = JSON.parse(rawBody.toString(\"utf8\")) as LarkEncryptedBody;\n if (envelope.encrypt) {\n workingBody = decryptPayload(envelope.encrypt, options.encryptKey) as Buffer;\n }\n } catch {\n return new Response(\"decrypt failed\", { status: 400 });\n }\n }\n\n // 3) Parse body\n let body: LarkEventBody;\n try {\n body = JSON.parse(workingBody.toString(\"utf8\")) as LarkEventBody;\n } catch {\n return new Response(\"invalid json\", { status: 400 });\n }\n\n // 4) url_verification short-circuit\n if (body.type === \"url_verification\") {\n return Response.json({ challenge: body.challenge ?? \"\" });\n }\n\n // 5) Schema check\n if (body.schema !== \"2.0\") {\n return ackOk();\n }\n\n // 6) Verification-token check\n if (body.header?.token !== options.verificationToken) {\n return new Response(\"verification token mismatch\", { status: 401 });\n }\n\n // 7) Dedup\n const dedupKey = body.header?.event_id ?? body.event?.message?.message_id;\n if (dedupKey) {\n if (dedup.has(dedupKey)) return ackOk();\n dedup.set(dedupKey);\n }\n\n // 8) Event filter — only handle text messages in v1\n if (body.header?.event_type !== \"im.message.receive_v1\") {\n return ackOk();\n }\n if (!body.event) return ackOk();\n\n // 9) Parse\n const parsed = parseInbound(body.event, options.botOpenId);\n\n // 10) Self-message suppression\n if (parsed.senderType === \"app\") {\n return ackOk();\n }\n\n // 11) Skip unsupported message types\n if (parsed.text === \"\" && parsed.files.length === 0) {\n return ackOk();\n }\n\n // 12) Build session inputs\n const userContent = buildUserContent(parsed.text, parsed.files, options, parsed.messageId);\n const continuationToken = larkContinuationToken(parsed.chatId, parsed.parentId ?? parsed.rootId);\n const auth = {\n authenticator: \"lark\",\n principalType: \"user\",\n principalId: parsed.senderOpenId,\n attributes: {\n chatId: parsed.chatId,\n rootMessageId: parsed.rootId,\n messageId: parsed.messageId,\n chatType: parsed.chatType,\n },\n };\n\n // 13) Start/resume session.\n const session = await helpers.send(userContent as never, {\n auth: auth as never,\n continuationToken,\n });\n\n // 14) Remember chat metadata keyed by session.id so terminal handlers\n // can look up where to deliver replies and which reaction to clean up.\n sessionMeta.set(session.id, {\n chatId: parsed.chatId,\n rootId: parsed.rootId ?? undefined,\n parentId: parsed.parentId ?? undefined,\n messageId: parsed.messageId,\n touchedAt: Date.now(),\n });\n\n // 15) Ack reaction — fire-and-forget. Stash the resulting reaction id\n // so terminal handlers can remove it once the reply has been delivered.\n const emoji = pickAckEmoji(options.ackReaction);\n if (emoji) {\n const sessionId = session.id;\n helpers.waitUntil(\n client\n .addReaction({ messageId: parsed.messageId, emojiType: emoji })\n .then(({ reactionId }) => {\n const m = sessionMeta.get(sessionId);\n if (m) m.ackReactionId = reactionId;\n })\n .catch((e) => {\n console.warn(\n \"[eve-lark] ack reaction failed:\",\n e instanceof Error ? e.message : e,\n );\n }),\n );\n }\n\n return ackOk();\n };\n\n return defineChannel({\n routes: [POST(options.webhookPath, webhookHandler as never)],\n\n fetchFile: async (url: string) => {\n if (!url.startsWith(options.baseUrl)) return null;\n const m = url.match(/\\/messages\\/([^/]+)\\/resources\\/([^?]+)\\?type=(image|file)/);\n if (!m || !m[1] || !m[2] || !m[3]) return null;\n return client.downloadResource({\n messageId: m[1],\n fileKey: m[2],\n type: m[3] as \"image\" | \"file\",\n });\n },\n\n events: {\n // Streaming delta — patch the card.\n \"message.appended\"(data, _channel, ctx) {\n if (options.replyMode !== \"streaming\") return;\n const sessionId = ctx.session.id;\n const info = sessionInfoFromCtx(ctx);\n if (!info) return;\n const d = data as { messageDelta?: string };\n if (typeof d.messageDelta !== \"string\") return;\n const ctrl = getController(sessionId, info);\n ctrl.appendDelta(d.messageDelta);\n },\n\n // Terminal — deliver the final reply, then clean up the ack reaction.\n async \"message.completed\"(data, _channel, ctx) {\n const sessionId = ctx.session.id;\n const info = sessionInfoFromCtx(ctx);\n if (!info) return;\n const d = data as { message?: string | null };\n const rawText = typeof d.message === \"string\" ? d.message : \"\";\n const text = rawText.length > 0 ? rawText : EMPTY_REPLY_TEXT;\n\n try {\n await deliverReply(sessionId, info, text);\n } finally {\n await cleanupAckReaction(sessionId);\n dropController(sessionId);\n }\n },\n\n async \"turn.failed\"(data, _channel, ctx) {\n const sessionId = ctx?.session?.id;\n if (!sessionId) return;\n const info = sessionInfoFromCtx(ctx);\n if (!info) return;\n const errMsg = errMsgFrom(data, \"turn failed\");\n const userText = `⚠ ${errMsg}`;\n\n // If a streaming card already exists, abort patches it with the\n // error — the user sees the failure in-place. Otherwise deliverReply\n // sends a fresh error card / text. Either way the user sees the\n // error, never a silent typing-emoji dead end.\n const ctrl = controllers.get(sessionId);\n if (ctrl) {\n try {\n await ctrl.abort(errMsg);\n } catch (e) {\n console.warn(\n \"[eve-lark] turn.failed: streaming abort failed, will deliver fresh error:\",\n e instanceof Error ? e.message : e,\n );\n // Fall through to deliverReply.\n try {\n await deliverReply(sessionId, info, userText);\n } catch {\n // deliverReply swallows internally; this is unreachable.\n }\n }\n } else {\n // No streaming card yet. Deliver a fresh error card via the\n // standard cascade. (deliverReply in streaming mode will still\n // try finalize on a fresh controller, which sends a card with\n // the error text — exactly what we want.)\n try {\n await deliverReply(sessionId, info, userText);\n } catch {\n // unreachable; deliverReply swallows\n }\n }\n\n await cleanupAckReaction(sessionId);\n dropController(sessionId);\n },\n\n async \"session.failed\"(data) {\n // `session.failed` carries no `ctx`, so we can't tell which chat\n // this is. Per-session delivery happens via `turn.failed`; this\n // event is informational only.\n const errMsg = errMsgFrom(data, \"session failed\");\n console.error(\"[eve-lark] session.failed:\", errMsg);\n },\n },\n });\n}\n","/**\n * Typed error hierarchy for eve-lark.\n *\n * All errors extend a common base so consumers can `instanceof LarkChannelError`\n * to catch anything thrown by the channel.\n */\n\nexport class LarkChannelError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n }\n}\n\nexport class LarkConfigError extends LarkChannelError {}\n\nexport class LarkSignatureError extends LarkChannelError {}\n\nexport class LarkDecryptError extends LarkChannelError {}\n\nexport interface LarkApiErrorBody {\n code?: number | undefined;\n msg?: string | undefined;\n}\n\nexport class LarkApiError extends LarkChannelError {\n readonly code: number | undefined;\n readonly body: LarkApiErrorBody | undefined;\n readonly status: number | undefined;\n\n constructor(\n message: string,\n opts?: {\n code?: number | undefined;\n body?: LarkApiErrorBody | undefined;\n status?: number | undefined;\n cause?: unknown;\n },\n ) {\n super(message, { cause: opts?.cause });\n this.code = opts?.code;\n this.body = opts?.body;\n this.status = opts?.status;\n }\n}\n","import { LarkApiError, type LarkApiErrorBody } from \"./errors.js\";\nimport type { LarkCard, ResolvedLarkOptions } from \"./types.js\";\n\ninterface TokenState {\n value: string;\n expiresAt: number;\n}\n\nconst TOKEN_INVALID_CODES = new Set<number>([99991663, 99991664, 99991661]);\n\ninterface RequestResult {\n status: number;\n body: unknown;\n retryAfter: number | null;\n}\n\nexport class LarkClient {\n private readonly options: ResolvedLarkOptions;\n private token: TokenState | null = null;\n private refreshPromise: Promise<string> | null = null;\n\n constructor(options: ResolvedLarkOptions) {\n this.options = options;\n }\n\n async getTenantAccessToken(): Promise<string> {\n if (\n this.token &&\n Date.now() + this.options.tokenRefreshBufferMs < this.token.expiresAt\n ) {\n return this.token.value;\n }\n if (this.refreshPromise) return this.refreshPromise;\n this.refreshPromise = this.#refresh();\n try {\n return await this.refreshPromise;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n async #refresh(): Promise<string> {\n const body = {\n app_id: this.options.appId,\n app_secret: this.options.appSecret,\n };\n const res = await this.options.fetch(\n `${this.options.baseUrl}/open-apis/auth/v3/tenant_access_token/internal`,\n {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(this.options.requestTimeoutMs),\n },\n );\n if (!res.ok) {\n throw new LarkApiError(\n `eve-lark: token refresh failed (HTTP ${res.status})`,\n { status: res.status },\n );\n }\n const json = (await res.json()) as { code?: number; tenant_access_token?: string; expire?: number; msg?: string };\n if (json.code !== 0 || !json.tenant_access_token) {\n throw new LarkApiError(\n `eve-lark: token refresh returned code=${json.code ?? \"?\"} msg=${json.msg ?? \"?\"}`,\n { body: json, code: json.code },\n );\n }\n const expireSec = typeof json.expire === \"number\" ? json.expire : 7200;\n this.token = {\n value: json.tenant_access_token,\n expiresAt: Date.now() + expireSec * 1000,\n };\n return this.token.value;\n }\n\n async sendText(args: {\n chatId: string;\n content: string;\n rootId?: string;\n parentId?: string;\n }): Promise<{ messageId: string }> {\n const content = JSON.stringify({ text: args.content });\n return this.#sendMessage({\n receive_id: args.chatId,\n msg_type: \"text\",\n content,\n root_id: args.rootId,\n parent_id: args.parentId,\n });\n }\n\n async sendCard(args: {\n chatId: string;\n card: LarkCard;\n rootId?: string;\n parentId?: string;\n }): Promise<{ messageId: string }> {\n const content = JSON.stringify(args.card);\n return this.#sendMessage({\n receive_id: args.chatId,\n msg_type: \"interactive\",\n content,\n root_id: args.rootId,\n parent_id: args.parentId,\n });\n }\n\n async #sendMessage(body: Record<string, unknown>): Promise<{ messageId: string }> {\n const payload = Object.fromEntries(\n Object.entries(body).filter(([, v]) => v !== undefined),\n );\n const json = await this.#request(\"POST\", \"/open-apis/im/v1/messages?receive_id_type=chat_id\", payload);\n const messageId = (json as { data?: { message_id?: string } }).data?.message_id;\n if (!messageId) {\n throw new LarkApiError(\"eve-lark: send missing message_id in response\", {\n body: json as LarkApiErrorBody,\n });\n }\n return { messageId };\n }\n\n async patchCard(args: { messageId: string; card: LarkCard }): Promise<void> {\n await this.#request(\n \"PATCH\",\n `/open-apis/im/v1/messages/${encodeURIComponent(args.messageId)}`,\n { content: JSON.stringify(args.card) },\n );\n }\n\n async downloadResource(args: {\n messageId: string;\n fileKey: string;\n type: \"image\" | \"file\";\n }): Promise<Buffer> {\n const path = `/open-apis/im/v1/messages/${encodeURIComponent(args.messageId)}/resources/${encodeURIComponent(args.fileKey)}?type=${args.type}`;\n const token = await this.getTenantAccessToken();\n const res = await this.options.fetch(`${this.options.baseUrl}${path}`, {\n method: \"GET\",\n headers: { authorization: `Bearer ${token}` },\n signal: AbortSignal.timeout(this.options.requestTimeoutMs),\n });\n if (!res.ok) {\n throw new LarkApiError(\n `eve-lark: downloadResource HTTP ${res.status}`,\n { status: res.status },\n );\n }\n return Buffer.from(await res.arrayBuffer());\n }\n\n async addReaction(args: {\n messageId: string;\n emojiType: string;\n }): Promise<{ reactionId: string }> {\n const path = `/open-apis/im/v1/messages/${encodeURIComponent(args.messageId)}/reactions`;\n const json = (await this.#request(\"POST\", path, {\n reaction_type: { emoji_type: args.emojiType },\n })) as { data?: { reaction_id?: string } };\n const reactionId = json.data?.reaction_id;\n if (!reactionId) {\n throw new LarkApiError(\"eve-lark: addReaction missing reaction_id\", {\n body: json as LarkApiErrorBody,\n });\n }\n return { reactionId };\n }\n\n async removeReaction(args: { messageId: string; reactionId: string }): Promise<void> {\n const path = `/open-apis/im/v1/messages/${encodeURIComponent(args.messageId)}/reactions/${encodeURIComponent(args.reactionId)}`;\n await this.#request(\"DELETE\", path, undefined);\n }\n\n /**\n * Central request wrapper with auth, retry, and Feishu error decoding.\n *\n * Retry policy:\n * - 429 (rate limit): always retry with `Retry-After` backoff. Safe —\n * server rejected the request before processing.\n * - 5xx: retry ONLY for idempotent methods (GET / PATCH / DELETE). POST\n * is NOT retried on 5xx because Feishu's POST /messages and POST\n * /reactions are non-idempotent — the server may have created the\n * resource before returning the error, and retrying would silently\n * double-send.\n * - 401 / token-invalid code: refresh and retry once.\n * - Other 4xx: throw LarkApiError with the Feishu code/msg.\n */\n async #request(method: string, path: string, body: unknown): Promise<unknown> {\n const url = `${this.options.baseUrl}${path}`;\n let token = await this.getTenantAccessToken();\n let tokenRefreshed = false;\n const methodNorm = method.toUpperCase();\n const retryableMethod = methodNorm !== \"POST\";\n\n for (let attempt = 0; attempt <= this.options.maxRetries; attempt++) {\n const res = await this.options.fetch(url, {\n method,\n headers: {\n authorization: `Bearer ${token}`,\n \"content-type\": \"application/json\",\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n signal: AbortSignal.timeout(this.options.requestTimeoutMs),\n });\n\n const result = await this.#consumeResponse(res);\n const status = result.status;\n\n if (status >= 200 && status < 300) {\n const jsonBody = result.body as { code?: number; msg?: string };\n if (jsonBody && typeof jsonBody.code === \"number\" && jsonBody.code !== 0) {\n if (TOKEN_INVALID_CODES.has(jsonBody.code) && !tokenRefreshed) {\n this.token = null;\n token = await this.getTenantAccessToken();\n tokenRefreshed = true;\n attempt -= 1;\n continue;\n }\n throw new LarkApiError(\n `eve-lark: ${method} ${path} failed code=${jsonBody.code} msg=${jsonBody.msg ?? \"?\"}`,\n { code: jsonBody.code, body: jsonBody as LarkApiErrorBody, status },\n );\n }\n return result.body;\n }\n\n if (status === 401 && !tokenRefreshed) {\n this.token = null;\n token = await this.getTenantAccessToken();\n tokenRefreshed = true;\n attempt -= 1;\n continue;\n }\n\n const isRateLimited = status === 429;\n const isServerErr = status >= 500 && status < 600;\n const retryable = isRateLimited || (isServerErr && retryableMethod);\n if (retryable && attempt < this.options.maxRetries) {\n const delayMs = this.#computeBackoff(status, result.retryAfter, attempt);\n await sleep(delayMs);\n continue;\n }\n\n const bodyObj = result.body as LarkApiErrorBody | undefined;\n const code = bodyObj?.code;\n const msg = bodyObj?.msg;\n const detail = msg ? ` code=${code ?? \"?\"} msg=${msg}` : \"\";\n throw new LarkApiError(\n `eve-lark: ${method} ${path} failed HTTP ${status}${detail}`,\n { status, body: bodyObj, code },\n );\n }\n throw new LarkApiError(`eve-lark: ${method} ${path} exhausted retries`);\n }\n\n async #consumeResponse(res: Response): Promise<RequestResult> {\n const retryAfterRaw = res.headers.get(\"retry-after\");\n const retryAfter = retryAfterRaw ? parseRetryAfter(retryAfterRaw) : null;\n const text = await res.text();\n if (!text) {\n return { status: res.status, body: undefined, retryAfter };\n }\n try {\n return { status: res.status, body: JSON.parse(text), retryAfter };\n } catch {\n return { status: res.status, body: { raw: text }, retryAfter };\n }\n }\n\n #computeBackoff(status: number, retryAfter: number | null, attempt: number): number {\n if (status === 429 && retryAfter !== null) {\n return Math.min(retryAfter * 1000, 10_000);\n }\n const base = 300 * Math.pow(2, attempt);\n const jitter = base * 0.2 * (Math.random() * 2 - 1);\n return Math.max(0, Math.round(base + jitter));\n }\n}\n\nfunction parseRetryAfter(raw: string): number | null {\n const sec = Number(raw);\n if (Number.isFinite(sec) && sec >= 0) return sec;\n const date = Date.parse(raw);\n if (Number.isFinite(date)) return Math.max(0, (date - Date.now()) / 1000);\n return null;\n}\n\nfunction sleep(ms: number): Promise<void> {\n if (ms <= 0) return Promise.resolve();\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * In-process deduplication for Feishu webhook events.\n *\n * Feishu retries delivery on non-2xx responses and during brief outage windows,\n * so consumers must idempotently ack events they've already seen. We key by\n * `header.event_id` (or `message.message_id` as a fallback) and remember each\n * key for the TTL window.\n *\n * Backed by an insertion-ordered Map so FIFO eviction is O(1) at the front.\n * Lazy sweep on every insert prevents unbounded growth of expired entries;\n * no `setInterval` so this is safe in serverless.\n */\nexport class DedupMap {\n private readonly entries = new Map<string, number>();\n private readonly ttlMs: number;\n private readonly maxEntries: number;\n private insertsSinceSweep = 0;\n\n constructor(ttlMs: number, maxEntries: number) {\n this.ttlMs = ttlMs;\n this.maxEntries = maxEntries;\n }\n\n has(key: string): boolean {\n const at = this.entries.get(key);\n if (at === undefined) return false;\n if (Date.now() - at > this.ttlMs) {\n this.entries.delete(key);\n return false;\n }\n return true;\n }\n\n set(key: string): void {\n this.maybeSweep();\n // Refresh timestamp by re-inserting at the tail of insertion order.\n this.entries.delete(key);\n this.entries.set(key, Date.now());\n\n while (this.entries.size > this.maxEntries) {\n const oldestKey = this.entries.keys().next().value;\n if (oldestKey === undefined) break;\n this.entries.delete(oldestKey);\n }\n }\n\n /**\n * Walk the insertion-ordered map from the front and drop expired entries.\n * Stops at the first non-expired entry since events arrive roughly in time\n * order. Called on every set, so cost is amortized.\n */\n private maybeSweep(): void {\n this.insertsSinceSweep += 1;\n if (this.insertsSinceSweep < 64 && this.entries.size < this.maxEntries) {\n return;\n }\n this.insertsSinceSweep = 0;\n const now = Date.now();\n for (const [key, at] of this.entries) {\n if (now - at <= this.ttlMs) break;\n this.entries.delete(key);\n }\n }\n}\n","import { createDecipheriv, createHash, timingSafeEqual } from \"node:crypto\";\nimport { LarkDecryptError } from \"./errors.js\";\n\n/**\n * Verify an `X-Lark-Signature` header against the raw webhook body.\n *\n * Feishu computes: `sha256(timestamp + nonce + encrypt_key + body)` and ships\n * the hex digest (optionally prefixed with `sha256=`) in `X-Lark-Signature`.\n * We concatenate the string parts first, then the raw bytes of the body, to\n * avoid a UTF-8 round-trip on the request body.\n *\n * Constant-time compare. Returns false on length mismatch instead of throwing.\n */\nexport function verifySignature(opts: {\n timestamp: string;\n nonce: string;\n encryptKey: string;\n rawBody: Buffer;\n signatureHeader: string;\n}): boolean {\n const expected = opts.signatureHeader.replace(/^sha256=/, \"\");\n const computed = createHash(\"sha256\")\n .update(opts.timestamp + opts.nonce + opts.encryptKey)\n .update(opts.rawBody)\n .digest(\"hex\");\n const a = Buffer.from(computed, \"hex\");\n const b = Buffer.from(expected, \"hex\");\n return a.length === b.length && timingSafeEqual(a, b);\n}\n\n/**\n * Decrypt the `encrypt` field from a Feishu webhook body.\n *\n * Layout:\n * key = SHA256(encrypt_key) // 32 bytes → AES-256\n * buf = base64decode(encrypt_field)\n * iv = buf[0:16]\n * ct = buf[16:] // AES-256-CBC ciphertext\n * plaintext = AES_256_CBC_decrypt(key, iv, ct) // PKCS#7 unpadded\n *\n * Returns the raw plaintext bytes. The caller is expected to JSON.parse them.\n */\nexport function decryptPayload(encryptB64: string, encryptKey: string): Buffer {\n const key = createHash(\"sha256\").update(encryptKey).digest();\n const buf = Buffer.from(encryptB64, \"base64\");\n\n if (buf.length < 32) {\n throw new LarkDecryptError(\n `eve-lark: ciphertext too short (${buf.length} bytes; need >= 32 for IV + one block)`,\n );\n }\n if ((buf.length - 16) % 16 !== 0) {\n throw new LarkDecryptError(\n `eve-lark: ciphertext length ${buf.length} is not 16 + N*16`,\n );\n }\n\n const iv = buf.subarray(0, 16);\n const ct = buf.subarray(16);\n const dec = createDecipheriv(\"aes-256-cbc\", key, iv);\n\n try {\n return Buffer.concat([dec.update(ct), dec.final()]);\n } catch (e) {\n throw new LarkDecryptError(\"eve-lark: AES decrypt failed (bad padding or wrong key)\", {\n cause: e,\n });\n }\n}\n","import type {\n LarkInboundEvent,\n LarkInboundFile,\n LarkInboundResult,\n LarkMention,\n LarkRawMention,\n} from \"./types.js\";\n\nconst MIME_BY_EXT: Record<string, string> = {\n pdf: \"application/pdf\",\n zip: \"application/zip\",\n gz: \"application/gzip\",\n tar: \"application/x-tar\",\n doc: \"application/msword\",\n docx: \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n xls: \"application/vnd.ms-excel\",\n xlsx: \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n ppt: \"application/vnd.ms-powerpoint\",\n pptx: \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n csv: \"text/csv\",\n txt: \"text/plain\",\n md: \"text/markdown\",\n json: \"application/json\",\n xml: \"application/xml\",\n html: \"text/html\",\n htm: \"text/html\",\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n mp3: \"audio/mpeg\",\n wav: \"audio/wav\",\n mp4: \"video/mp4\",\n mov: \"video/quicktime\",\n};\n\nfunction mimeFromExt(filename: string | undefined): string {\n if (!filename) return \"application/octet-stream\";\n const dot = filename.lastIndexOf(\".\");\n if (dot < 0) return \"application/octet-stream\";\n return MIME_BY_EXT[filename.slice(dot + 1).toLowerCase()] ?? \"application/octet-stream\";\n}\n\nfunction mentionFromRaw(m: LarkRawMention, botOpenId: string | undefined): LarkMention {\n const isOpenIdOfBot =\n !!botOpenId && !!m.id.open_id && m.id.open_id === botOpenId;\n const isAll = !!m.id.open_id && m.id.open_id === \"all\";\n return {\n key: m.key,\n id: {\n openId: m.id.open_id,\n userId: m.id.user_id,\n unionId: m.id.union_id,\n },\n name: m.name,\n idType: m.id_type ?? \"open_id\",\n isOpenIdOfBot,\n isAll,\n };\n}\n\nfunction stripBotMentions(text: string, mentions: LarkMention[]): string {\n // Feishu ships mentions as opaque placeholders (e.g. \"@_user_1\") in the text\n // body alongside a structured mentions array. Rewrite them to something the\n // model can read:\n // - the bot itself: dropped (the model already knows it's being addressed)\n // - @all: replaced with a literal \"@all\" token\n // - other users: replaced with \"@<display name>\"\n let out = text;\n for (const m of mentions) {\n if (!m.key) continue;\n if (m.isOpenIdOfBot) {\n out = out.split(m.key).join(\"\");\n } else if (m.isAll) {\n out = out.split(m.key).join(\"@all\");\n } else {\n out = out.split(m.key).join(`@${m.name}`);\n }\n }\n return out.replace(/\\s+/g, \" \").trim();\n}\n\ninterface ParsedContent {\n text: string;\n files: LarkInboundFile[];\n}\n\nfunction parseContent(messageType: string, rawContent: string): ParsedContent {\n if (!rawContent) return { text: \"\", files: [] };\n let content: Record<string, unknown>;\n try {\n content = JSON.parse(rawContent) as Record<string, unknown>;\n } catch {\n return { text: \"\", files: [] };\n }\n\n switch (messageType) {\n case \"text\": {\n const text = typeof content.text === \"string\" ? content.text : \"\";\n return { text, files: [] };\n }\n case \"image\": {\n const imageKey = typeof content.image_key === \"string\" ? content.image_key : \"\";\n if (!imageKey) return { text: \"\", files: [] };\n return {\n text: \"\",\n files: [{ fileKey: imageKey, mediaType: \"image/png\", kind: \"image\" }],\n };\n }\n case \"file\": {\n const fileKey = typeof content.file_key === \"string\" ? content.file_key : \"\";\n if (!fileKey) return { text: \"\", files: [] };\n const fileName = typeof content.file_name === \"string\" ? content.file_name : undefined;\n return {\n text: \"\",\n files: [{ fileKey, mediaType: mimeFromExt(fileName), kind: \"file\" }],\n };\n }\n case \"post\": {\n const locale = (content.zh_cn ?? content.en_us ?? content.ja_jp ?? null) as\n | { content?: unknown[][] }\n | null;\n if (!locale?.content) return { text: \"\", files: [] };\n const text = locale.content\n .flatMap((line) =>\n (line ?? [])\n .filter((node): node is { tag: string; text?: unknown } => {\n if (typeof node !== \"object\" || node === null) return false;\n const tag = (node as { tag?: unknown }).tag;\n const text = (node as { text?: unknown }).text;\n return tag === \"text\" && typeof text === \"string\";\n })\n .map((node) => node.text as string),\n )\n .join(\" \");\n return { text, files: [] };\n }\n default:\n // audio, media, sticker, share_chat, share_user, interactive — not in v1 scope.\n return { text: \"\", files: [] };\n }\n}\n\nexport function parseInbound(\n event: LarkInboundEvent,\n botOpenId?: string,\n): LarkInboundResult {\n const messageType = event.message.message_type;\n const parsed = parseContent(messageType, event.message.content);\n const rawMentions = event.message.mentions ?? [];\n const mentions = rawMentions.map((m) => mentionFromRaw(m, botOpenId));\n\n const senderOpenId =\n event.sender.sender_id.open_id ??\n event.sender.sender_id.user_id ??\n event.sender.sender_id.union_id ??\n \"\";\n\n const text =\n messageType === \"text\"\n ? stripBotMentions(parsed.text, mentions)\n : parsed.text;\n\n const chatType = event.chat_type === \"group\" ? \"group\" : \"p2p\";\n const senderType = event.sender.sender_type === \"app\" ? \"app\" : \"user\";\n\n return {\n text,\n files: parsed.files,\n chatId: event.message.chat_id,\n rootId: event.message.root_id ?? null,\n parentId: event.message.parent_id ?? null,\n messageId: event.message.message_id,\n senderOpenId,\n senderType,\n chatType,\n mentions,\n };\n}\n","import type { LarkCard } from \"./types.js\";\n\nconst BASE_CONFIG = {\n wide_screen_mode: true,\n update_multi: true,\n} as const;\n\n/**\n * Build a simple single-shot card with the given markdown text.\n */\nexport function buildTextCard(text: string): LarkCard {\n return {\n config: { ...BASE_CONFIG },\n elements: [{ tag: \"markdown\", content: text }],\n };\n}\n\n/**\n * Build a streaming card with an optional status prefix and an answer buffer.\n *\n * Format:\n * <optional status prefix in muted tone>\n * <buffer>\n */\nexport function buildStreamingCard(opts: { buffer: string; status?: string | undefined }): LarkCard {\n const lines: string[] = [];\n if (opts.status) {\n lines.push(`<font color='grey'>${opts.status}</font>`);\n }\n lines.push(opts.buffer.length > 0 ? opts.buffer : \"_…_\");\n return {\n config: { ...BASE_CONFIG },\n elements: [{ tag: \"markdown\", content: lines.join(\"\\n\\n\") }],\n };\n}\n\n/**\n * Build an error card displayed when a turn fails. `message` is rendered\n * verbatim under a red warning glyph — the caller is responsible for\n * prefixing/shape (most callers pass the raw error string and we wrap it).\n */\nexport function buildErrorCard(message: string): LarkCard {\n return {\n config: { ...BASE_CONFIG },\n elements: [\n { tag: \"markdown\", content: `<font color='red'>⚠ ${message}</font>` },\n ],\n };\n}\n","import { buildErrorCard, buildStreamingCard, buildTextCard } from \"./card.js\";\nimport type { LarkCard } from \"./types.js\";\n\ntype State = \"idle\" | \"creating\" | \"streaming\" | \"completed\" | \"aborted\";\n\ninterface ControllerDeps {\n chatId: string;\n rootId?: string | undefined;\n parentId?: string | undefined;\n patchIntervalMs: number;\n createThresholdMs: number;\n}\n\ninterface LarkClientLike {\n sendCard(args: {\n chatId: string;\n card: LarkCard;\n rootId?: string;\n parentId?: string;\n }): Promise<{ messageId: string }>;\n patchCard(args: { messageId: string; card: LarkCard }): Promise<void>;\n sendText(args: {\n chatId: string;\n content: string;\n rootId?: string;\n parentId?: string;\n }): Promise<{ messageId: string }>;\n}\n\n/**\n * Streaming interactive-card state machine.\n *\n * idle ──first delta──> creating ──sendCard ok──> streaming ──finalize──> completed\n * │\n * └──sendCard fail──> aborted (flag; static fallback on message.completed)\n *\n * The card is created lazily after `createThresholdMs` of the first delta, so\n * short turns can short-circuit straight to `finalize` (which sends the card\n * with the full answer in one shot). Once streaming, patches are throttled to\n * `patchIntervalMs`.\n *\n * If `sendCard` fails on creation, the controller flips to `fallbackToText`\n * and `finalize`/`ensureFinalized` deliver via `sendText` instead.\n */\nexport class StreamingCardController {\n private readonly deps: ControllerDeps;\n private readonly client: LarkClientLike;\n\n private state: State = \"idle\";\n private buffer = \"\";\n private status: string | undefined;\n private messageId: string | undefined;\n private fallbackToText = false;\n\n private createTimer: ReturnType<typeof setTimeout> | null = null;\n private patchTimer: ReturnType<typeof setTimeout> | null = null;\n private patchInFlight: Promise<void> | null = null;\n private patchScheduled = false;\n private lastPatchAt = 0;\n\n constructor(client: LarkClientLike, deps: ControllerDeps) {\n this.client = client;\n this.deps = deps;\n }\n\n appendDelta(text: string): void {\n if (this.state === \"completed\" || this.state === \"aborted\") return;\n this.buffer += text;\n if (this.state === \"idle\") {\n this.scheduleCreate();\n } else if (this.state === \"streaming\") {\n this.schedulePatch();\n }\n }\n\n setStatus(status: string): void {\n if (this.state === \"completed\" || this.state === \"aborted\") return;\n this.status = status;\n if (this.state === \"streaming\") {\n this.schedulePatch();\n }\n }\n\n async finalize(fullText: string): Promise<void> {\n if (this.state === \"completed\" || this.state === \"aborted\") return;\n this.cancelCreateTimer();\n this.cancelPatchTimer();\n this.buffer = fullText;\n\n if (this.fallbackToText) {\n await this.client.sendText({\n chatId: this.deps.chatId,\n content: fullText,\n rootId: this.deps.rootId,\n parentId: this.deps.parentId,\n });\n this.state = \"completed\";\n return;\n }\n\n if (this.messageId === undefined) {\n // Never managed to create a card. Send one with the full text in a\n // single shot so the user still gets a card reply.\n try {\n const res = await this.client.sendCard({\n chatId: this.deps.chatId,\n card: buildTextCard(fullText),\n rootId: this.deps.rootId,\n parentId: this.deps.parentId,\n });\n this.messageId = res.messageId;\n this.state = \"completed\";\n } catch {\n // Last-resort fallback: plain text.\n this.fallbackToText = true;\n await this.client.sendText({\n chatId: this.deps.chatId,\n content: fullText,\n rootId: this.deps.rootId,\n parentId: this.deps.parentId,\n });\n this.state = \"completed\";\n }\n return;\n }\n\n // Card already exists; flush the final state.\n if (this.patchInFlight) {\n try {\n await this.patchInFlight;\n } catch {\n // swallow; we'll attempt the final patch below\n }\n }\n await this.client.patchCard({\n messageId: this.messageId,\n card: buildStreamingCard({ buffer: fullText, status: undefined }),\n });\n this.state = \"completed\";\n }\n\n async abort(error: string): Promise<void> {\n if (this.state === \"completed\" || this.state === \"aborted\") return;\n this.cancelCreateTimer();\n this.cancelPatchTimer();\n if (this.messageId === undefined) {\n // No card to patch; mark fallback and let finalize/ensureFinalized\n // deliver a plain-text error if asked.\n this.fallbackToText = true;\n this.state = \"aborted\";\n return;\n }\n try {\n await this.client.patchCard({\n messageId: this.messageId,\n card: buildErrorCard(error),\n });\n } finally {\n this.state = \"aborted\";\n }\n }\n\n async ensureFinalized(): Promise<void> {\n if (this.state !== \"completed\" && this.state !== \"aborted\") {\n await this.finalize(this.buffer);\n }\n }\n\n isStreaming(): boolean {\n return this.state === \"streaming\" || this.state === \"creating\";\n }\n\n isCompleted(): boolean {\n return this.state === \"completed\" || this.state === \"aborted\";\n }\n\n private scheduleCreate(): void {\n if (this.createTimer) return;\n this.state = \"creating\";\n this.createTimer = setTimeout(() => {\n this.createTimer = null;\n void this.doCreate();\n }, this.deps.createThresholdMs);\n }\n\n private cancelCreateTimer(): void {\n if (this.createTimer) {\n clearTimeout(this.createTimer);\n this.createTimer = null;\n }\n }\n\n private async doCreate(): Promise<void> {\n if (this.state !== \"creating\") return;\n try {\n const res = await this.client.sendCard({\n chatId: this.deps.chatId,\n card: buildStreamingCard({ buffer: this.buffer, status: this.status }),\n rootId: this.deps.rootId,\n parentId: this.deps.parentId,\n });\n this.messageId = res.messageId;\n this.state = \"streaming\";\n this.lastPatchAt = Date.now();\n } catch (e) {\n console.warn(\n \"[eve-lark] streaming card create failed; will deliver via plain text on finalize:\",\n e instanceof Error ? e.message : e,\n );\n this.fallbackToText = true;\n this.state = \"streaming\"; // keep accepting deltas; finalize will deliver as text\n }\n }\n\n private schedulePatch(): void {\n if (this.patchScheduled) return;\n this.patchScheduled = true;\n const elapsed = Date.now() - this.lastPatchAt;\n const wait = Math.max(0, this.deps.patchIntervalMs - elapsed);\n this.patchTimer = setTimeout(() => {\n this.patchTimer = null;\n this.patchScheduled = false;\n void this.maybeFlushPatch();\n }, wait);\n }\n\n private cancelPatchTimer(): void {\n if (this.patchTimer) {\n clearTimeout(this.patchTimer);\n this.patchTimer = null;\n }\n this.patchScheduled = false;\n }\n\n private async maybeFlushPatch(): Promise<void> {\n if (this.state !== \"streaming\") return;\n if (this.patchInFlight) return;\n if (this.messageId === undefined) return;\n const card = buildStreamingCard({ buffer: this.buffer, status: this.status });\n this.patchInFlight = this.client\n .patchCard({ messageId: this.messageId, card })\n .catch((e) => {\n // Best-effort: the next delta will retry. Log so operators can see\n // when the card stream is degraded.\n console.warn(\n \"[eve-lark] streaming card patch failed:\",\n e instanceof Error ? e.message : e,\n );\n })\n .finally(() => {\n this.patchInFlight = null;\n this.lastPatchAt = Date.now();\n });\n await this.patchInFlight;\n }\n}\n","import { LarkConfigError } from \"./errors.js\";\nimport type {\n LarkChannelOptions,\n LarkReplyMode,\n LarkTransportMode,\n ResolvedLarkOptions,\n} from \"./types.js\";\n\nconst DEFAULTS = {\n baseUrl: \"https://open.feishu.cn\",\n webhookPath: \"/lark/webhook\",\n replyMode: \"streaming\" as LarkReplyMode,\n streamPatchIntervalMs: 1000,\n streamCreateThresholdMs: 400,\n dedupTtlMs: 30 * 60 * 1000,\n dedupMaxEntries: 5000,\n requestTimeoutMs: 15000,\n maxRetries: 2,\n tokenRefreshBufferMs: 5 * 60 * 1000,\n signatureSkewMs: 5 * 60 * 1000,\n ackReaction: \"TYPING\" as string | false,\n mode: \"long-connection\" as LarkTransportMode,\n};\n\nconst ENV_KEYS = {\n appId: \"LARK_APP_ID\",\n appSecret: \"LARK_APP_SECRET\",\n verificationToken: \"LARK_VERIFICATION_TOKEN\",\n encryptKey: \"LARK_ENCRYPT_KEY\",\n baseUrl: \"LARK_BASE_URL\",\n botOpenId: \"LARK_BOT_OPEN_ID\",\n replyMode: \"LARK_REPLY_MODE\",\n mode: \"LARK_MODE\",\n} as const;\n\nexport type ResolveEnv = Record<string, string | undefined>;\n\nfunction defaultEnv(): ResolveEnv {\n if (typeof process !== \"undefined\" && process.env) {\n return process.env as ResolveEnv;\n }\n return {};\n}\n\nfunction pick(input: string | undefined, envValue: string | undefined): string | undefined {\n return input ?? envValue;\n}\n\nexport function resolveOptions(\n options: LarkChannelOptions,\n env: ResolveEnv = defaultEnv(),\n): ResolvedLarkOptions {\n const appId = pick(options.appId, env[ENV_KEYS.appId]);\n const appSecret = pick(options.appSecret, env[ENV_KEYS.appSecret]);\n const verificationToken = pick(\n options.verificationToken,\n env[ENV_KEYS.verificationToken],\n );\n\n if (!appId) {\n throw new LarkConfigError(\n `eve-lark: appId is required (option \\`appId\\` or env \\`${ENV_KEYS.appId}\\`)`,\n );\n }\n if (!appSecret) {\n throw new LarkConfigError(\n `eve-lark: appSecret is required (option \\`appSecret\\` or env \\`${ENV_KEYS.appSecret}\\`)`,\n );\n }\n if (!verificationToken) {\n throw new LarkConfigError(\n `eve-lark: verificationToken is required (option \\`verificationToken\\` or env \\`${ENV_KEYS.verificationToken}\\`)`,\n );\n }\n\n const rawBaseUrl = pick(options.baseUrl, env[ENV_KEYS.baseUrl]) ?? DEFAULTS.baseUrl;\n const baseUrl = rawBaseUrl.replace(/\\/+$/, \"\");\n\n const replyModeEnv = env[ENV_KEYS.replyMode];\n const replyMode: LarkReplyMode =\n options.replyMode ??\n (replyModeEnv === \"static\" || replyModeEnv === \"streaming\" ? replyModeEnv : DEFAULTS.replyMode);\n\n const modeEnv = env[ENV_KEYS.mode];\n const mode: LarkTransportMode =\n options.mode ??\n (modeEnv === \"webhook\" || modeEnv === \"long-connection\" ? modeEnv : DEFAULTS.mode);\n\n return {\n appId,\n appSecret,\n verificationToken,\n encryptKey: pick(options.encryptKey, env[ENV_KEYS.encryptKey]),\n baseUrl,\n botOpenId: pick(options.botOpenId, env[ENV_KEYS.botOpenId]),\n webhookPath: options.webhookPath ?? DEFAULTS.webhookPath,\n replyMode,\n streamPatchIntervalMs: options.streamPatchIntervalMs ?? DEFAULTS.streamPatchIntervalMs,\n streamCreateThresholdMs: options.streamCreateThresholdMs ?? DEFAULTS.streamCreateThresholdMs,\n dedupTtlMs: options.dedupTtlMs ?? DEFAULTS.dedupTtlMs,\n dedupMaxEntries: options.dedupMaxEntries ?? DEFAULTS.dedupMaxEntries,\n requestTimeoutMs: options.requestTimeoutMs ?? DEFAULTS.requestTimeoutMs,\n maxRetries: options.maxRetries ?? DEFAULTS.maxRetries,\n tokenRefreshBufferMs: options.tokenRefreshBufferMs ?? DEFAULTS.tokenRefreshBufferMs,\n signatureSkewMs: options.signatureSkewMs ?? DEFAULTS.signatureSkewMs,\n fetch: options.fetch ?? globalThis.fetch,\n ackReaction: options.ackReaction ?? DEFAULTS.ackReaction,\n mode,\n port:\n options.port ??\n (process.env.PORT ? Number(process.env.PORT) : 2000),\n };\n}\n","/**\n * Long-connection transport: when `mode: \"long-connection\"` is set on\n * {@link createLarkChannel} (the default), the channel starts a Feishu\n * `@larksuiteoapi/node-sdk` WSClient as a side effect of construction. Each\n * inbound event is re-encrypted + re-signed and POSTed to the channel's own\n * webhook on localhost, where the standard webhook handler runs (with full\n * access to `send()` etc.). This lets users run the bot against a real Feishu\n * app from `eve dev` alone — no public webhook URL, no second process.\n *\n * The SDK is a hard runtime dependency of this package (declared in\n * `dependencies`), so `pnpm add eve-lark` brings it in automatically. The\n * `import()` below is dynamic only so `mode: \"webhook\"` code paths don't\n * eagerly load the SDK at module import time.\n */\n\nimport {\n createCipheriv,\n createHash,\n randomBytes,\n} from \"node:crypto\";\nimport type { ResolvedLarkOptions } from \"./types.js\";\n\n/** A Feishu v2 envelope we forward to the channel webhook. */\nexport type LarkEvent = {\n schema?: string;\n type?: string;\n challenge?: string;\n token?: string;\n header?: Record<string, unknown>;\n event?: unknown;\n [k: string]: unknown;\n};\n\nexport interface PostEventOptions {\n eveWebhookUrl: string;\n /** When set, the body is AES-encrypted and the request is signed. */\n encryptKey?: string | undefined;\n /** Override for tests. Defaults to globalThis.fetch. */\n fetch?: typeof fetch | undefined;\n}\n\nfunction aesEncrypt(plaintext: Buffer, key: string): Buffer {\n const keyBuf = createHash(\"sha256\").update(key).digest();\n const iv = randomBytes(16);\n const cipher = createCipheriv(\"aes-256-cbc\", keyBuf, iv);\n return Buffer.concat([iv, cipher.update(plaintext), cipher.final()]);\n}\n\nfunction signBody(timestamp: string, nonce: string, body: Buffer, key: string): string {\n return createHash(\"sha256\")\n .update(timestamp + nonce + key)\n .update(body)\n .digest(\"hex\");\n}\n\n/**\n * Forward one Feishu event to the local eve webhook. Re-encrypts and signs\n * when an `encryptKey` is set, so the channel handler exercises its own\n * signature + AES pipeline on every event.\n */\nexport async function postEventToWebhook(\n event: LarkEvent,\n opts: PostEventOptions,\n): Promise<void> {\n const fetchImpl = opts.fetch ?? globalThis.fetch;\n const plain = Buffer.from(JSON.stringify(event), \"utf8\");\n let body: Buffer;\n const headers: Record<string, string> = { \"content-type\": \"application/json\" };\n\n if (opts.encryptKey) {\n const encrypted = aesEncrypt(plain, opts.encryptKey).toString(\"base64\");\n body = Buffer.from(JSON.stringify({ encrypt: encrypted }), \"utf8\");\n const ts = Math.floor(Date.now() / 1000).toString();\n const nonce = randomBytes(8).toString(\"hex\");\n const sig = signBody(ts, nonce, body, opts.encryptKey);\n headers[\"x-lark-request-timestamp\"] = ts;\n headers[\"x-lark-request-nonce\"] = nonce;\n headers[\"x-lark-signature\"] = `sha256=${sig}`;\n } else {\n body = plain;\n }\n\n const res = await fetchImpl(opts.eveWebhookUrl, {\n method: \"POST\",\n headers,\n body: new Uint8Array(body.buffer, body.byteOffset, body.byteLength) as never,\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(\n `eve-lark: forward to ${opts.eveWebhookUrl} failed (HTTP ${res.status})${text ? `: ${text.slice(0, 200)}` : \"\"}`,\n );\n }\n}\n\n/**\n * Post-event wrapper with a single exponential-backoff retry. Catches the\n * common case where `eve dev` is momentarily unavailable (between HMR\n * reloads, mid-restart, GC pause). Without this the event would be dropped\n * on the floor with just a log line — bad UX during dev.\n */\nexport async function postEventToWebhookRetry(\n event: LarkEvent,\n opts: PostEventOptions,\n): Promise<void> {\n try {\n await postEventToWebhook(event, opts);\n } catch (firstErr) {\n await new Promise((r) => setTimeout(r, 300));\n // Regenerate signature: timestamp/nonce moved on by ~300ms, and the\n // skew check at the channel handler would reject a stale signature.\n await postEventToWebhook(event, opts).catch((retryErr) => {\n throw retryErr instanceof Error\n ? retryErr\n : new Error(String(retryErr), { cause: firstErr });\n });\n }\n}\n\n/**\n * The Feishu SDK's EventDispatcher passes handlers a payload that may or may\n * not include the outer envelope. Rebuild a v2-shaped envelope so the channel\n * webhook can parse it the same way it parses a raw Feishu POST.\n */\nexport function rebuildEnvelopeFromSdkEvent(\n eventType: string,\n data: unknown,\n ctx: { appId: string; verificationToken: string },\n): LarkEvent {\n const maybeHeader = (data as { header?: Record<string, unknown> })?.header;\n const header =\n maybeHeader && typeof maybeHeader === \"object\"\n ? maybeHeader\n : {\n event_id: `lc_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,\n event_type: eventType,\n create_time: String(Math.floor(Date.now() / 1000)),\n token: ctx.verificationToken,\n app_id: ctx.appId,\n };\n const maybeEvent = (data as { event?: unknown })?.event;\n return {\n schema: \"2.0\",\n header,\n event: maybeEvent ?? data,\n };\n}\n\nexport interface StartLongConnectionArgs {\n resolved: ResolvedLarkOptions;\n eveWebhookUrl: string;\n /** Override logger. */\n log?: ((msg: string) => void) | undefined;\n logError?: ((msg: string, err?: unknown) => void) | undefined;\n /** Test seam: inject a custom SDK module. */\n sdk?: unknown;\n}\n\n/**\n * Active connections keyed by `${appId}:${eveWebhookUrl}`.\n *\n * Eve's lifecycle can construct the channel module more than once (e.g.,\n * build-time scan + serve-time import, or HMR reload). Each construction\n * would naively start a fresh WSClient — Feishu then delivers every event\n * to BOTH connections, and the user sees double replies.\n *\n * Guard: if a connection for the same key is already running (or starting),\n * the second call resolves immediately without touching the SDK. On\n * failure, the slot is cleared so a retry can succeed.\n *\n * Single-process, so no lock is needed — `Map.has` + `Map.set` from the\n * same synchronous block is atomic.\n */\nconst activeConnections = new Map<string, Promise<void>>();\n\n/** @internal — test-only seam for resetting module state between cases. */\nexport function __resetLongConnectionSingletonsForTests(): void {\n activeConnections.clear();\n}\n\n/**\n * Start the Feishu WSClient side effect. Connects via the official SDK,\n * registers a handler that re-signs each event and POSTs it to the local eve\n * webhook. Resolves once the connection is established; the WSClient then\n * runs in the background for the lifetime of the process.\n *\n * Idempotent: a second call with the same `appId` + `eveWebhookUrl` is a\n * no-op (see {@link activeConnections}). Different keys (different app, or\n * different webhook URL — e.g. different `--port`) get separate WSClients.\n *\n * @throws if @larksuiteoapi/node-sdk is not installed, or the WSClient\n * fails to establish its first connection.\n */\nexport async function startLongConnection(args: StartLongConnectionArgs): Promise<void> {\n const key = `${args.resolved.appId}:${args.eveWebhookUrl}`;\n const existing = activeConnections.get(key);\n if (existing) {\n // Already running (or starting). Skip — never spawn a second WSClient\n // for the same Feishu app + webhook target.\n return;\n }\n\n const promise = (async () => {\n await doStartLongConnection(args);\n })().catch((e) => {\n // On failure, clear the slot so a future call can retry.\n activeConnections.delete(key);\n throw e;\n });\n\n // Synchronous set after the synchronous has-check above — atomic in JS's\n // single-threaded runtime. No race with another concurrent caller.\n activeConnections.set(key, promise);\n await promise;\n}\n\nasync function doStartLongConnection(args: StartLongConnectionArgs): Promise<void> {\n const log = args.log ?? ((m: string) => console.log(`[eve-lark] ${m}`));\n const logError = args.logError ?? ((m: string, e?: unknown) => console.error(`[eve-lark] ${m}`, e ?? \"\"));\n\n const sdk = (args.sdk ?? (await loadLarkSdk())) as LarkSdk;\n\n const dispatcher = new sdk.EventDispatcher({\n verificationToken: args.resolved.verificationToken,\n encryptKey: args.resolved.encryptKey,\n });\n\n dispatcher.register({\n \"im.message.receive_v1\": async (data: unknown) => {\n try {\n const envelope = rebuildEnvelopeFromSdkEvent(\"im.message.receive_v1\", data, {\n appId: args.resolved.appId,\n verificationToken: args.resolved.verificationToken,\n });\n await postEventToWebhookRetry(envelope, {\n eveWebhookUrl: args.eveWebhookUrl,\n encryptKey: args.resolved.encryptKey,\n });\n } catch (e) {\n logError(`forward failed (event dropped)`, e);\n }\n },\n });\n\n const domain = args.resolved.baseUrl.includes(\"larksuite.com\")\n ? sdk.Domain.Lark\n : sdk.Domain.Feishu;\n\n const wsClient = new sdk.WSClient({\n appId: args.resolved.appId,\n appSecret: args.resolved.appSecret,\n domain,\n onReady: () => log(`WS connected to Feishu (${args.resolved.baseUrl})`),\n onError: (err: Error) => logError(`WS error`, err),\n onReconnecting: () => log(`WS reconnecting…`),\n onReconnected: () => log(`WS reconnected`),\n autoReconnect: true,\n });\n\n await wsClient.start({ eventDispatcher: dispatcher });\n}\n\n/* eslint-disable @typescript-eslint/consistent-type-imports */\n// The `import()` type query is the canonical way to express \"the runtime\n// namespace of this dynamic import\" without making the SDK a hard dep.\ntype LarkSdk = typeof import(\"@larksuiteoapi/node-sdk\");\n/* eslint-enable @typescript-eslint/consistent-type-imports */\n\nasync function loadLarkSdk(): Promise<LarkSdk> {\n try {\n return await import(\"@larksuiteoapi/node-sdk\");\n } catch {\n throw new Error(\n \"eve-lark: mode:\\\"long-connection\\\" requires @larksuiteoapi/node-sdk. Install it: pnpm add @larksuiteoapi/node-sdk (or npm/yarn equivalent).\",\n );\n }\n}\n"],"mappings":"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","names":["text","createHash","createHash"]}
1
+ {"version":3,"sources":["../src/channel.ts","../src/errors.ts","../src/lark-client.ts","../src/dedup.ts","../src/crypto.ts","../src/parse.ts","../src/card.ts","../src/streaming-controller.ts","../src/options.ts","../src/long-connection.ts"],"sourcesContent":["import {\n defineChannel,\n POST,\n type Channel,\n type RouteHandlerArgs,\n} from \"eve/channels\";\n\nimport { LarkClient } from \"./lark-client.js\";\nimport { DedupMap } from \"./dedup.js\";\nimport { decryptPayload, verifySignature } from \"./crypto.js\";\nimport { parseInbound } from \"./parse.js\";\nimport { StreamingCardController } from \"./streaming-controller.js\";\nimport { buildTextCard } from \"./card.js\";\nimport { resolveOptions } from \"./options.js\";\nimport { startLongConnection } from \"./long-connection.js\";\nimport type {\n LarkChannelOptions,\n LarkContinuationToken,\n LarkEncryptedBody,\n LarkEventBody,\n LarkInboundFile,\n ResolvedLarkOptions,\n} from \"./types.js\";\n\n/** Hard cap on inbound webhook body size. Feishu payloads are <10 KB; this\n * is purely defense against a malicious or buggy peer OOMing the process. */\nconst MAX_BODY_BYTES = 1_000_000;\n\n/** Drop a session's controller if it's been inactive this long. Bounds the\n * closure-scoped `controllers`/`sessionMeta` Maps against crashes that\n * prevent `message.completed`/`turn.failed` from firing. */\nconst STALE_SESSION_MS = 30 * 60 * 1000;\n\n/** How often to sweep stale controllers. */\nconst SWEEP_INTERVAL_MS = 5 * 60 * 1000;\n\n/** Reply text used when the model returns null/empty — guarantees the user\n * always sees *something* so they're not left looking at a typing emoji. */\nconst EMPTY_REPLY_TEXT = \"(model returned no content)\";\n\n/**\n * Continuation token format: `${chatId}:${rootMessageId ?? \"_\"}`.\n * The framework prepends the channel file stem before handing the token to\n * the runtime; consumers should call this helper rather than concatenate.\n */\nexport function larkContinuationToken(\n chatId: string,\n rootMessageId: string | null,\n): LarkContinuationToken {\n return `${chatId}:${rootMessageId ?? \"_\"}` as LarkContinuationToken;\n}\n\ninterface LarkSessionMeta {\n chatId: string;\n rootId?: string | undefined;\n parentId?: string | undefined;\n /** The user message we ack-reacted to; needed to remove the reaction\n * after delivery. Same value as `ctx.session.auth.initiator.attributes.messageId`,\n * mirrored here so terminal handlers don't have to re-extract it. */\n messageId?: string | undefined;\n /** Reaction id returned by `addReaction`. Present once the ack-reaction\n * POST resolves, which may be after the first terminal event fires. */\n ackReactionId?: string | undefined;\n /** When the controller was last touched. Used by the stale-sweep. */\n touchedAt: number;\n}\n\ninterface ResolvedSessionInfo {\n chatId: string;\n rootId?: string | undefined;\n parentId?: string | undefined;\n messageId?: string | undefined;\n}\n\n/**\n * Extract chat + message metadata stashed on `auth.initiator.attributes` at\n * session start. This is the canonical place to read it: closure state\n * doesn't reliably cross eve's process/worker boundary, but auth attributes\n * are persisted with the session.\n */\nfunction sessionInfoFromCtx(ctx: { session?: { auth?: { initiator?: { attributes?: unknown } | null } | null } | null }): ResolvedSessionInfo | null {\n const attrs = (ctx.session?.auth?.initiator?.attributes ?? {}) as {\n chatId?: unknown;\n rootMessageId?: unknown;\n parentId?: unknown;\n messageId?: unknown;\n };\n if (typeof attrs.chatId !== \"string\" || !attrs.chatId) return null;\n return {\n chatId: attrs.chatId,\n rootId: typeof attrs.rootMessageId === \"string\" ? attrs.rootMessageId : undefined,\n parentId: typeof attrs.parentId === \"string\" ? attrs.parentId : undefined,\n messageId: typeof attrs.messageId === \"string\" ? attrs.messageId : undefined,\n };\n}\n\nfunction ackOk(): Response {\n return Response.json({ code: 0 });\n}\n\n/**\n * Resolve the configured `ackReaction` to a single emoji type for this event,\n * or `false` if reactions are disabled. Picks randomly when given an array.\n */\nfunction pickAckEmoji(reaction: string | readonly string[] | false): string | false {\n if (typeof reaction === \"string\") return reaction;\n if (Array.isArray(reaction)) {\n if (reaction.length === 0) return false;\n const idx = Math.floor(Math.random() * reaction.length);\n return reaction[idx] ?? false;\n }\n return false;\n}\n\nfunction resourceUrl(\n options: ResolvedLarkOptions,\n file: LarkInboundFile,\n messageId: string,\n): string {\n const type = file.kind === \"image\" ? \"image\" : \"file\";\n return `${options.baseUrl}/open-apis/im/v1/messages/${encodeURIComponent(messageId)}/resources/${encodeURIComponent(file.fileKey)}?type=${type}`;\n}\n\n/**\n * Build the eve UserContent payload from a parsed inbound event. Text comes\n * first; each inbound image/file becomes a `file` part carrying a URL pointing\n * at the Lark resource endpoint. The channel's `fetchFile` hook will stage\n * those URLs to bytes when the model runs.\n */\nfunction buildUserContent(\n text: string,\n files: LarkInboundFile[],\n options: ResolvedLarkOptions,\n messageId: string,\n): unknown[] {\n const parts: unknown[] = [];\n if (text.length > 0) parts.push({ type: \"text\", text });\n for (const f of files) {\n parts.push({\n type: \"file\",\n data: new URL(resourceUrl(options, f, messageId)),\n mediaType: f.mediaType,\n });\n }\n return parts;\n}\n\nfunction errMsgFrom(data: unknown, fallback: string): string {\n if (typeof data !== \"object\" || data === null) return fallback;\n const err = (data as { error?: unknown }).error;\n if (typeof err === \"string\") return err;\n if (typeof err === \"object\" && err !== null) {\n const msg = (err as { message?: unknown }).message;\n if (typeof msg === \"string\") return msg;\n }\n return fallback;\n}\n\n/**\n * Create a Lark/Feishu channel for the eve agent framework.\n *\n * The channel mounts a single POST webhook that verifies the request,\n * decrypts the body when an encrypt key is configured, deduplicates events\n * by id, parses the inbound message, and starts or resumes an eve session.\n *\n * Streaming happens via eve's native channel events: `message.appended`\n * drives live card patches, `message.completed` finalizes the card, and\n * `turn.failed` aborts it. In `replyMode: \"static\"` the controller is\n * skipped and `message.completed` delivers a single card.\n *\n * **Delivery guarantee**: every terminal event (`message.completed` or\n * `turn.failed`) delivers *something* to the user. If the streaming card\n * path fails, we fall back to a fresh card; if that fails, plain text; if\n * even that fails, the error is logged. The user is never left looking at\n * a typing-emoji reaction with no reply.\n */\nexport function createLarkChannel(\n optionsInput: LarkChannelOptions,\n): Channel<undefined, Record<string, unknown>, Record<string, unknown>> {\n const options = resolveOptions(optionsInput);\n const client = new LarkClient(options);\n const dedup = new DedupMap(options.dedupTtlMs, options.dedupMaxEntries);\n\n // Long-connection side effect: when mode is \"long-connection\" (the\n // default), start a Feishu WSClient in the background. Each inbound event\n // is re-signed and POSTed to this channel's webhook on localhost, where\n // the standard handler runs with full access to send() etc.\n if (options.mode === \"long-connection\") {\n const eveWebhookUrl = `http://127.0.0.1:${options.port}${options.webhookPath}`;\n void startLongConnection({ resolved: options, eveWebhookUrl }).catch((e) => {\n console.error(\"[eve-lark] long-connection startup failed:\", e);\n });\n }\n\n // Channel-scoped (closure) state. Each session has its own controller +\n // chat metadata, keyed by session.id. Bounded by the stale-sweep below.\n const controllers = new Map<string, StreamingCardController>();\n const sessionMeta = new Map<string, LarkSessionMeta>();\n\n function getController(sessionId: string, meta: ResolvedSessionInfo): StreamingCardController {\n let ctrl = controllers.get(sessionId);\n if (!ctrl) {\n ctrl = new StreamingCardController(client, {\n chatId: meta.chatId,\n rootId: meta.rootId,\n parentId: meta.parentId,\n patchIntervalMs: options.streamPatchIntervalMs,\n createThresholdMs: options.streamCreateThresholdMs,\n });\n controllers.set(sessionId, ctrl);\n }\n if (sessionMeta.has(sessionId)) {\n sessionMeta.get(sessionId)!.touchedAt = Date.now();\n } else {\n sessionMeta.set(sessionId, {\n chatId: meta.chatId,\n rootId: meta.rootId,\n parentId: meta.parentId,\n messageId: meta.messageId,\n touchedAt: Date.now(),\n });\n }\n return ctrl;\n }\n\n function dropController(sessionId: string): void {\n controllers.delete(sessionId);\n sessionMeta.delete(sessionId);\n }\n\n /** Best-effort ack-reaction cleanup. Called from terminal handlers. */\n async function cleanupAckReaction(sessionId: string): Promise<void> {\n const meta = sessionMeta.get(sessionId);\n if (!meta?.ackReactionId || !meta.messageId) return;\n try {\n await client.removeReaction({\n messageId: meta.messageId,\n reactionId: meta.ackReactionId,\n });\n } catch (e) {\n console.warn(\n \"[eve-lark] ack reaction cleanup failed:\",\n e instanceof Error ? e.message : e,\n );\n }\n }\n\n /**\n * Cascade-deliver a reply to the user. Tries (in order):\n * 1. streaming controller finalize (patches existing card OR creates one\n * with the full text — used when message.appended already started a\n * card, or as a one-shot in streaming mode for very short turns)\n * 2. fresh sendCard (static mode, or streaming finalize failed)\n * 3. sendText (card POST rejected)\n * Each failure logs; we never throw out of here.\n */\n async function deliverReply(sessionId: string, info: ResolvedSessionInfo, text: string): Promise<void> {\n if (options.replyMode === \"streaming\") {\n const ctrl = controllers.get(sessionId) ?? getController(sessionId, info);\n try {\n await ctrl.finalize(text);\n console.log(`[eve-lark] delivered via streaming finalize (sessionId=${sessionId})`);\n return;\n } catch (e) {\n console.warn(\n `[eve-lark] streaming finalize failed; falling back to fresh card (sessionId=${sessionId}):`,\n e instanceof Error ? e.message : e,\n );\n }\n }\n\n try {\n await client.sendCard({\n chatId: info.chatId,\n card: buildTextCard(text),\n rootId: info.rootId,\n parentId: info.parentId,\n });\n console.log(`[eve-lark] delivered via sendCard (sessionId=${sessionId})`);\n return;\n } catch (cardErr) {\n console.warn(\n `[eve-lark] sendCard failed; falling back to plain text (sessionId=${sessionId}):`,\n cardErr instanceof Error ? cardErr.message : cardErr,\n );\n }\n\n try {\n await client.sendText({\n chatId: info.chatId,\n content: text,\n rootId: info.rootId,\n parentId: info.parentId,\n });\n console.log(`[eve-lark] delivered via sendText fallback (sessionId=${sessionId})`);\n } catch (textErr) {\n console.error(\n `[eve-lark] sendText fallback ALSO failed; the user will not see this reply (sessionId=${sessionId}):`,\n textErr instanceof Error ? textErr.message : textErr,\n );\n }\n }\n\n // Lazy sweep: drop controllers whose session hasn't been touched in\n // STALE_SESSION_MS. Guards against the case where eve crashes mid-turn.\n let lastSweepAt = 0;\n function maybeSweep(): void {\n const now = Date.now();\n if (now - lastSweepAt < SWEEP_INTERVAL_MS) return;\n lastSweepAt = now;\n const cutoff = now - STALE_SESSION_MS;\n for (const [id, meta] of sessionMeta) {\n if (meta.touchedAt < cutoff) {\n controllers.delete(id);\n sessionMeta.delete(id);\n }\n }\n }\n\n const webhookHandler = async (\n req: Request,\n helpers: RouteHandlerArgs[\"send\"] extends never ? never : RouteHandlerArgs,\n ): Promise<Response> => {\n maybeSweep();\n\n // 0) Body size cap — refuse gigantic bodies before allocating.\n const contentLength = Number(req.headers.get(\"content-length\") ?? \"0\");\n if (Number.isFinite(contentLength) && contentLength > MAX_BODY_BYTES) {\n return new Response(\"request body too large\", { status: 413 });\n }\n const rawBody = Buffer.from(await req.arrayBuffer());\n if (rawBody.byteLength > MAX_BODY_BYTES) {\n return new Response(\"request body too large\", { status: 413 });\n }\n\n // 1) Skew check (only enforced when a real timestamp header is present)\n const tsHeader = req.headers.get(\"x-lark-request-timestamp\") ?? \"\";\n const ts = Number(tsHeader);\n if (\n tsHeader &&\n Number.isFinite(ts) &&\n ts > 0 &&\n Math.abs(Date.now() / 1000 - ts) > options.signatureSkewMs / 1000\n ) {\n return new Response(\"request timestamp out of skew window\", { status: 408 });\n }\n\n // 2) Signature verify + AES decrypt when encryptKey configured\n let workingBody: Buffer = rawBody;\n if (options.encryptKey) {\n const nonce = req.headers.get(\"x-lark-request-nonce\") ?? \"\";\n const sigHeader = req.headers.get(\"x-lark-signature\");\n if (!sigHeader) return new Response(\"missing signature\", { status: 401 });\n const ok = verifySignature({\n timestamp: tsHeader,\n nonce,\n encryptKey: options.encryptKey,\n rawBody,\n signatureHeader: sigHeader,\n });\n if (!ok) return new Response(\"bad signature\", { status: 401 });\n\n try {\n const envelope = JSON.parse(rawBody.toString(\"utf8\")) as LarkEncryptedBody;\n if (envelope.encrypt) {\n workingBody = decryptPayload(envelope.encrypt, options.encryptKey) as Buffer;\n }\n } catch {\n return new Response(\"decrypt failed\", { status: 400 });\n }\n }\n\n // 3) Parse body\n let body: LarkEventBody;\n try {\n body = JSON.parse(workingBody.toString(\"utf8\")) as LarkEventBody;\n } catch {\n return new Response(\"invalid json\", { status: 400 });\n }\n\n // 4) url_verification short-circuit\n if (body.type === \"url_verification\") {\n return Response.json({ challenge: body.challenge ?? \"\" });\n }\n\n // 5) Schema check\n if (body.schema !== \"2.0\") {\n return ackOk();\n }\n\n // 6) Verification-token check\n if (body.header?.token !== options.verificationToken) {\n return new Response(\"verification token mismatch\", { status: 401 });\n }\n\n // 7) Dedup\n const dedupKey = body.header?.event_id ?? body.event?.message?.message_id;\n if (dedupKey) {\n if (dedup.has(dedupKey)) return ackOk();\n dedup.set(dedupKey);\n }\n\n // 8) Event filter — only handle text messages in v1\n if (body.header?.event_type !== \"im.message.receive_v1\") {\n return ackOk();\n }\n if (!body.event) return ackOk();\n\n // 9) Parse\n const parsed = parseInbound(body.event, options.botOpenId);\n\n // 10) Self-message suppression\n if (parsed.senderType === \"app\") {\n return ackOk();\n }\n\n // 11) Skip unsupported message types\n if (parsed.text === \"\" && parsed.files.length === 0) {\n return ackOk();\n }\n\n // 12) Build session inputs\n const userContent = buildUserContent(parsed.text, parsed.files, options, parsed.messageId);\n const continuationToken = larkContinuationToken(parsed.chatId, parsed.parentId ?? parsed.rootId);\n const auth = {\n authenticator: \"lark\",\n principalType: \"user\",\n principalId: parsed.senderOpenId,\n attributes: {\n chatId: parsed.chatId,\n rootMessageId: parsed.rootId,\n messageId: parsed.messageId,\n chatType: parsed.chatType,\n },\n };\n\n // 13) Start/resume session.\n const session = await helpers.send(userContent as never, {\n auth: auth as never,\n continuationToken,\n });\n\n // 14) Remember chat metadata keyed by session.id so terminal handlers\n // can look up where to deliver replies and which reaction to clean up.\n sessionMeta.set(session.id, {\n chatId: parsed.chatId,\n rootId: parsed.rootId ?? undefined,\n parentId: parsed.parentId ?? undefined,\n messageId: parsed.messageId,\n touchedAt: Date.now(),\n });\n\n // 15) Ack reaction — fire-and-forget. Stash the resulting reaction id\n // so terminal handlers can remove it once the reply has been delivered.\n const emoji = pickAckEmoji(options.ackReaction);\n if (emoji) {\n const sessionId = session.id;\n helpers.waitUntil(\n client\n .addReaction({ messageId: parsed.messageId, emojiType: emoji })\n .then(({ reactionId }) => {\n const m = sessionMeta.get(sessionId);\n if (m) m.ackReactionId = reactionId;\n })\n .catch((e) => {\n console.warn(\n \"[eve-lark] ack reaction failed:\",\n e instanceof Error ? e.message : e,\n );\n }),\n );\n }\n\n return ackOk();\n };\n\n return defineChannel({\n routes: [POST(options.webhookPath, webhookHandler as never)],\n\n fetchFile: async (url: string) => {\n if (!url.startsWith(options.baseUrl)) return null;\n const m = url.match(/\\/messages\\/([^/]+)\\/resources\\/([^?]+)\\?type=(image|file)/);\n if (!m || !m[1] || !m[2] || !m[3]) return null;\n return client.downloadResource({\n messageId: m[1],\n fileKey: m[2],\n type: m[3] as \"image\" | \"file\",\n });\n },\n\n events: {\n // Streaming delta — patch the card.\n \"message.appended\"(data, _channel, ctx) {\n if (options.replyMode !== \"streaming\") return;\n const sessionId = ctx.session.id;\n const info = sessionInfoFromCtx(ctx);\n if (!info) return;\n const d = data as { messageDelta?: string };\n if (typeof d.messageDelta !== \"string\") return;\n const ctrl = getController(sessionId, info);\n ctrl.appendDelta(d.messageDelta);\n },\n\n // Terminal — deliver the final reply, then clean up the ack reaction.\n async \"message.completed\"(data, _channel, ctx) {\n const sessionId = ctx.session.id;\n const info = sessionInfoFromCtx(ctx);\n if (!info) {\n console.warn(`[eve-lark] message.completed: no session info, cannot deliver (sessionId=${sessionId})`);\n return;\n }\n const d = data as { message?: string | null };\n const rawText = typeof d.message === \"string\" ? d.message : \"\";\n console.log(\n `[eve-lark] message.completed sessionId=${sessionId} chatId=${info.chatId} msgLen=${rawText.length}`,\n );\n const text = rawText.length > 0 ? rawText : EMPTY_REPLY_TEXT;\n\n try {\n await deliverReply(sessionId, info, text);\n } finally {\n await cleanupAckReaction(sessionId);\n dropController(sessionId);\n }\n },\n\n async \"turn.failed\"(data, _channel, ctx) {\n const sessionId = ctx?.session?.id;\n if (!sessionId) {\n console.warn(\"[eve-lark] turn.failed: no sessionId on ctx\");\n return;\n }\n const info = sessionInfoFromCtx(ctx);\n if (!info) {\n console.warn(`[eve-lark] turn.failed: no session info (sessionId=${sessionId})`);\n return;\n }\n const errMsg = errMsgFrom(data, \"turn failed\");\n console.warn(\n `[eve-lark] turn.failed sessionId=${sessionId} chatId=${info.chatId} err=\"${errMsg.slice(0, 200)}\"`,\n );\n const userText = `⚠ ${errMsg}`;\n\n // If a streaming card already exists, abort patches it with the\n // error — the user sees the failure in-place. Otherwise deliverReply\n // sends a fresh error card / text. Either way the user sees the\n // error, never a silent typing-emoji dead end.\n const ctrl = controllers.get(sessionId);\n if (ctrl) {\n try {\n await ctrl.abort(errMsg);\n console.log(`[eve-lark] error shown via streaming abort (sessionId=${sessionId})`);\n } catch (e) {\n console.warn(\n `[eve-lark] turn.failed: streaming abort failed, will deliver fresh error (sessionId=${sessionId}):`,\n e instanceof Error ? e.message : e,\n );\n try {\n await deliverReply(sessionId, info, userText);\n } catch {\n // deliverReply swallows internally; unreachable.\n }\n }\n } else {\n try {\n await deliverReply(sessionId, info, userText);\n } catch {\n // unreachable\n }\n }\n\n await cleanupAckReaction(sessionId);\n dropController(sessionId);\n },\n\n async \"session.failed\"(data) {\n // `session.failed` carries no `ctx`, so we can't tell which chat\n // this is. Per-session delivery happens via `turn.failed`; this\n // event is informational only.\n const errMsg = errMsgFrom(data, \"session failed\");\n console.error(\"[eve-lark] session.failed:\", errMsg);\n },\n },\n });\n}\n","/**\n * Typed error hierarchy for eve-lark.\n *\n * All errors extend a common base so consumers can `instanceof LarkChannelError`\n * to catch anything thrown by the channel.\n */\n\nexport class LarkChannelError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = new.target.name;\n }\n}\n\nexport class LarkConfigError extends LarkChannelError {}\n\nexport class LarkSignatureError extends LarkChannelError {}\n\nexport class LarkDecryptError extends LarkChannelError {}\n\nexport interface LarkApiErrorBody {\n code?: number | undefined;\n msg?: string | undefined;\n}\n\nexport class LarkApiError extends LarkChannelError {\n readonly code: number | undefined;\n readonly body: LarkApiErrorBody | undefined;\n readonly status: number | undefined;\n\n constructor(\n message: string,\n opts?: {\n code?: number | undefined;\n body?: LarkApiErrorBody | undefined;\n status?: number | undefined;\n cause?: unknown;\n },\n ) {\n super(message, { cause: opts?.cause });\n this.code = opts?.code;\n this.body = opts?.body;\n this.status = opts?.status;\n }\n}\n","import { LarkApiError, type LarkApiErrorBody } from \"./errors.js\";\nimport type { LarkCard, ResolvedLarkOptions } from \"./types.js\";\n\ninterface TokenState {\n value: string;\n expiresAt: number;\n}\n\nconst TOKEN_INVALID_CODES = new Set<number>([99991663, 99991664, 99991661]);\n\ninterface RequestResult {\n status: number;\n body: unknown;\n retryAfter: number | null;\n}\n\nexport class LarkClient {\n private readonly options: ResolvedLarkOptions;\n private token: TokenState | null = null;\n private refreshPromise: Promise<string> | null = null;\n\n constructor(options: ResolvedLarkOptions) {\n this.options = options;\n }\n\n async getTenantAccessToken(): Promise<string> {\n if (\n this.token &&\n Date.now() + this.options.tokenRefreshBufferMs < this.token.expiresAt\n ) {\n return this.token.value;\n }\n if (this.refreshPromise) return this.refreshPromise;\n this.refreshPromise = this.#refresh();\n try {\n return await this.refreshPromise;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n async #refresh(): Promise<string> {\n const body = {\n app_id: this.options.appId,\n app_secret: this.options.appSecret,\n };\n const res = await this.options.fetch(\n `${this.options.baseUrl}/open-apis/auth/v3/tenant_access_token/internal`,\n {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(this.options.requestTimeoutMs),\n },\n );\n if (!res.ok) {\n throw new LarkApiError(\n `eve-lark: token refresh failed (HTTP ${res.status})`,\n { status: res.status },\n );\n }\n const json = (await res.json()) as { code?: number; tenant_access_token?: string; expire?: number; msg?: string };\n if (json.code !== 0 || !json.tenant_access_token) {\n throw new LarkApiError(\n `eve-lark: token refresh returned code=${json.code ?? \"?\"} msg=${json.msg ?? \"?\"}`,\n { body: json, code: json.code },\n );\n }\n const expireSec = typeof json.expire === \"number\" ? json.expire : 7200;\n this.token = {\n value: json.tenant_access_token,\n expiresAt: Date.now() + expireSec * 1000,\n };\n return this.token.value;\n }\n\n async sendText(args: {\n chatId: string;\n content: string;\n rootId?: string;\n parentId?: string;\n }): Promise<{ messageId: string }> {\n const content = JSON.stringify({ text: args.content });\n return this.#sendMessage({\n receive_id: args.chatId,\n msg_type: \"text\",\n content,\n root_id: args.rootId,\n parent_id: args.parentId,\n });\n }\n\n async sendCard(args: {\n chatId: string;\n card: LarkCard;\n rootId?: string;\n parentId?: string;\n }): Promise<{ messageId: string }> {\n const content = JSON.stringify(args.card);\n return this.#sendMessage({\n receive_id: args.chatId,\n msg_type: \"interactive\",\n content,\n root_id: args.rootId,\n parent_id: args.parentId,\n });\n }\n\n async #sendMessage(body: Record<string, unknown>): Promise<{ messageId: string }> {\n const payload = Object.fromEntries(\n Object.entries(body).filter(([, v]) => v !== undefined),\n );\n const json = await this.#request(\"POST\", \"/open-apis/im/v1/messages?receive_id_type=chat_id\", payload);\n const messageId = (json as { data?: { message_id?: string } }).data?.message_id;\n if (!messageId) {\n throw new LarkApiError(\"eve-lark: send missing message_id in response\", {\n body: json as LarkApiErrorBody,\n });\n }\n return { messageId };\n }\n\n async patchCard(args: { messageId: string; card: LarkCard }): Promise<void> {\n await this.#request(\n \"PATCH\",\n `/open-apis/im/v1/messages/${encodeURIComponent(args.messageId)}`,\n { content: JSON.stringify(args.card) },\n );\n }\n\n async downloadResource(args: {\n messageId: string;\n fileKey: string;\n type: \"image\" | \"file\";\n }): Promise<Buffer> {\n const path = `/open-apis/im/v1/messages/${encodeURIComponent(args.messageId)}/resources/${encodeURIComponent(args.fileKey)}?type=${args.type}`;\n const token = await this.getTenantAccessToken();\n const res = await this.options.fetch(`${this.options.baseUrl}${path}`, {\n method: \"GET\",\n headers: { authorization: `Bearer ${token}` },\n signal: AbortSignal.timeout(this.options.requestTimeoutMs),\n });\n if (!res.ok) {\n throw new LarkApiError(\n `eve-lark: downloadResource HTTP ${res.status}`,\n { status: res.status },\n );\n }\n return Buffer.from(await res.arrayBuffer());\n }\n\n async addReaction(args: {\n messageId: string;\n emojiType: string;\n }): Promise<{ reactionId: string }> {\n const path = `/open-apis/im/v1/messages/${encodeURIComponent(args.messageId)}/reactions`;\n const json = (await this.#request(\"POST\", path, {\n reaction_type: { emoji_type: args.emojiType },\n })) as { data?: { reaction_id?: string } };\n const reactionId = json.data?.reaction_id;\n if (!reactionId) {\n throw new LarkApiError(\"eve-lark: addReaction missing reaction_id\", {\n body: json as LarkApiErrorBody,\n });\n }\n return { reactionId };\n }\n\n async removeReaction(args: { messageId: string; reactionId: string }): Promise<void> {\n const path = `/open-apis/im/v1/messages/${encodeURIComponent(args.messageId)}/reactions/${encodeURIComponent(args.reactionId)}`;\n await this.#request(\"DELETE\", path, undefined);\n }\n\n /**\n * Central request wrapper with auth, retry, and Feishu error decoding.\n *\n * Retry policy:\n * - 429 (rate limit): always retry with `Retry-After` backoff. Safe —\n * server rejected the request before processing.\n * - 5xx: retry ONLY for idempotent methods (GET / PATCH / DELETE). POST\n * is NOT retried on 5xx because Feishu's POST /messages and POST\n * /reactions are non-idempotent — the server may have created the\n * resource before returning the error, and retrying would silently\n * double-send.\n * - 401 / token-invalid code: refresh and retry once.\n * - Other 4xx: throw LarkApiError with the Feishu code/msg.\n */\n async #request(method: string, path: string, body: unknown): Promise<unknown> {\n const url = `${this.options.baseUrl}${path}`;\n let token = await this.getTenantAccessToken();\n let tokenRefreshed = false;\n const methodNorm = method.toUpperCase();\n const retryableMethod = methodNorm !== \"POST\";\n\n for (let attempt = 0; attempt <= this.options.maxRetries; attempt++) {\n const res = await this.options.fetch(url, {\n method,\n headers: {\n authorization: `Bearer ${token}`,\n \"content-type\": \"application/json\",\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n signal: AbortSignal.timeout(this.options.requestTimeoutMs),\n });\n\n const result = await this.#consumeResponse(res);\n const status = result.status;\n\n if (status >= 200 && status < 300) {\n const jsonBody = result.body as { code?: number; msg?: string };\n if (jsonBody && typeof jsonBody.code === \"number\" && jsonBody.code !== 0) {\n if (TOKEN_INVALID_CODES.has(jsonBody.code) && !tokenRefreshed) {\n this.token = null;\n token = await this.getTenantAccessToken();\n tokenRefreshed = true;\n attempt -= 1;\n continue;\n }\n throw new LarkApiError(\n `eve-lark: ${method} ${path} failed code=${jsonBody.code} msg=${jsonBody.msg ?? \"?\"}`,\n { code: jsonBody.code, body: jsonBody as LarkApiErrorBody, status },\n );\n }\n return result.body;\n }\n\n if (status === 401 && !tokenRefreshed) {\n this.token = null;\n token = await this.getTenantAccessToken();\n tokenRefreshed = true;\n attempt -= 1;\n continue;\n }\n\n const isRateLimited = status === 429;\n const isServerErr = status >= 500 && status < 600;\n const retryable = isRateLimited || (isServerErr && retryableMethod);\n if (retryable && attempt < this.options.maxRetries) {\n const delayMs = this.#computeBackoff(status, result.retryAfter, attempt);\n await sleep(delayMs);\n continue;\n }\n\n const bodyObj = result.body as LarkApiErrorBody | undefined;\n const code = bodyObj?.code;\n const msg = bodyObj?.msg;\n const detail = msg ? ` code=${code ?? \"?\"} msg=${msg}` : \"\";\n throw new LarkApiError(\n `eve-lark: ${method} ${path} failed HTTP ${status}${detail}`,\n { status, body: bodyObj, code },\n );\n }\n throw new LarkApiError(`eve-lark: ${method} ${path} exhausted retries`);\n }\n\n async #consumeResponse(res: Response): Promise<RequestResult> {\n const retryAfterRaw = res.headers.get(\"retry-after\");\n const retryAfter = retryAfterRaw ? parseRetryAfter(retryAfterRaw) : null;\n const text = await res.text();\n if (!text) {\n return { status: res.status, body: undefined, retryAfter };\n }\n try {\n return { status: res.status, body: JSON.parse(text), retryAfter };\n } catch {\n return { status: res.status, body: { raw: text }, retryAfter };\n }\n }\n\n #computeBackoff(status: number, retryAfter: number | null, attempt: number): number {\n if (status === 429 && retryAfter !== null) {\n return Math.min(retryAfter * 1000, 10_000);\n }\n const base = 300 * Math.pow(2, attempt);\n const jitter = base * 0.2 * (Math.random() * 2 - 1);\n return Math.max(0, Math.round(base + jitter));\n }\n}\n\nfunction parseRetryAfter(raw: string): number | null {\n const sec = Number(raw);\n if (Number.isFinite(sec) && sec >= 0) return sec;\n const date = Date.parse(raw);\n if (Number.isFinite(date)) return Math.max(0, (date - Date.now()) / 1000);\n return null;\n}\n\nfunction sleep(ms: number): Promise<void> {\n if (ms <= 0) return Promise.resolve();\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * In-process deduplication for Feishu webhook events.\n *\n * Feishu retries delivery on non-2xx responses and during brief outage windows,\n * so consumers must idempotently ack events they've already seen. We key by\n * `header.event_id` (or `message.message_id` as a fallback) and remember each\n * key for the TTL window.\n *\n * Backed by an insertion-ordered Map so FIFO eviction is O(1) at the front.\n * Lazy sweep on every insert prevents unbounded growth of expired entries;\n * no `setInterval` so this is safe in serverless.\n */\nexport class DedupMap {\n private readonly entries = new Map<string, number>();\n private readonly ttlMs: number;\n private readonly maxEntries: number;\n private insertsSinceSweep = 0;\n\n constructor(ttlMs: number, maxEntries: number) {\n this.ttlMs = ttlMs;\n this.maxEntries = maxEntries;\n }\n\n has(key: string): boolean {\n const at = this.entries.get(key);\n if (at === undefined) return false;\n if (Date.now() - at > this.ttlMs) {\n this.entries.delete(key);\n return false;\n }\n return true;\n }\n\n set(key: string): void {\n this.maybeSweep();\n // Refresh timestamp by re-inserting at the tail of insertion order.\n this.entries.delete(key);\n this.entries.set(key, Date.now());\n\n while (this.entries.size > this.maxEntries) {\n const oldestKey = this.entries.keys().next().value;\n if (oldestKey === undefined) break;\n this.entries.delete(oldestKey);\n }\n }\n\n /**\n * Walk the insertion-ordered map from the front and drop expired entries.\n * Stops at the first non-expired entry since events arrive roughly in time\n * order. Called on every set, so cost is amortized.\n */\n private maybeSweep(): void {\n this.insertsSinceSweep += 1;\n if (this.insertsSinceSweep < 64 && this.entries.size < this.maxEntries) {\n return;\n }\n this.insertsSinceSweep = 0;\n const now = Date.now();\n for (const [key, at] of this.entries) {\n if (now - at <= this.ttlMs) break;\n this.entries.delete(key);\n }\n }\n}\n","import { createDecipheriv, createHash, timingSafeEqual } from \"node:crypto\";\nimport { LarkDecryptError } from \"./errors.js\";\n\n/**\n * Verify an `X-Lark-Signature` header against the raw webhook body.\n *\n * Feishu computes: `sha256(timestamp + nonce + encrypt_key + body)` and ships\n * the hex digest (optionally prefixed with `sha256=`) in `X-Lark-Signature`.\n * We concatenate the string parts first, then the raw bytes of the body, to\n * avoid a UTF-8 round-trip on the request body.\n *\n * Constant-time compare. Returns false on length mismatch instead of throwing.\n */\nexport function verifySignature(opts: {\n timestamp: string;\n nonce: string;\n encryptKey: string;\n rawBody: Buffer;\n signatureHeader: string;\n}): boolean {\n const expected = opts.signatureHeader.replace(/^sha256=/, \"\");\n const computed = createHash(\"sha256\")\n .update(opts.timestamp + opts.nonce + opts.encryptKey)\n .update(opts.rawBody)\n .digest(\"hex\");\n const a = Buffer.from(computed, \"hex\");\n const b = Buffer.from(expected, \"hex\");\n return a.length === b.length && timingSafeEqual(a, b);\n}\n\n/**\n * Decrypt the `encrypt` field from a Feishu webhook body.\n *\n * Layout:\n * key = SHA256(encrypt_key) // 32 bytes → AES-256\n * buf = base64decode(encrypt_field)\n * iv = buf[0:16]\n * ct = buf[16:] // AES-256-CBC ciphertext\n * plaintext = AES_256_CBC_decrypt(key, iv, ct) // PKCS#7 unpadded\n *\n * Returns the raw plaintext bytes. The caller is expected to JSON.parse them.\n */\nexport function decryptPayload(encryptB64: string, encryptKey: string): Buffer {\n const key = createHash(\"sha256\").update(encryptKey).digest();\n const buf = Buffer.from(encryptB64, \"base64\");\n\n if (buf.length < 32) {\n throw new LarkDecryptError(\n `eve-lark: ciphertext too short (${buf.length} bytes; need >= 32 for IV + one block)`,\n );\n }\n if ((buf.length - 16) % 16 !== 0) {\n throw new LarkDecryptError(\n `eve-lark: ciphertext length ${buf.length} is not 16 + N*16`,\n );\n }\n\n const iv = buf.subarray(0, 16);\n const ct = buf.subarray(16);\n const dec = createDecipheriv(\"aes-256-cbc\", key, iv);\n\n try {\n return Buffer.concat([dec.update(ct), dec.final()]);\n } catch (e) {\n throw new LarkDecryptError(\"eve-lark: AES decrypt failed (bad padding or wrong key)\", {\n cause: e,\n });\n }\n}\n","import type {\n LarkInboundEvent,\n LarkInboundFile,\n LarkInboundResult,\n LarkMention,\n LarkRawMention,\n} from \"./types.js\";\n\nconst MIME_BY_EXT: Record<string, string> = {\n pdf: \"application/pdf\",\n zip: \"application/zip\",\n gz: \"application/gzip\",\n tar: \"application/x-tar\",\n doc: \"application/msword\",\n docx: \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\",\n xls: \"application/vnd.ms-excel\",\n xlsx: \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\",\n ppt: \"application/vnd.ms-powerpoint\",\n pptx: \"application/vnd.openxmlformats-officedocument.presentationml.presentation\",\n csv: \"text/csv\",\n txt: \"text/plain\",\n md: \"text/markdown\",\n json: \"application/json\",\n xml: \"application/xml\",\n html: \"text/html\",\n htm: \"text/html\",\n png: \"image/png\",\n jpg: \"image/jpeg\",\n jpeg: \"image/jpeg\",\n gif: \"image/gif\",\n webp: \"image/webp\",\n mp3: \"audio/mpeg\",\n wav: \"audio/wav\",\n mp4: \"video/mp4\",\n mov: \"video/quicktime\",\n};\n\nfunction mimeFromExt(filename: string | undefined): string {\n if (!filename) return \"application/octet-stream\";\n const dot = filename.lastIndexOf(\".\");\n if (dot < 0) return \"application/octet-stream\";\n return MIME_BY_EXT[filename.slice(dot + 1).toLowerCase()] ?? \"application/octet-stream\";\n}\n\nfunction mentionFromRaw(m: LarkRawMention, botOpenId: string | undefined): LarkMention {\n const isOpenIdOfBot =\n !!botOpenId && !!m.id.open_id && m.id.open_id === botOpenId;\n const isAll = !!m.id.open_id && m.id.open_id === \"all\";\n return {\n key: m.key,\n id: {\n openId: m.id.open_id,\n userId: m.id.user_id,\n unionId: m.id.union_id,\n },\n name: m.name,\n idType: m.id_type ?? \"open_id\",\n isOpenIdOfBot,\n isAll,\n };\n}\n\nfunction stripBotMentions(text: string, mentions: LarkMention[]): string {\n // Feishu ships mentions as opaque placeholders (e.g. \"@_user_1\") in the text\n // body alongside a structured mentions array. Rewrite them to something the\n // model can read:\n // - the bot itself: dropped (the model already knows it's being addressed)\n // - @all: replaced with a literal \"@all\" token\n // - other users: replaced with \"@<display name>\"\n let out = text;\n for (const m of mentions) {\n if (!m.key) continue;\n if (m.isOpenIdOfBot) {\n out = out.split(m.key).join(\"\");\n } else if (m.isAll) {\n out = out.split(m.key).join(\"@all\");\n } else {\n out = out.split(m.key).join(`@${m.name}`);\n }\n }\n return out.replace(/\\s+/g, \" \").trim();\n}\n\ninterface ParsedContent {\n text: string;\n files: LarkInboundFile[];\n}\n\nfunction parseContent(messageType: string, rawContent: string): ParsedContent {\n if (!rawContent) return { text: \"\", files: [] };\n let content: Record<string, unknown>;\n try {\n content = JSON.parse(rawContent) as Record<string, unknown>;\n } catch {\n return { text: \"\", files: [] };\n }\n\n switch (messageType) {\n case \"text\": {\n const text = typeof content.text === \"string\" ? content.text : \"\";\n return { text, files: [] };\n }\n case \"image\": {\n const imageKey = typeof content.image_key === \"string\" ? content.image_key : \"\";\n if (!imageKey) return { text: \"\", files: [] };\n return {\n text: \"\",\n files: [{ fileKey: imageKey, mediaType: \"image/png\", kind: \"image\" }],\n };\n }\n case \"file\": {\n const fileKey = typeof content.file_key === \"string\" ? content.file_key : \"\";\n if (!fileKey) return { text: \"\", files: [] };\n const fileName = typeof content.file_name === \"string\" ? content.file_name : undefined;\n return {\n text: \"\",\n files: [{ fileKey, mediaType: mimeFromExt(fileName), kind: \"file\" }],\n };\n }\n case \"post\": {\n const locale = (content.zh_cn ?? content.en_us ?? content.ja_jp ?? null) as\n | { content?: unknown[][] }\n | null;\n if (!locale?.content) return { text: \"\", files: [] };\n const text = locale.content\n .flatMap((line) =>\n (line ?? [])\n .filter((node): node is { tag: string; text?: unknown } => {\n if (typeof node !== \"object\" || node === null) return false;\n const tag = (node as { tag?: unknown }).tag;\n const text = (node as { text?: unknown }).text;\n return tag === \"text\" && typeof text === \"string\";\n })\n .map((node) => node.text as string),\n )\n .join(\" \");\n return { text, files: [] };\n }\n default:\n // audio, media, sticker, share_chat, share_user, interactive — not in v1 scope.\n return { text: \"\", files: [] };\n }\n}\n\nexport function parseInbound(\n event: LarkInboundEvent,\n botOpenId?: string,\n): LarkInboundResult {\n const messageType = event.message.message_type;\n const parsed = parseContent(messageType, event.message.content);\n const rawMentions = event.message.mentions ?? [];\n const mentions = rawMentions.map((m) => mentionFromRaw(m, botOpenId));\n\n const senderOpenId =\n event.sender.sender_id.open_id ??\n event.sender.sender_id.user_id ??\n event.sender.sender_id.union_id ??\n \"\";\n\n const text =\n messageType === \"text\"\n ? stripBotMentions(parsed.text, mentions)\n : parsed.text;\n\n const chatType = event.chat_type === \"group\" ? \"group\" : \"p2p\";\n const senderType = event.sender.sender_type === \"app\" ? \"app\" : \"user\";\n\n return {\n text,\n files: parsed.files,\n chatId: event.message.chat_id,\n rootId: event.message.root_id ?? null,\n parentId: event.message.parent_id ?? null,\n messageId: event.message.message_id,\n senderOpenId,\n senderType,\n chatType,\n mentions,\n };\n}\n","import type { LarkCard } from \"./types.js\";\n\nconst BASE_CONFIG = {\n wide_screen_mode: true,\n update_multi: true,\n} as const;\n\n/**\n * Build a simple single-shot card with the given markdown text. Renders via\n * `div` + `lark_md` so the font size is close to a native chat message\n * (the bare `markdown` element renders noticeably smaller).\n */\nexport function buildTextCard(text: string): LarkCard {\n return {\n config: { ...BASE_CONFIG },\n elements: [{ tag: \"div\", text: { tag: \"lark_md\", content: text } }],\n };\n}\n\n/**\n * Build a streaming card with an optional status prefix and an answer buffer.\n *\n * Format:\n * <optional status prefix in muted tone>\n * <buffer>\n */\nexport function buildStreamingCard(opts: { buffer: string; status?: string | undefined }): LarkCard {\n const lines: string[] = [];\n if (opts.status) {\n lines.push(`<font color='grey'>${opts.status}</font>`);\n }\n lines.push(opts.buffer.length > 0 ? opts.buffer : \"_…_\");\n return {\n config: { ...BASE_CONFIG },\n elements: [{ tag: \"div\", text: { tag: \"lark_md\", content: lines.join(\"\\n\\n\") } }],\n };\n}\n\n/**\n * Build an error card displayed when a turn fails. `message` is rendered\n * verbatim under a red warning glyph — the caller is responsible for\n * prefixing/shape (most callers pass the raw error string and we wrap it).\n */\nexport function buildErrorCard(message: string): LarkCard {\n return {\n config: { ...BASE_CONFIG },\n elements: [\n { tag: \"div\", text: { tag: \"lark_md\", content: `<font color='red'>⚠ ${message}</font>` } },\n ],\n };\n}\n","import { buildErrorCard, buildStreamingCard, buildTextCard } from \"./card.js\";\nimport type { LarkCard } from \"./types.js\";\n\ntype State = \"idle\" | \"creating\" | \"streaming\" | \"completed\" | \"aborted\";\n\ninterface ControllerDeps {\n chatId: string;\n rootId?: string | undefined;\n parentId?: string | undefined;\n patchIntervalMs: number;\n createThresholdMs: number;\n}\n\ninterface LarkClientLike {\n sendCard(args: {\n chatId: string;\n card: LarkCard;\n rootId?: string;\n parentId?: string;\n }): Promise<{ messageId: string }>;\n patchCard(args: { messageId: string; card: LarkCard }): Promise<void>;\n sendText(args: {\n chatId: string;\n content: string;\n rootId?: string;\n parentId?: string;\n }): Promise<{ messageId: string }>;\n}\n\n/**\n * Streaming interactive-card state machine.\n *\n * idle ──first delta──> creating ──sendCard ok──> streaming ──finalize──> completed\n * │\n * └──sendCard fail──> aborted (flag; static fallback on message.completed)\n *\n * The card is created lazily after `createThresholdMs` of the first delta, so\n * short turns can short-circuit straight to `finalize` (which sends the card\n * with the full answer in one shot). Once streaming, patches are throttled to\n * `patchIntervalMs`.\n *\n * If `sendCard` fails on creation, the controller flips to `fallbackToText`\n * and `finalize`/`ensureFinalized` deliver via `sendText` instead.\n */\nexport class StreamingCardController {\n private readonly deps: ControllerDeps;\n private readonly client: LarkClientLike;\n\n private state: State = \"idle\";\n private buffer = \"\";\n private status: string | undefined;\n private messageId: string | undefined;\n private fallbackToText = false;\n\n private createTimer: ReturnType<typeof setTimeout> | null = null;\n private patchTimer: ReturnType<typeof setTimeout> | null = null;\n private patchInFlight: Promise<void> | null = null;\n private patchScheduled = false;\n private lastPatchAt = 0;\n\n constructor(client: LarkClientLike, deps: ControllerDeps) {\n this.client = client;\n this.deps = deps;\n }\n\n appendDelta(text: string): void {\n if (this.state === \"completed\" || this.state === \"aborted\") return;\n this.buffer += text;\n if (this.state === \"idle\") {\n this.scheduleCreate();\n } else if (this.state === \"streaming\") {\n this.schedulePatch();\n }\n }\n\n setStatus(status: string): void {\n if (this.state === \"completed\" || this.state === \"aborted\") return;\n this.status = status;\n if (this.state === \"streaming\") {\n this.schedulePatch();\n }\n }\n\n async finalize(fullText: string): Promise<void> {\n if (this.state === \"completed\" || this.state === \"aborted\") return;\n this.cancelCreateTimer();\n this.cancelPatchTimer();\n this.buffer = fullText;\n\n if (this.fallbackToText) {\n await this.client.sendText({\n chatId: this.deps.chatId,\n content: fullText,\n rootId: this.deps.rootId,\n parentId: this.deps.parentId,\n });\n this.state = \"completed\";\n return;\n }\n\n if (this.messageId === undefined) {\n // Never managed to create a card. Send one with the full text in a\n // single shot so the user still gets a card reply.\n try {\n const res = await this.client.sendCard({\n chatId: this.deps.chatId,\n card: buildTextCard(fullText),\n rootId: this.deps.rootId,\n parentId: this.deps.parentId,\n });\n this.messageId = res.messageId;\n this.state = \"completed\";\n } catch {\n // Last-resort fallback: plain text.\n this.fallbackToText = true;\n await this.client.sendText({\n chatId: this.deps.chatId,\n content: fullText,\n rootId: this.deps.rootId,\n parentId: this.deps.parentId,\n });\n this.state = \"completed\";\n }\n return;\n }\n\n // Card already exists; flush the final state.\n if (this.patchInFlight) {\n try {\n await this.patchInFlight;\n } catch {\n // swallow; we'll attempt the final patch below\n }\n }\n await this.client.patchCard({\n messageId: this.messageId,\n card: buildStreamingCard({ buffer: fullText, status: undefined }),\n });\n this.state = \"completed\";\n }\n\n async abort(error: string): Promise<void> {\n if (this.state === \"completed\" || this.state === \"aborted\") return;\n this.cancelCreateTimer();\n this.cancelPatchTimer();\n if (this.messageId === undefined) {\n // No card to patch; mark fallback and let finalize/ensureFinalized\n // deliver a plain-text error if asked.\n this.fallbackToText = true;\n this.state = \"aborted\";\n return;\n }\n try {\n await this.client.patchCard({\n messageId: this.messageId,\n card: buildErrorCard(error),\n });\n } finally {\n this.state = \"aborted\";\n }\n }\n\n async ensureFinalized(): Promise<void> {\n if (this.state !== \"completed\" && this.state !== \"aborted\") {\n await this.finalize(this.buffer);\n }\n }\n\n isStreaming(): boolean {\n return this.state === \"streaming\" || this.state === \"creating\";\n }\n\n isCompleted(): boolean {\n return this.state === \"completed\" || this.state === \"aborted\";\n }\n\n private scheduleCreate(): void {\n if (this.createTimer) return;\n this.state = \"creating\";\n this.createTimer = setTimeout(() => {\n this.createTimer = null;\n void this.doCreate();\n }, this.deps.createThresholdMs);\n }\n\n private cancelCreateTimer(): void {\n if (this.createTimer) {\n clearTimeout(this.createTimer);\n this.createTimer = null;\n }\n }\n\n private async doCreate(): Promise<void> {\n if (this.state !== \"creating\") return;\n try {\n const res = await this.client.sendCard({\n chatId: this.deps.chatId,\n card: buildStreamingCard({ buffer: this.buffer, status: this.status }),\n rootId: this.deps.rootId,\n parentId: this.deps.parentId,\n });\n this.messageId = res.messageId;\n this.state = \"streaming\";\n this.lastPatchAt = Date.now();\n } catch (e) {\n console.warn(\n \"[eve-lark] streaming card create failed; will deliver via plain text on finalize:\",\n e instanceof Error ? e.message : e,\n );\n this.fallbackToText = true;\n this.state = \"streaming\"; // keep accepting deltas; finalize will deliver as text\n }\n }\n\n private schedulePatch(): void {\n if (this.patchScheduled) return;\n this.patchScheduled = true;\n const elapsed = Date.now() - this.lastPatchAt;\n const wait = Math.max(0, this.deps.patchIntervalMs - elapsed);\n this.patchTimer = setTimeout(() => {\n this.patchTimer = null;\n this.patchScheduled = false;\n void this.maybeFlushPatch();\n }, wait);\n }\n\n private cancelPatchTimer(): void {\n if (this.patchTimer) {\n clearTimeout(this.patchTimer);\n this.patchTimer = null;\n }\n this.patchScheduled = false;\n }\n\n private async maybeFlushPatch(): Promise<void> {\n if (this.state !== \"streaming\") return;\n if (this.patchInFlight) return;\n if (this.messageId === undefined) return;\n const card = buildStreamingCard({ buffer: this.buffer, status: this.status });\n this.patchInFlight = this.client\n .patchCard({ messageId: this.messageId, card })\n .catch((e) => {\n // Best-effort: the next delta will retry. Log so operators can see\n // when the card stream is degraded.\n console.warn(\n \"[eve-lark] streaming card patch failed:\",\n e instanceof Error ? e.message : e,\n );\n })\n .finally(() => {\n this.patchInFlight = null;\n this.lastPatchAt = Date.now();\n });\n await this.patchInFlight;\n }\n}\n","import { LarkConfigError } from \"./errors.js\";\nimport type {\n LarkChannelOptions,\n LarkReplyMode,\n LarkTransportMode,\n ResolvedLarkOptions,\n} from \"./types.js\";\n\nconst DEFAULTS = {\n baseUrl: \"https://open.feishu.cn\",\n webhookPath: \"/lark/webhook\",\n replyMode: \"streaming\" as LarkReplyMode,\n streamPatchIntervalMs: 1000,\n streamCreateThresholdMs: 400,\n dedupTtlMs: 30 * 60 * 1000,\n dedupMaxEntries: 5000,\n requestTimeoutMs: 15000,\n maxRetries: 2,\n tokenRefreshBufferMs: 5 * 60 * 1000,\n signatureSkewMs: 5 * 60 * 1000,\n ackReaction: \"TYPING\" as string | false,\n mode: \"long-connection\" as LarkTransportMode,\n};\n\nconst ENV_KEYS = {\n appId: \"LARK_APP_ID\",\n appSecret: \"LARK_APP_SECRET\",\n verificationToken: \"LARK_VERIFICATION_TOKEN\",\n encryptKey: \"LARK_ENCRYPT_KEY\",\n baseUrl: \"LARK_BASE_URL\",\n botOpenId: \"LARK_BOT_OPEN_ID\",\n replyMode: \"LARK_REPLY_MODE\",\n mode: \"LARK_MODE\",\n} as const;\n\nexport type ResolveEnv = Record<string, string | undefined>;\n\nfunction defaultEnv(): ResolveEnv {\n if (typeof process !== \"undefined\" && process.env) {\n return process.env as ResolveEnv;\n }\n return {};\n}\n\nfunction pick(input: string | undefined, envValue: string | undefined): string | undefined {\n return input ?? envValue;\n}\n\nexport function resolveOptions(\n options: LarkChannelOptions,\n env: ResolveEnv = defaultEnv(),\n): ResolvedLarkOptions {\n const appId = pick(options.appId, env[ENV_KEYS.appId]);\n const appSecret = pick(options.appSecret, env[ENV_KEYS.appSecret]);\n const verificationToken = pick(\n options.verificationToken,\n env[ENV_KEYS.verificationToken],\n );\n\n if (!appId) {\n throw new LarkConfigError(\n `eve-lark: appId is required (option \\`appId\\` or env \\`${ENV_KEYS.appId}\\`)`,\n );\n }\n if (!appSecret) {\n throw new LarkConfigError(\n `eve-lark: appSecret is required (option \\`appSecret\\` or env \\`${ENV_KEYS.appSecret}\\`)`,\n );\n }\n if (!verificationToken) {\n throw new LarkConfigError(\n `eve-lark: verificationToken is required (option \\`verificationToken\\` or env \\`${ENV_KEYS.verificationToken}\\`)`,\n );\n }\n\n const rawBaseUrl = pick(options.baseUrl, env[ENV_KEYS.baseUrl]) ?? DEFAULTS.baseUrl;\n const baseUrl = rawBaseUrl.replace(/\\/+$/, \"\");\n\n const replyModeEnv = env[ENV_KEYS.replyMode];\n const replyMode: LarkReplyMode =\n options.replyMode ??\n (replyModeEnv === \"static\" || replyModeEnv === \"streaming\" ? replyModeEnv : DEFAULTS.replyMode);\n\n const modeEnv = env[ENV_KEYS.mode];\n const mode: LarkTransportMode =\n options.mode ??\n (modeEnv === \"webhook\" || modeEnv === \"long-connection\" ? modeEnv : DEFAULTS.mode);\n\n return {\n appId,\n appSecret,\n verificationToken,\n encryptKey: pick(options.encryptKey, env[ENV_KEYS.encryptKey]),\n baseUrl,\n botOpenId: pick(options.botOpenId, env[ENV_KEYS.botOpenId]),\n webhookPath: options.webhookPath ?? DEFAULTS.webhookPath,\n replyMode,\n streamPatchIntervalMs: options.streamPatchIntervalMs ?? DEFAULTS.streamPatchIntervalMs,\n streamCreateThresholdMs: options.streamCreateThresholdMs ?? DEFAULTS.streamCreateThresholdMs,\n dedupTtlMs: options.dedupTtlMs ?? DEFAULTS.dedupTtlMs,\n dedupMaxEntries: options.dedupMaxEntries ?? DEFAULTS.dedupMaxEntries,\n requestTimeoutMs: options.requestTimeoutMs ?? DEFAULTS.requestTimeoutMs,\n maxRetries: options.maxRetries ?? DEFAULTS.maxRetries,\n tokenRefreshBufferMs: options.tokenRefreshBufferMs ?? DEFAULTS.tokenRefreshBufferMs,\n signatureSkewMs: options.signatureSkewMs ?? DEFAULTS.signatureSkewMs,\n fetch: options.fetch ?? globalThis.fetch,\n ackReaction: options.ackReaction ?? DEFAULTS.ackReaction,\n mode,\n port:\n options.port ??\n (process.env.PORT ? Number(process.env.PORT) : 2000),\n };\n}\n","/**\n * Long-connection transport: when `mode: \"long-connection\"` is set on\n * {@link createLarkChannel} (the default), the channel starts a Feishu\n * `@larksuiteoapi/node-sdk` WSClient as a side effect of construction. Each\n * inbound event is re-encrypted + re-signed and POSTed to the channel's own\n * webhook on localhost, where the standard webhook handler runs (with full\n * access to `send()` etc.). This lets users run the bot against a real Feishu\n * app from `eve dev` alone — no public webhook URL, no second process.\n *\n * The SDK is a hard runtime dependency of this package (declared in\n * `dependencies`), so `pnpm add eve-lark` brings it in automatically. The\n * `import()` below is dynamic only so `mode: \"webhook\"` code paths don't\n * eagerly load the SDK at module import time.\n */\n\nimport {\n createCipheriv,\n createHash,\n randomBytes,\n} from \"node:crypto\";\nimport type { ResolvedLarkOptions } from \"./types.js\";\n\n/** A Feishu v2 envelope we forward to the channel webhook. */\nexport type LarkEvent = {\n schema?: string;\n type?: string;\n challenge?: string;\n token?: string;\n header?: Record<string, unknown>;\n event?: unknown;\n [k: string]: unknown;\n};\n\nexport interface PostEventOptions {\n eveWebhookUrl: string;\n /** When set, the body is AES-encrypted and the request is signed. */\n encryptKey?: string | undefined;\n /** Override for tests. Defaults to globalThis.fetch. */\n fetch?: typeof fetch | undefined;\n}\n\nfunction aesEncrypt(plaintext: Buffer, key: string): Buffer {\n const keyBuf = createHash(\"sha256\").update(key).digest();\n const iv = randomBytes(16);\n const cipher = createCipheriv(\"aes-256-cbc\", keyBuf, iv);\n return Buffer.concat([iv, cipher.update(plaintext), cipher.final()]);\n}\n\nfunction signBody(timestamp: string, nonce: string, body: Buffer, key: string): string {\n return createHash(\"sha256\")\n .update(timestamp + nonce + key)\n .update(body)\n .digest(\"hex\");\n}\n\n/**\n * Forward one Feishu event to the local eve webhook. Re-encrypts and signs\n * when an `encryptKey` is set, so the channel handler exercises its own\n * signature + AES pipeline on every event.\n */\nexport async function postEventToWebhook(\n event: LarkEvent,\n opts: PostEventOptions,\n): Promise<void> {\n const fetchImpl = opts.fetch ?? globalThis.fetch;\n const plain = Buffer.from(JSON.stringify(event), \"utf8\");\n let body: Buffer;\n const headers: Record<string, string> = { \"content-type\": \"application/json\" };\n\n if (opts.encryptKey) {\n const encrypted = aesEncrypt(plain, opts.encryptKey).toString(\"base64\");\n body = Buffer.from(JSON.stringify({ encrypt: encrypted }), \"utf8\");\n const ts = Math.floor(Date.now() / 1000).toString();\n const nonce = randomBytes(8).toString(\"hex\");\n const sig = signBody(ts, nonce, body, opts.encryptKey);\n headers[\"x-lark-request-timestamp\"] = ts;\n headers[\"x-lark-request-nonce\"] = nonce;\n headers[\"x-lark-signature\"] = `sha256=${sig}`;\n } else {\n body = plain;\n }\n\n const res = await fetchImpl(opts.eveWebhookUrl, {\n method: \"POST\",\n headers,\n body: new Uint8Array(body.buffer, body.byteOffset, body.byteLength) as never,\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(\n `eve-lark: forward to ${opts.eveWebhookUrl} failed (HTTP ${res.status})${text ? `: ${text.slice(0, 200)}` : \"\"}`,\n );\n }\n}\n\n/**\n * Post-event wrapper with a single exponential-backoff retry. Catches the\n * common case where `eve dev` is momentarily unavailable (between HMR\n * reloads, mid-restart, GC pause). Without this the event would be dropped\n * on the floor with just a log line — bad UX during dev.\n */\nexport async function postEventToWebhookRetry(\n event: LarkEvent,\n opts: PostEventOptions,\n): Promise<void> {\n try {\n await postEventToWebhook(event, opts);\n } catch (firstErr) {\n await new Promise((r) => setTimeout(r, 300));\n // Regenerate signature: timestamp/nonce moved on by ~300ms, and the\n // skew check at the channel handler would reject a stale signature.\n await postEventToWebhook(event, opts).catch((retryErr) => {\n throw retryErr instanceof Error\n ? retryErr\n : new Error(String(retryErr), { cause: firstErr });\n });\n }\n}\n\n/**\n * The Feishu SDK's EventDispatcher passes handlers a payload that may or may\n * not include the outer envelope. Rebuild a v2-shaped envelope so the channel\n * webhook can parse it the same way it parses a raw Feishu POST.\n */\nexport function rebuildEnvelopeFromSdkEvent(\n eventType: string,\n data: unknown,\n ctx: { appId: string; verificationToken: string },\n): LarkEvent {\n const maybeHeader = (data as { header?: Record<string, unknown> })?.header;\n const header =\n maybeHeader && typeof maybeHeader === \"object\"\n ? maybeHeader\n : {\n event_id: `lc_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,\n event_type: eventType,\n create_time: String(Math.floor(Date.now() / 1000)),\n token: ctx.verificationToken,\n app_id: ctx.appId,\n };\n const maybeEvent = (data as { event?: unknown })?.event;\n return {\n schema: \"2.0\",\n header,\n event: maybeEvent ?? data,\n };\n}\n\nexport interface StartLongConnectionArgs {\n resolved: ResolvedLarkOptions;\n eveWebhookUrl: string;\n /** Override logger. */\n log?: ((msg: string) => void) | undefined;\n logError?: ((msg: string, err?: unknown) => void) | undefined;\n /** Test seam: inject a custom SDK module. */\n sdk?: unknown;\n}\n\n/**\n * Active connections keyed by `${appId}:${eveWebhookUrl}`.\n *\n * Eve's lifecycle can construct the channel module more than once (e.g.,\n * build-time scan + serve-time import, or HMR reload). Each construction\n * would naively start a fresh WSClient — Feishu then delivers every event\n * to BOTH connections, and the user sees double replies.\n *\n * Guard: if a connection for the same key is already running (or starting),\n * the second call resolves immediately without touching the SDK. On\n * failure, the slot is cleared so a retry can succeed.\n *\n * The map lives on `globalThis` so it survives module reloads (HMR, build\n * then serve) — otherwise a reloaded module instance would have its own\n * fresh map and the guard would silently fail.\n *\n * Single-process, so no lock is needed — `Map.has` + `Map.set` from the\n * same synchronous block is atomic in JS's single-threaded runtime.\n */\nconst GLOBAL_KEY = \"__eveLarkActiveConnections\";\ntype GlobalWithLark = typeof globalThis & {\n [GLOBAL_KEY]?: Map<string, Promise<void>>;\n};\nfunction getActiveConnections(): Map<string, Promise<void>> {\n const g = globalThis as GlobalWithLark;\n if (!g[GLOBAL_KEY]) g[GLOBAL_KEY] = new Map();\n return g[GLOBAL_KEY]!;\n}\n\n/** @internal — test-only seam for resetting module state between cases. */\nexport function __resetLongConnectionSingletonsForTests(): void {\n getActiveConnections().clear();\n}\n\n/**\n * Start the Feishu WSClient side effect. Connects via the official SDK,\n * registers a handler that re-signs each event and POSTs it to the local eve\n * webhook. Resolves once the connection is established; the WSClient then\n * runs in the background for the lifetime of the process.\n *\n * Idempotent: a second call with the same `appId` + `eveWebhookUrl` is a\n * no-op (see {@link getActiveConnections}). Different keys (different app,\n * or different webhook URL — e.g. different `--port`) get separate WSClients.\n *\n * @throws if @larksuiteoapi/node-sdk is not installed, or the WSClient\n * fails to establish its first connection.\n */\nexport async function startLongConnection(args: StartLongConnectionArgs): Promise<void> {\n const key = `${args.resolved.appId}:${args.eveWebhookUrl}`;\n const activeConnections = getActiveConnections();\n const existing = activeConnections.get(key);\n if (existing) {\n console.log(\n `[eve-lark] startLongConnection: skip (already running) key=${key} pid=${process.pid}`,\n );\n return;\n }\n console.log(\n `[eve-lark] startLongConnection: start new WSClient key=${key} pid=${process.pid}`,\n );\n\n const promise = (async () => {\n await doStartLongConnection(args);\n })().catch((e) => {\n // On failure, clear the slot so a future call can retry.\n activeConnections.delete(key);\n throw e;\n });\n\n // Synchronous set after the synchronous has-check above — atomic in JS's\n // single-threaded runtime. No race with another concurrent caller.\n activeConnections.set(key, promise);\n await promise;\n}\n\nasync function doStartLongConnection(args: StartLongConnectionArgs): Promise<void> {\n const log = args.log ?? ((m: string) => console.log(`[eve-lark] ${m}`));\n const logError = args.logError ?? ((m: string, e?: unknown) => console.error(`[eve-lark] ${m}`, e ?? \"\"));\n\n const sdk = (args.sdk ?? (await loadLarkSdk())) as LarkSdk;\n\n const dispatcher = new sdk.EventDispatcher({\n verificationToken: args.resolved.verificationToken,\n encryptKey: args.resolved.encryptKey,\n });\n\n dispatcher.register({\n \"im.message.receive_v1\": async (data: unknown) => {\n try {\n const envelope = rebuildEnvelopeFromSdkEvent(\"im.message.receive_v1\", data, {\n appId: args.resolved.appId,\n verificationToken: args.resolved.verificationToken,\n });\n await postEventToWebhookRetry(envelope, {\n eveWebhookUrl: args.eveWebhookUrl,\n encryptKey: args.resolved.encryptKey,\n });\n } catch (e) {\n logError(`forward failed (event dropped)`, e);\n }\n },\n });\n\n const domain = args.resolved.baseUrl.includes(\"larksuite.com\")\n ? sdk.Domain.Lark\n : sdk.Domain.Feishu;\n\n const wsClient = new sdk.WSClient({\n appId: args.resolved.appId,\n appSecret: args.resolved.appSecret,\n domain,\n onReady: () => log(`WS connected to Feishu (${args.resolved.baseUrl})`),\n onError: (err: Error) => logError(`WS error`, err),\n onReconnecting: () => log(`WS reconnecting…`),\n onReconnected: () => log(`WS reconnected`),\n autoReconnect: true,\n });\n\n await wsClient.start({ eventDispatcher: dispatcher });\n}\n\n/* eslint-disable @typescript-eslint/consistent-type-imports */\n// The `import()` type query is the canonical way to express \"the runtime\n// namespace of this dynamic import\" without making the SDK a hard dep.\ntype LarkSdk = typeof import(\"@larksuiteoapi/node-sdk\");\n/* eslint-enable @typescript-eslint/consistent-type-imports */\n\nasync function loadLarkSdk(): Promise<LarkSdk> {\n try {\n return await import(\"@larksuiteoapi/node-sdk\");\n } catch {\n throw new Error(\n \"eve-lark: mode:\\\"long-connection\\\" requires @larksuiteoapi/node-sdk. Install it: pnpm add @larksuiteoapi/node-sdk (or npm/yarn equivalent).\",\n );\n }\n}\n"],"mappings":"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","names":["text","createHash","createHash"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eve-lark",
3
- "version": "0.2.4",
3
+ "version": "0.2.6",
4
4
  "description": "Lark/Feishu channel for the eve agent framework",
5
5
  "type": "module",
6
6
  "license": "MIT",