@coclaw/openclaw-coclaw 0.2.1 → 0.2.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coclaw/openclaw-coclaw",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "description": "OpenClaw CoClaw channel plugin for remote chat",
@@ -53,7 +53,7 @@
53
53
  "test:plugin": "node --test src/plugin-mode.test.js",
54
54
  "test": "node --test",
55
55
  "coverage": "c8 --check-coverage --lines 100 --functions 100 --branches 95 --statements 100 --reporter=text --reporter=lcov node --test",
56
- "verify": "pnpm check && pnpm test:standalone && pnpm test:plugin && pnpm test && pnpm coverage",
56
+ "verify": "pnpm check && pnpm test:plugin && pnpm test && pnpm coverage",
57
57
  "link": "bash ./scripts/link.sh",
58
58
  "unlink": "bash ./scripts/unlink.sh",
59
59
  "install:npm": "bash ./scripts/install-npm.sh",
@@ -10,6 +10,7 @@ const RECONNECT_MS = 10_000;
10
10
  const CONNECT_TIMEOUT_MS = 10_000;
11
11
  const SERVER_HB_PING_MS = 25_000;
12
12
  const SERVER_HB_TIMEOUT_MS = 45_000;
13
+ const SERVER_HB_MAX_MISS = 4; // 连续 4 次无响应才断连(~3 分钟)
13
14
 
14
15
  function toServerWsUrl(baseUrl, token) {
15
16
  const url = new URL(baseUrl);
@@ -88,6 +89,7 @@ export class RealtimeBridge {
88
89
  this.intentionallyClosed = false;
89
90
  this.serverHbInterval = null;
90
91
  this.serverHbTimer = null;
92
+ this.__serverHbMissCount = 0;
91
93
  }
92
94
 
93
95
  __resolveWebSocket() {
@@ -102,6 +104,7 @@ export class RealtimeBridge {
102
104
 
103
105
  __startServerHeartbeat(sock) {
104
106
  this.__clearServerHeartbeat();
107
+ this.__serverHbMissCount = 0;
105
108
  this.serverHbInterval = setInterval(() => {
106
109
  if (sock.readyState === 1) {
107
110
  try { sock.send(JSON.stringify({ type: 'ping' })); } catch {}
@@ -112,14 +115,36 @@ export class RealtimeBridge {
112
115
  }
113
116
 
114
117
  __resetServerHbTimeout(sock) {
118
+ this.__serverHbMissCount = 0;
115
119
  if (this.serverHbTimer) clearTimeout(this.serverHbTimer);
116
120
  this.serverHbTimer = setTimeout(() => {
117
- this.logger.warn?.(`[coclaw] server ws heartbeat timeout (${SERVER_HB_TIMEOUT_MS / 1000}s), closing`);
118
- try { sock.close(4000, 'heartbeat_timeout'); } catch {}
121
+ this.__onServerHbMiss(sock);
119
122
  }, SERVER_HB_TIMEOUT_MS);
120
123
  this.serverHbTimer.unref?.();
121
124
  }
122
125
 
126
+ __onServerHbMiss(sock) {
127
+ this.__serverHbMissCount++;
128
+ if (this.__serverHbMissCount < SERVER_HB_MAX_MISS) {
129
+ this.__logDebug(
130
+ `server heartbeat miss ${this.__serverHbMissCount}/${SERVER_HB_MAX_MISS}, will retry`
131
+ );
132
+ // 补发 ping,继续等下一轮
133
+ if (sock.readyState === 1) {
134
+ try { sock.send(JSON.stringify({ type: 'ping' })); } catch {}
135
+ }
136
+ this.serverHbTimer = setTimeout(() => {
137
+ this.__onServerHbMiss(sock);
138
+ }, SERVER_HB_TIMEOUT_MS);
139
+ this.serverHbTimer.unref?.();
140
+ return;
141
+ }
142
+ this.logger.warn?.(
143
+ `[coclaw] server ws heartbeat timeout after ${this.__serverHbMissCount} consecutive misses (~${this.__serverHbMissCount * SERVER_HB_TIMEOUT_MS / 1000}s), closing`
144
+ );
145
+ try { sock.close(4000, 'heartbeat_timeout'); } catch {}
146
+ }
147
+
123
148
  __clearServerHeartbeat() {
124
149
  if (this.serverHbInterval) { clearInterval(this.serverHbInterval); this.serverHbInterval = null; }
125
150
  if (this.serverHbTimer) { clearTimeout(this.serverHbTimer); this.serverHbTimer = null; }