@eyeclaw/eyeclaw 2.4.3 → 2.4.5
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 +49 -18
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,14 +189,20 @@ 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
|
|
|
190
202
|
try {
|
|
191
203
|
// 尝试一个简单的 HTTP 请求来检测网络是否可用
|
|
192
|
-
|
|
204
|
+
// 直接使用配置的 serverUrl,不需要转换
|
|
205
|
+
const serverUrl = this.config.serverUrl
|
|
193
206
|
const controller = new AbortController()
|
|
194
207
|
const timeoutId = setTimeout(() => controller.abort(), 3000)
|
|
195
208
|
|
|
@@ -205,7 +218,7 @@ export class EyeClawWebSocketClient {
|
|
|
205
218
|
|
|
206
219
|
if (!wasOnline && this.networkOnline) {
|
|
207
220
|
// 网络从离线变为在线,触发重连
|
|
208
|
-
this.api.logger.info('[EyeClaw] 🌐 Network restored, triggering
|
|
221
|
+
this.api.logger.info('[EyeClaw] 🌐 Network restored, triggering reconnect')
|
|
209
222
|
this.deploymentDetected = true // 使用快速重连模式
|
|
210
223
|
this.scheduleReconnect()
|
|
211
224
|
} else if (this.networkOnline && !this.wasConnected) {
|
|
@@ -785,14 +798,22 @@ export class EyeClawWebSocketClient {
|
|
|
785
798
|
}
|
|
786
799
|
|
|
787
800
|
// 🚀 部署恢复场景或网络恢复:立即重连(无延迟)
|
|
801
|
+
// 但需要限制次数,防止无限立即重连失败导致死循环
|
|
788
802
|
if (this.deploymentDetected) {
|
|
789
|
-
|
|
790
|
-
this.
|
|
791
|
-
this.
|
|
792
|
-
this.
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
803
|
+
// 如果连续 3 次立即重连都失败,说明可能是网络问题,不再使用立即重连
|
|
804
|
+
if (this.consecutiveFailures > 3) {
|
|
805
|
+
this.api.logger.warn('[EyeClaw] ⚠️ Too many immediate reconnects failed, switching to backoff mode')
|
|
806
|
+
this.deploymentDetected = false
|
|
807
|
+
// 继续使用正常重连逻辑
|
|
808
|
+
} else {
|
|
809
|
+
this.api.logger.info('[EyeClaw] ⚡ Deployment recovery or network restored: immediate reconnect')
|
|
810
|
+
this.reconnectTimer = setTimeout(() => {
|
|
811
|
+
this.reconnecting = false
|
|
812
|
+
this.start()
|
|
813
|
+
}, 100) // 100ms 延迟(给服务器一点启动时间)
|
|
814
|
+
this.deploymentDetected = false // 重置标志
|
|
815
|
+
return
|
|
816
|
+
}
|
|
796
817
|
}
|
|
797
818
|
|
|
798
819
|
// 检测是否是网络断开导致的失败(code 0)
|
|
@@ -824,9 +845,16 @@ export class EyeClawWebSocketClient {
|
|
|
824
845
|
* 健康检查后重连
|
|
825
846
|
*/
|
|
826
847
|
private async performHealthCheckAndReconnect() {
|
|
848
|
+
// 如果 WebSocket 已经连接,不需要重连
|
|
849
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
850
|
+
this.api.logger?.debug?.('[EyeClaw] ✅ WebSocket already connected, skipping reconnect')
|
|
851
|
+
return
|
|
852
|
+
}
|
|
853
|
+
|
|
827
854
|
try {
|
|
828
855
|
// 尝试 HTTP 健康检查
|
|
829
|
-
|
|
856
|
+
// 直接使用配置的 serverUrl,不需要转换
|
|
857
|
+
const serverUrl = this.config.serverUrl
|
|
830
858
|
const healthUrl = `${serverUrl}/api/v1/health`
|
|
831
859
|
|
|
832
860
|
const controller = new AbortController()
|
|
@@ -840,7 +868,7 @@ export class EyeClawWebSocketClient {
|
|
|
840
868
|
clearTimeout(timeoutId)
|
|
841
869
|
|
|
842
870
|
if (response.ok) {
|
|
843
|
-
this.api.logger.info('[EyeClaw] ✅ Health check passed,
|
|
871
|
+
this.api.logger.info('[EyeClaw] ✅ Health check passed, connecting...')
|
|
844
872
|
this.start()
|
|
845
873
|
} else {
|
|
846
874
|
this.api.logger.warn(`[EyeClaw] ⚠️ Health check failed (${response.status}), retrying soon...`)
|
|
@@ -851,9 +879,12 @@ export class EyeClawWebSocketClient {
|
|
|
851
879
|
}, 2000)
|
|
852
880
|
}
|
|
853
881
|
} catch (error) {
|
|
854
|
-
this.api.logger.warn(`[EyeClaw] ⚠️ Health check error: ${error},
|
|
855
|
-
//
|
|
856
|
-
this.
|
|
882
|
+
this.api.logger.warn(`[EyeClaw] ⚠️ Health check error: ${error}, retrying soon...`)
|
|
883
|
+
// 健康检查失败,稍后重试
|
|
884
|
+
this.reconnectTimer = setTimeout(() => {
|
|
885
|
+
this.reconnecting = false
|
|
886
|
+
this.scheduleReconnect()
|
|
887
|
+
}, 2000)
|
|
857
888
|
}
|
|
858
889
|
}
|
|
859
890
|
|