@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.
- package/dist/clawbot/client.js +50 -29
- package/package.json +1 -1
package/dist/clawbot/client.js
CHANGED
|
@@ -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
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
},
|
|
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');
|