@wu529778790/open-im 1.11.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
  }
@@ -47,34 +55,10 @@ export async function initClawbot(config, eventHandler, onStateChange) {
47
55
  reconnectAttempt = 0;
48
56
  fatal = false;
49
57
  getUpdatesBuf = '';
50
- // Verify connectivitynon-fatal, reconnect loop will retry
51
- try {
52
- const res = await postApi('/ilink/bot/getupdates', {
53
- get_updates_buf: '',
54
- base_info: BASE_INFO,
55
- });
56
- if (!res.ok) {
57
- throw new Error(`API check failed: ${res.error ?? 'unknown'}`);
58
- }
59
- log.info(`ClawBot API reachable at ${apiUrl}`);
60
- fatal = false;
61
- reconnectAttempt = 0;
62
- if (res.updatesBuf)
63
- getUpdatesBuf = res.updatesBuf;
64
- updateState('connected');
65
- startPolling();
66
- }
67
- catch (err) {
68
- if (isFatalReconnectError(err)) {
69
- fatal = true;
70
- log.warn('ClawBot API auth/session error, will slow-probe:', err);
71
- }
72
- else {
73
- log.warn('ClawBot API not reachable, will retry:', err);
74
- }
75
- updateState('connecting');
76
- scheduleReconnect();
77
- }
58
+ // Start polling directly no blocking connectivity check.
59
+ // The polling loop handles errors and reconnection internally.
60
+ updateState('connected');
61
+ startPolling();
78
62
  log.info('ClawBot client initialized');
79
63
  }
80
64
  function startPolling() {
@@ -82,14 +66,21 @@ function startPolling() {
82
66
  return;
83
67
  pollController = new AbortController();
84
68
  const signal = pollController.signal;
69
+ // Start watchdog to detect stale connections (e.g. Mac sleep / network drop)
70
+ startWatchdog();
85
71
  (async () => {
86
72
  log.info('ClawBot long-polling started');
87
73
  while (!stopped && !signal.aborted) {
88
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]);
89
78
  const res = await postApi('/ilink/bot/getupdates', {
90
79
  get_updates_buf: getUpdatesBuf,
91
80
  base_info: BASE_INFO,
92
- }, signal);
81
+ }, combinedSignal);
82
+ // Record successful response time for watchdog
83
+ lastResponseAt = Date.now();
93
84
  if (signal.aborted)
94
85
  break;
95
86
  if (!res.ok) {
@@ -192,6 +183,35 @@ function updateState(state) {
192
183
  stateChangeHandler?.(state);
193
184
  log.debug(`ClawBot state: ${state}`);
194
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
+ }
195
215
  /**
196
216
  * Extract text content from an iLink message's item_list.
197
217
  * Returns the first text item found, or a placeholder for media types.
@@ -296,6 +316,7 @@ export function stopClawbot() {
296
316
  clearTimeout(reconnectTimer);
297
317
  reconnectTimer = null;
298
318
  }
319
+ stopWatchdog();
299
320
  messageHandler = null;
300
321
  // Don't clear context_token here — it's persisted for startup notifications
301
322
  updateState('disconnected');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wu529778790/open-im",
3
- "version": "1.11.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",