@wu529778790/open-im 1.11.1-beta.0 → 1.11.1-beta.1

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.
@@ -21,6 +21,7 @@ let channelState = 'disconnected';
21
21
  let messageHandler = null;
22
22
  let stateChangeHandler = null;
23
23
  let reconnectTimer = null;
24
+ let watchdogTimer = null;
24
25
  let reconnectAttempt = 0;
25
26
  let fatal = false;
26
27
  let stopped = false;
@@ -28,6 +29,13 @@ let apiUrl = 'https://ilinkai.weixin.qq.com';
28
29
  let apiToken = '';
29
30
  /** Opaque cursor for getupdates pagination (replaces numeric offset) */
30
31
  let getUpdatesBuf = '';
32
+ /** Timestamp of last successful poll response (for watchdog) */
33
+ let lastResponseAt = 0;
34
+ /** Per-request timeout for long-polling (ms) */
35
+ const POLL_REQUEST_TIMEOUT_MS = 3 * 60 * 1000; // 3 minutes
36
+ /** Watchdog interval: force reconnect if no response for this long (ms) */
37
+ const WATCHDOG_INTERVAL_MS = 60_000; // check every 60s
38
+ const WATCHDOG_STALE_MS = 5 * 60 * 1000; // 5 minutes without response = stale
31
39
  export function getChannelState() {
32
40
  return channelState;
33
41
  }
@@ -58,14 +66,21 @@ function startPolling() {
58
66
  return;
59
67
  pollController = new AbortController();
60
68
  const signal = pollController.signal;
69
+ // Start watchdog to detect stale connections (e.g. Mac sleep / network drop)
70
+ startWatchdog();
61
71
  (async () => {
62
72
  log.info('ClawBot long-polling started');
63
73
  while (!stopped && !signal.aborted) {
64
74
  try {
75
+ // Combine the poll controller signal with a per-request timeout
76
+ const timeoutSignal = AbortSignal.timeout(POLL_REQUEST_TIMEOUT_MS);
77
+ const combinedSignal = AbortSignal.any([signal, timeoutSignal]);
65
78
  const res = await postApi('/ilink/bot/getupdates', {
66
79
  get_updates_buf: getUpdatesBuf,
67
80
  base_info: BASE_INFO,
68
- }, signal);
81
+ }, combinedSignal);
82
+ // Record successful response time for watchdog
83
+ lastResponseAt = Date.now();
69
84
  if (signal.aborted)
70
85
  break;
71
86
  if (!res.ok) {
@@ -168,6 +183,35 @@ function updateState(state) {
168
183
  stateChangeHandler?.(state);
169
184
  log.debug(`ClawBot state: ${state}`);
170
185
  }
186
+ /**
187
+ * Watchdog: periodically check if the poll loop is alive.
188
+ * After Mac sleep or network drop, the fetch may hang without throwing.
189
+ * If no successful response for WATCHDOG_STALE_MS, force a reconnect.
190
+ */
191
+ function startWatchdog() {
192
+ stopWatchdog();
193
+ lastResponseAt = Date.now();
194
+ watchdogTimer = setInterval(() => {
195
+ if (stopped)
196
+ return;
197
+ const elapsed = Date.now() - lastResponseAt;
198
+ if (elapsed > WATCHDOG_STALE_MS) {
199
+ log.warn(`ClawBot watchdog: no response for ${Math.round(elapsed / 1000)}s, forcing reconnect`);
200
+ if (pollController) {
201
+ pollController.abort();
202
+ pollController = null;
203
+ }
204
+ updateState('error');
205
+ scheduleReconnect();
206
+ }
207
+ }, WATCHDOG_INTERVAL_MS);
208
+ }
209
+ function stopWatchdog() {
210
+ if (watchdogTimer) {
211
+ clearInterval(watchdogTimer);
212
+ watchdogTimer = null;
213
+ }
214
+ }
171
215
  /**
172
216
  * Extract text content from an iLink message's item_list.
173
217
  * Returns the first text item found, or a placeholder for media types.
@@ -272,6 +316,7 @@ export function stopClawbot() {
272
316
  clearTimeout(reconnectTimer);
273
317
  reconnectTimer = null;
274
318
  }
319
+ stopWatchdog();
275
320
  messageHandler = null;
276
321
  // Don't clear context_token here — it's persisted for startup notifications
277
322
  updateState('disconnected');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wu529778790/open-im",
3
- "version": "1.11.1-beta.0",
3
+ "version": "1.11.1-beta.1",
4
4
  "description": "Multi-platform IM bridge for AI CLI tools (Claude, Codex, CodeBuddy)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",