codex-to-im 1.0.48 → 1.0.49

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.
Files changed (2) hide show
  1. package/dist/daemon.mjs +25 -57
  2. package/package.json +1 -1
package/dist/daemon.mjs CHANGED
@@ -1415,7 +1415,8 @@ function buildPermissionButtonCard(text2, permissionRequestId, chatId) {
1415
1415
  // src/lib/bridge/adapters/feishu-adapter.ts
1416
1416
  var DEDUP_MAX = 1e3;
1417
1417
  var MAX_FILE_SIZE = 20 * 1024 * 1024;
1418
- var TYPING_EMOJI = "Typing";
1418
+ var COMPLETED_EMOJI = "DONE";
1419
+ var ERROR_EMOJI = "ERROR";
1419
1420
  var CARD_THROTTLE_MS = 1e3;
1420
1421
  var CARD_REQUEST_TIMEOUT_MS = 15e3;
1421
1422
  var CARD_FINALIZE_FLUSH_WAIT_EXTRA_MS = 1e3;
@@ -1484,14 +1485,8 @@ var FeishuAdapter = class extends BaseChannelAdapter {
1484
1485
  botOpenId = null;
1485
1486
  /** All known bot IDs (open_id, user_id, union_id) for mention matching. */
1486
1487
  botIds = /* @__PURE__ */ new Set();
1487
- /** Track last incoming message ID per chat for typing indicator. */
1488
+ /** Track last incoming message ID per chat for replying with streaming cards. */
1488
1489
  lastIncomingMessageId = /* @__PURE__ */ new Map();
1489
- /** Track active typing reaction IDs per stream key for cleanup. */
1490
- typingReactions = /* @__PURE__ */ new Map();
1491
- /** Track in-flight typing reaction creates so repeated status updates stay idempotent. */
1492
- typingReactionCreatePromises = /* @__PURE__ */ new Map();
1493
- /** Track streams that ended before the async reaction create finished. */
1494
- typingReactionCleanupRequested = /* @__PURE__ */ new Set();
1495
1490
  /** Active streaming card state per stream key. */
1496
1491
  activeCards = /* @__PURE__ */ new Map();
1497
1492
  /** In-flight card creation promises per stream key — prevents duplicate creation. */
@@ -1598,9 +1593,6 @@ var FeishuAdapter = class extends BaseChannelAdapter {
1598
1593
  this.cardCreatePromises.clear();
1599
1594
  this.seenMessageIds.clear();
1600
1595
  this.lastIncomingMessageId.clear();
1601
- this.typingReactions.clear();
1602
- this.typingReactionCreatePromises.clear();
1603
- this.typingReactionCleanupRequested.clear();
1604
1596
  console.log("[feishu-adapter] Stopped");
1605
1597
  }
1606
1598
  isRunning() {
@@ -1610,67 +1602,24 @@ var FeishuAdapter = class extends BaseChannelAdapter {
1610
1602
  consumeOne() {
1611
1603
  return this.consumeInboundMessage(this.running);
1612
1604
  }
1613
- // ── Typing indicator (Openclaw-style reaction) ─────────────
1605
+ // ── Streaming lifecycle hooks ──────────────────────────────
1614
1606
  /**
1615
- * Add a "Typing" emoji reaction to the user's message and create streaming card.
1607
+ * Create the streaming card as early as possible.
1616
1608
  * Called by bridge-manager via onMessageStart().
1617
1609
  */
1618
1610
  onMessageStart(chatId, streamKey) {
1619
1611
  const messageId = this.lastIncomingMessageId.get(chatId);
1620
- const reactionKey = this.resolveStreamKey(chatId, streamKey);
1621
1612
  if (messageId && this.isStreamingEnabled()) {
1622
1613
  this.createStreamingCard(chatId, messageId, streamKey).catch(() => {
1623
1614
  });
1624
1615
  }
1625
- if (!messageId || !this.restClient) return;
1626
- if (this.typingReactions.has(reactionKey) || this.typingReactionCreatePromises.has(reactionKey)) {
1627
- return;
1628
- }
1629
- const createPromise = this.restClient.im.messageReaction.create({
1630
- path: { message_id: messageId },
1631
- data: { reaction_type: { emoji_type: TYPING_EMOJI } }
1632
- }).then((res) => {
1633
- const reactionId = res?.data?.reaction_id;
1634
- if (reactionId) {
1635
- this.typingReactions.set(reactionKey, { messageId, reactionId });
1636
- if (this.typingReactionCleanupRequested.delete(reactionKey)) {
1637
- this.removeTypingReaction(reactionKey);
1638
- }
1639
- }
1640
- }).catch((err) => {
1641
- const code2 = err?.code;
1642
- if (code2 !== 99991400 && code2 !== 99991403) {
1643
- console.warn("[feishu-adapter] Typing indicator failed:", err instanceof Error ? err.message : err);
1644
- }
1645
- }).finally(() => {
1646
- this.typingReactionCreatePromises.delete(reactionKey);
1647
- if (!this.typingReactions.has(reactionKey)) {
1648
- this.typingReactionCleanupRequested.delete(reactionKey);
1649
- }
1650
- });
1651
- this.typingReactionCreatePromises.set(reactionKey, createPromise);
1652
1616
  }
1653
1617
  /**
1654
- * Remove the "Typing" emoji reaction and clean up card state.
1618
+ * Clean up card state.
1655
1619
  * Called by bridge-manager via onMessageEnd().
1656
1620
  */
1657
1621
  onMessageEnd(chatId, streamKey) {
1658
1622
  this.cleanupCard(chatId, streamKey);
1659
- const reactionKey = this.resolveStreamKey(chatId, streamKey);
1660
- if (this.typingReactionCreatePromises.has(reactionKey) && !this.typingReactions.has(reactionKey)) {
1661
- this.typingReactionCleanupRequested.add(reactionKey);
1662
- return;
1663
- }
1664
- this.removeTypingReaction(reactionKey);
1665
- }
1666
- removeTypingReaction(reactionKey) {
1667
- const reaction = this.typingReactions.get(reactionKey);
1668
- if (!reaction || !this.restClient) return;
1669
- this.typingReactions.delete(reactionKey);
1670
- this.restClient.im.messageReaction.delete({
1671
- path: { message_id: reaction.messageId, reaction_id: reaction.reactionId }
1672
- }).catch(() => {
1673
- });
1674
1623
  }
1675
1624
  // ── Card Action Handler ─────────────────────────────────────
1676
1625
  /**
@@ -2055,6 +2004,10 @@ ${trimmedResponse}`;
2055
2004
  sequence: state.sequence
2056
2005
  }
2057
2006
  }));
2007
+ const terminalReactionEmoji = status === "completed" ? COMPLETED_EMOJI : status === "error" ? ERROR_EMOJI : null;
2008
+ if (terminalReactionEmoji) {
2009
+ await this.addTerminalReaction(cardKey, state.messageId, terminalReactionEmoji);
2010
+ }
2058
2011
  console.log(`[feishu-adapter] Card finalized: streamKey=${cardKey}, cardId=${state.cardId}, status=${status}, elapsed=${formatElapsed(elapsedMs)}`);
2059
2012
  return true;
2060
2013
  } catch (err) {
@@ -2064,6 +2017,21 @@ ${trimmedResponse}`;
2064
2017
  this.activeCards.delete(cardKey);
2065
2018
  }
2066
2019
  }
2020
+ async addTerminalReaction(streamKey, messageId, emojiType) {
2021
+ const messageReaction = this.restClient?.im?.messageReaction;
2022
+ if (typeof messageReaction?.create !== "function") return;
2023
+ try {
2024
+ await this.withFeishuRequestTimeout(streamKey, `im.messageReaction.create:${emojiType}`, () => messageReaction.create({
2025
+ path: { message_id: messageId },
2026
+ data: { reaction_type: { emoji_type: emojiType } }
2027
+ }));
2028
+ } catch (err) {
2029
+ const code2 = err?.code;
2030
+ if (code2 !== 99991400 && code2 !== 99991403) {
2031
+ console.warn("[feishu-adapter] Terminal reaction failed:", err instanceof Error ? err.message : err);
2032
+ }
2033
+ }
2034
+ }
2067
2035
  /**
2068
2036
  * Clean up card state without finalizing (e.g. on unexpected errors).
2069
2037
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-to-im",
3
- "version": "1.0.48",
3
+ "version": "1.0.49",
4
4
  "description": "Installable Codex-to-IM bridge with local setup UI and background service",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/zhangle1987/codex-to-im#readme",