echoclaw-relay-agent 0.9.3 → 0.9.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/dist/RelayAgent.d.ts +1 -0
- package/dist/RelayAgent.js +24 -0
- package/dist/RelayClient.d.ts +1 -0
- package/dist/RelayClient.js +27 -3
- package/dist/chat/ChatHandler.d.ts +4 -1
- package/dist/chat/ChatHandler.js +20 -3
- package/package.json +1 -1
package/dist/RelayAgent.d.ts
CHANGED
package/dist/RelayAgent.js
CHANGED
|
@@ -97,6 +97,12 @@ export class RelayAgent extends EventEmitter {
|
|
|
97
97
|
writable: true,
|
|
98
98
|
value: false
|
|
99
99
|
});
|
|
100
|
+
Object.defineProperty(this, "_rekeyRetryCount", {
|
|
101
|
+
enumerable: true,
|
|
102
|
+
configurable: true,
|
|
103
|
+
writable: true,
|
|
104
|
+
value: 0
|
|
105
|
+
});
|
|
100
106
|
Object.defineProperty(this, "_activePairing", {
|
|
101
107
|
enumerable: true,
|
|
102
108
|
configurable: true,
|
|
@@ -398,6 +404,7 @@ export class RelayAgent extends EventEmitter {
|
|
|
398
404
|
// If desktop is online, ECDH completes in <1s; 30s gives ample margin.
|
|
399
405
|
pairing.pairAsAgent(undefined, 30000)
|
|
400
406
|
.then(result => {
|
|
407
|
+
this._rekeyRetryCount = 0; // Reset on success
|
|
401
408
|
this.onPaired(result);
|
|
402
409
|
// V2: Reattach gateway callbacks after reconnect
|
|
403
410
|
if (this.gatewayManager) {
|
|
@@ -411,11 +418,28 @@ export class RelayAgent extends EventEmitter {
|
|
|
411
418
|
})
|
|
412
419
|
.catch(err => {
|
|
413
420
|
this._activePairing = null;
|
|
421
|
+
this._rekeyRetryCount++;
|
|
422
|
+
// Circuit breaker: after 5 consecutive failures, force full reconnect cycle
|
|
423
|
+
// (disconnect + restart resets the relay server connection from scratch)
|
|
424
|
+
if (this._rekeyRetryCount >= 5) {
|
|
425
|
+
this._rekeyRetryCount = 0; // Reset for next cycle
|
|
426
|
+
this.emit('error', new Error(`Re-key failed 5 times, forcing fresh reconnect`));
|
|
427
|
+
if (this.transport) {
|
|
428
|
+
this.transport.disconnect();
|
|
429
|
+
this.transport.restart();
|
|
430
|
+
}
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
414
433
|
// Re-key failed — keep old crypto intact, mark as connected
|
|
415
434
|
// (data handler still works with old keys if desktop didn't re-key either)
|
|
416
435
|
if (this._paired && this.sessionKey) {
|
|
417
436
|
this.setStatus('connected');
|
|
418
437
|
}
|
|
438
|
+
// If WS is still OPEN, force reconnect to trigger fresh on('open')
|
|
439
|
+
if (this.transport?.isConnected) {
|
|
440
|
+
this.transport.disconnect();
|
|
441
|
+
this.transport.restart();
|
|
442
|
+
}
|
|
419
443
|
this.emit('error', err);
|
|
420
444
|
});
|
|
421
445
|
}
|
package/dist/RelayClient.d.ts
CHANGED
package/dist/RelayClient.js
CHANGED
|
@@ -91,6 +91,12 @@ export class RelayClient extends EventEmitter {
|
|
|
91
91
|
writable: true,
|
|
92
92
|
value: false
|
|
93
93
|
});
|
|
94
|
+
Object.defineProperty(this, "_rekeyRetryCount", {
|
|
95
|
+
enumerable: true,
|
|
96
|
+
configurable: true,
|
|
97
|
+
writable: true,
|
|
98
|
+
value: 0
|
|
99
|
+
});
|
|
94
100
|
Object.defineProperty(this, "_activePairing", {
|
|
95
101
|
enumerable: true,
|
|
96
102
|
configurable: true,
|
|
@@ -371,12 +377,30 @@ export class RelayClient extends EventEmitter {
|
|
|
371
377
|
const pairing = new PairingProtocol(this.transport);
|
|
372
378
|
this._activePairing = pairing;
|
|
373
379
|
pairing.pairAsClient(this.pairingCode)
|
|
374
|
-
.then(result =>
|
|
380
|
+
.then(result => {
|
|
381
|
+
this._rekeyRetryCount = 0; // Reset on success
|
|
382
|
+
this.onPaired(result, 'resumed');
|
|
383
|
+
})
|
|
375
384
|
.catch(err => {
|
|
376
385
|
this._activePairing = null;
|
|
377
|
-
|
|
378
|
-
//
|
|
386
|
+
this._rekeyRetryCount++;
|
|
387
|
+
// Circuit breaker: stop after 5 consecutive re-key failures
|
|
388
|
+
if (this._rekeyRetryCount >= 5) {
|
|
389
|
+
this._needsRekey = false;
|
|
390
|
+
this.emit('error', new Error(`Re-key failed after ${this._rekeyRetryCount} retries, giving up`));
|
|
391
|
+
// Force transport to close and reconnect from scratch
|
|
392
|
+
this.transport?.disconnect();
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
// Re-key failed — set _needsRekey back to true for retry.
|
|
379
396
|
this._needsRekey = true;
|
|
397
|
+
// If WS is still OPEN (pairing failed at protocol level, not transport level),
|
|
398
|
+
// _needsRekey won't be consumed until next on('open').
|
|
399
|
+
// Force a reconnect to trigger a fresh on('open').
|
|
400
|
+
if (this.transport?.isConnected) {
|
|
401
|
+
this.transport.disconnect();
|
|
402
|
+
this.transport.restart();
|
|
403
|
+
}
|
|
380
404
|
this.emit('error', err);
|
|
381
405
|
});
|
|
382
406
|
}
|
|
@@ -149,7 +149,10 @@ export declare class ChatHandler {
|
|
|
149
149
|
private _extractText;
|
|
150
150
|
/** Send a message back to desktop via the persistent callback. Buffers if disconnected. */
|
|
151
151
|
private _send;
|
|
152
|
-
/**
|
|
152
|
+
/** Sequential send queue to prevent message reordering between flush and real-time messages. */
|
|
153
|
+
private _sendQueue;
|
|
154
|
+
private _enqueueSend;
|
|
155
|
+
/** Flush buffered messages to desktop after relay reconnects (sequential, preserves order). */
|
|
153
156
|
private _flushMessageBuffer;
|
|
154
157
|
/**
|
|
155
158
|
* Send a message via a captured sendBack reference.
|
package/dist/chat/ChatHandler.js
CHANGED
|
@@ -112,6 +112,13 @@ export class ChatHandler {
|
|
|
112
112
|
writable: true,
|
|
113
113
|
value: []
|
|
114
114
|
});
|
|
115
|
+
/** Sequential send queue to prevent message reordering between flush and real-time messages. */
|
|
116
|
+
Object.defineProperty(this, "_sendQueue", {
|
|
117
|
+
enumerable: true,
|
|
118
|
+
configurable: true,
|
|
119
|
+
writable: true,
|
|
120
|
+
value: Promise.resolve()
|
|
121
|
+
});
|
|
115
122
|
this.sessionKey = config?.sessionKey ?? 'main';
|
|
116
123
|
this.requestTimeoutMs = config?.requestTimeoutMs ?? 30000;
|
|
117
124
|
this.wsClient = new OpenClawWsClient({
|
|
@@ -569,7 +576,8 @@ export class ChatHandler {
|
|
|
569
576
|
/** Send a message back to desktop via the persistent callback. Buffers if disconnected. */
|
|
570
577
|
async _send(msg) {
|
|
571
578
|
if (this._sendBack) {
|
|
572
|
-
|
|
579
|
+
// Use send queue to guarantee ordering (flush + real-time share same queue)
|
|
580
|
+
await this._enqueueSend(msg);
|
|
573
581
|
}
|
|
574
582
|
else {
|
|
575
583
|
// Buffer important messages while relay is disconnected (OpenClaw may still be responding)
|
|
@@ -580,13 +588,22 @@ export class ChatHandler {
|
|
|
580
588
|
}
|
|
581
589
|
}
|
|
582
590
|
}
|
|
583
|
-
|
|
591
|
+
_enqueueSend(msg) {
|
|
592
|
+
const p = this._sendQueue.then(async () => {
|
|
593
|
+
if (this._sendBack) {
|
|
594
|
+
await this._sendBack(msg);
|
|
595
|
+
}
|
|
596
|
+
}).catch(() => { });
|
|
597
|
+
this._sendQueue = p;
|
|
598
|
+
return p;
|
|
599
|
+
}
|
|
600
|
+
/** Flush buffered messages to desktop after relay reconnects (sequential, preserves order). */
|
|
584
601
|
_flushMessageBuffer() {
|
|
585
602
|
if (!this._sendBack || this._messageBuffer.length === 0)
|
|
586
603
|
return;
|
|
587
604
|
const buf = this._messageBuffer.splice(0);
|
|
588
605
|
for (const msg of buf) {
|
|
589
|
-
this.
|
|
606
|
+
this._enqueueSend(msg);
|
|
590
607
|
}
|
|
591
608
|
}
|
|
592
609
|
/**
|
package/package.json
CHANGED