codex-to-im 1.0.48 → 1.0.50

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/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
  */
@@ -10178,6 +10146,31 @@ var IGNORED_EVENT_MSG_TYPES = /* @__PURE__ */ new Set([
10178
10146
  var IGNORED_RESPONSE_ITEM_TYPES = /* @__PURE__ */ new Set([
10179
10147
  "web_search_call"
10180
10148
  ]);
10149
+ var TERMINAL_COMPLETION_EVENT_TYPES = /* @__PURE__ */ new Set([
10150
+ "task_complete",
10151
+ "turn.completed",
10152
+ "turn_completed"
10153
+ ]);
10154
+ function isTerminalCompletionEventType(value) {
10155
+ return typeof value === "string" && TERMINAL_COMPLETION_EVENT_TYPES.has(value.trim());
10156
+ }
10157
+ function getEventTurnId(payload) {
10158
+ return payload?.turn_id || payload?.turnId || "";
10159
+ }
10160
+ function extractTerminalCompletionText(payload) {
10161
+ if (!payload) return "";
10162
+ for (const value of [
10163
+ payload.last_agent_message,
10164
+ payload.message,
10165
+ payload.text,
10166
+ payload.final_response,
10167
+ payload.response
10168
+ ]) {
10169
+ const text2 = extractNormalizedStructuredText(value);
10170
+ if (text2) return text2;
10171
+ }
10172
+ return "";
10173
+ }
10181
10174
  function isIgnoredMirrorLineKind(line) {
10182
10175
  if (isSessionEventLine(line)) {
10183
10176
  const payloadType = typeof line.payload?.type === "string" ? line.payload.type.trim() : "";
@@ -10281,8 +10274,8 @@ function pushDesktopSessionEvent(events, parsed, rawLine) {
10281
10274
  });
10282
10275
  return;
10283
10276
  }
10284
- if (isSessionEventLine(parsed) && parsed.payload?.type === "task_complete") {
10285
- const text2 = extractNormalizedStructuredText(parsed.payload.last_agent_message);
10277
+ if (isSessionEventLine(parsed) && isTerminalCompletionEventType(parsed.payload?.type)) {
10278
+ const text2 = extractTerminalCompletionText(parsed.payload);
10286
10279
  if (!text2) return;
10287
10280
  const lastEvent = events[events.length - 1];
10288
10281
  if (lastEvent?.role === "assistant" && lastEvent.content === text2) {
@@ -10329,7 +10322,7 @@ function pushDesktopMirrorEventRecord(records, parsed, rawLine, activeTurnId) {
10329
10322
  type: "task_started",
10330
10323
  content: "",
10331
10324
  timestamp,
10332
- turnId: parsed.payload.turn_id || ""
10325
+ turnId: getEventTurnId(parsed.payload)
10333
10326
  });
10334
10327
  return true;
10335
10328
  }
@@ -10476,14 +10469,14 @@ function pushDesktopMirrorEventRecord(records, parsed, rawLine, activeTurnId) {
10476
10469
  });
10477
10470
  return true;
10478
10471
  }
10479
- if (parsed.payload?.type === "task_complete") {
10472
+ if (isTerminalCompletionEventType(parsed.payload?.type)) {
10480
10473
  records.push({
10481
10474
  signature,
10482
10475
  type: "task_complete",
10483
10476
  role: "assistant",
10484
- content: extractNormalizedStructuredText(parsed.payload.last_agent_message),
10477
+ content: extractTerminalCompletionText(parsed.payload),
10485
10478
  timestamp,
10486
- turnId: parsed.payload.turn_id || ""
10479
+ turnId: getEventTurnId(parsed.payload)
10487
10480
  });
10488
10481
  return true;
10489
10482
  }
@@ -10712,16 +10705,16 @@ function parseDesktopMirrorRecordText(content, leadingText = "", flushTrailingTe
10712
10705
  }
10713
10706
  if (isSessionEventLine(parsed) && parsed.payload?.type === "task_started") {
10714
10707
  const eventPayload = parsed.payload;
10715
- activeTurnId = eventPayload?.turn_id || activeTurnId;
10708
+ activeTurnId = getEventTurnId(eventPayload) || activeTurnId;
10716
10709
  }
10717
10710
  const handled = pushDesktopMirrorRecord(records, parsed, trimmed, activeTurnId, activeSpecialCallIds);
10718
10711
  if (!handled) {
10719
10712
  const unknownKind = describeUnhandledMirrorLineKind(parsed);
10720
10713
  if (unknownKind) unknownKinds.add(unknownKind);
10721
10714
  }
10722
- if (isSessionEventLine(parsed) && (parsed.payload?.type === "task_complete" || parsed.payload?.type === "turn_aborted")) {
10715
+ if (isSessionEventLine(parsed) && (isTerminalCompletionEventType(parsed.payload?.type) || parsed.payload?.type === "turn_aborted")) {
10723
10716
  const eventPayload = parsed.payload;
10724
- const completedTurnId = eventPayload?.turn_id || activeTurnId;
10717
+ const completedTurnId = getEventTurnId(eventPayload) || activeTurnId;
10725
10718
  if (!completedTurnId || completedTurnId === activeTurnId) {
10726
10719
  activeTurnId = null;
10727
10720
  }
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.50",
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",
@@ -40,11 +40,16 @@
40
40
  "prepublishOnly": "npm run typecheck && npm run build"
41
41
  },
42
42
  "dependencies": {
43
- "@larksuiteoapi/node-sdk": "^1.59.0",
44
- "@openai/codex-sdk": "^0.130.0",
43
+ "@larksuiteoapi/node-sdk": "^1.65.0",
44
+ "@openai/codex-sdk": "^0.133.0",
45
45
  "markdown-it": "^14.1.1",
46
46
  "qrcode": "^1.5.4",
47
- "ws": "^8.18.0"
47
+ "ws": "^8.20.1"
48
+ },
49
+ "overrides": {
50
+ "axios": "1.16.1",
51
+ "follow-redirects": "1.16.0",
52
+ "protobufjs": "7.5.9"
48
53
  },
49
54
  "devDependencies": {
50
55
  "@types/markdown-it": "^14.1.2",
@@ -15,7 +15,7 @@ import path from 'node:path';
15
15
  * - If upstream adds `windowsHide` natively, remove this script.
16
16
  */
17
17
  const PATCH_MARKER = 'windowsHide: process.platform === "win32"';
18
- const SUPPORTED_SDK_VERSION = /^0\.(11\d|12\d)\.\d+$/;
18
+ const SUPPORTED_SDK_VERSION = /^0\.(11\d|12\d|13[0-3])\.\d+$/;
19
19
 
20
20
  function logSkip(message) {
21
21
  console.warn(`[postinstall] ${message}`);