@eyeclaw/eyeclaw 2.4.3 → 2.4.4
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 +1 -1
- package/src/websocket-client.ts +46 -17
package/package.json
CHANGED
package/src/websocket-client.ts
CHANGED
|
@@ -123,12 +123,19 @@ export class EyeClawWebSocketClient {
|
|
|
123
123
|
this.stopPing()
|
|
124
124
|
this.lastCloseCode = closeCode
|
|
125
125
|
|
|
126
|
+
// 保存之前的连接状态用于检测部署
|
|
127
|
+
const previouslyConnected = this.wasConnected
|
|
128
|
+
const lastConnected = this.lastConnectedAt
|
|
129
|
+
|
|
130
|
+
// 标记为未连接状态,防止死循环
|
|
131
|
+
this.wasConnected = false
|
|
132
|
+
|
|
126
133
|
// 统计连续失败
|
|
127
134
|
this.consecutiveFailures++
|
|
128
135
|
|
|
129
136
|
// 检测是否是部署导致的断连(之前已成功连接过,且断开时间 > 5 秒)
|
|
130
137
|
// 或者关闭码为 1000/1001(正常关闭)但之前已连接
|
|
131
|
-
if (
|
|
138
|
+
if (previouslyConnected && Date.now() - lastConnected > 5000) {
|
|
132
139
|
// 正常关闭或部署重启
|
|
133
140
|
if (wasClean) {
|
|
134
141
|
this.deploymentDetected = true
|
|
@@ -138,7 +145,7 @@ export class EyeClawWebSocketClient {
|
|
|
138
145
|
this.deploymentDetected = true
|
|
139
146
|
this.api.logger.info('[EyeClaw] 🔄 Server restart detected (unclean close), will reconnect immediately')
|
|
140
147
|
}
|
|
141
|
-
} else if (!
|
|
148
|
+
} else if (!previouslyConnected && !wasClean) {
|
|
142
149
|
// 首次连接失败,增加重试延迟
|
|
143
150
|
this.api.logger.warn('[EyeClaw] Initial connection failed, increasing delay...')
|
|
144
151
|
}
|
|
@@ -182,8 +189,13 @@ export class EyeClawWebSocketClient {
|
|
|
182
189
|
* 检测网络状态并在需要时触发重连
|
|
183
190
|
*/
|
|
184
191
|
private async checkNetworkAndReconnectIfNeeded() {
|
|
185
|
-
//
|
|
186
|
-
if (this.
|
|
192
|
+
// 如果 WebSocket 已经连接,跳过(不需要重连)
|
|
193
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
194
|
+
return
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 如果正在重连中,跳过
|
|
198
|
+
if (this.reconnecting) {
|
|
187
199
|
return
|
|
188
200
|
}
|
|
189
201
|
|
|
@@ -205,7 +217,7 @@ export class EyeClawWebSocketClient {
|
|
|
205
217
|
|
|
206
218
|
if (!wasOnline && this.networkOnline) {
|
|
207
219
|
// 网络从离线变为在线,触发重连
|
|
208
|
-
this.api.logger.info('[EyeClaw] 🌐 Network restored, triggering
|
|
220
|
+
this.api.logger.info('[EyeClaw] 🌐 Network restored, triggering reconnect')
|
|
209
221
|
this.deploymentDetected = true // 使用快速重连模式
|
|
210
222
|
this.scheduleReconnect()
|
|
211
223
|
} else if (this.networkOnline && !this.wasConnected) {
|
|
@@ -785,14 +797,22 @@ export class EyeClawWebSocketClient {
|
|
|
785
797
|
}
|
|
786
798
|
|
|
787
799
|
// 🚀 部署恢复场景或网络恢复:立即重连(无延迟)
|
|
800
|
+
// 但需要限制次数,防止无限立即重连失败导致死循环
|
|
788
801
|
if (this.deploymentDetected) {
|
|
789
|
-
|
|
790
|
-
this.
|
|
791
|
-
this.
|
|
792
|
-
this.
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
802
|
+
// 如果连续 3 次立即重连都失败,说明可能是网络问题,不再使用立即重连
|
|
803
|
+
if (this.consecutiveFailures > 3) {
|
|
804
|
+
this.api.logger.warn('[EyeClaw] ⚠️ Too many immediate reconnects failed, switching to backoff mode')
|
|
805
|
+
this.deploymentDetected = false
|
|
806
|
+
// 继续使用正常重连逻辑
|
|
807
|
+
} else {
|
|
808
|
+
this.api.logger.info('[EyeClaw] ⚡ Deployment recovery or network restored: immediate reconnect')
|
|
809
|
+
this.reconnectTimer = setTimeout(() => {
|
|
810
|
+
this.reconnecting = false
|
|
811
|
+
this.start()
|
|
812
|
+
}, 100) // 100ms 延迟(给服务器一点启动时间)
|
|
813
|
+
this.deploymentDetected = false // 重置标志
|
|
814
|
+
return
|
|
815
|
+
}
|
|
796
816
|
}
|
|
797
817
|
|
|
798
818
|
// 检测是否是网络断开导致的失败(code 0)
|
|
@@ -824,9 +844,15 @@ export class EyeClawWebSocketClient {
|
|
|
824
844
|
* 健康检查后重连
|
|
825
845
|
*/
|
|
826
846
|
private async performHealthCheckAndReconnect() {
|
|
847
|
+
// 如果 WebSocket 已经连接,不需要重连
|
|
848
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
849
|
+
this.api.logger?.debug?.('[EyeClaw] ✅ WebSocket already connected, skipping reconnect')
|
|
850
|
+
return
|
|
851
|
+
}
|
|
852
|
+
|
|
827
853
|
try {
|
|
828
854
|
// 尝试 HTTP 健康检查
|
|
829
|
-
const serverUrl = this.config.serverUrl.replace(/^
|
|
855
|
+
const serverUrl = this.config.serverUrl.replace(/^ws/, 'http').replace(/^wss/, 'https')
|
|
830
856
|
const healthUrl = `${serverUrl}/api/v1/health`
|
|
831
857
|
|
|
832
858
|
const controller = new AbortController()
|
|
@@ -840,7 +866,7 @@ export class EyeClawWebSocketClient {
|
|
|
840
866
|
clearTimeout(timeoutId)
|
|
841
867
|
|
|
842
868
|
if (response.ok) {
|
|
843
|
-
this.api.logger.info('[EyeClaw] ✅ Health check passed,
|
|
869
|
+
this.api.logger.info('[EyeClaw] ✅ Health check passed, connecting...')
|
|
844
870
|
this.start()
|
|
845
871
|
} else {
|
|
846
872
|
this.api.logger.warn(`[EyeClaw] ⚠️ Health check failed (${response.status}), retrying soon...`)
|
|
@@ -851,9 +877,12 @@ export class EyeClawWebSocketClient {
|
|
|
851
877
|
}, 2000)
|
|
852
878
|
}
|
|
853
879
|
} catch (error) {
|
|
854
|
-
this.api.logger.warn(`[EyeClaw] ⚠️ Health check error: ${error},
|
|
855
|
-
//
|
|
856
|
-
this.
|
|
880
|
+
this.api.logger.warn(`[EyeClaw] ⚠️ Health check error: ${error}, retrying soon...`)
|
|
881
|
+
// 健康检查失败,稍后重试
|
|
882
|
+
this.reconnectTimer = setTimeout(() => {
|
|
883
|
+
this.reconnecting = false
|
|
884
|
+
this.scheduleReconnect()
|
|
885
|
+
}, 2000)
|
|
857
886
|
}
|
|
858
887
|
}
|
|
859
888
|
|