viveworker 0.8.7 → 0.8.8
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/web/remote-pairing/transport.js +59 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "viveworker",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.8",
|
|
4
4
|
"description": "Mobile control plane for AI agents. Approve, answer, and hand off work for Codex, Claude, MCP-ready tools, and A2A from one phone.",
|
|
5
5
|
"author": "Yuta Hoshino <hoshino.lireneo@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -48,7 +48,6 @@ import {
|
|
|
48
48
|
decode,
|
|
49
49
|
encodeData,
|
|
50
50
|
encodeAck,
|
|
51
|
-
encodePing,
|
|
52
51
|
encodeResumeReq,
|
|
53
52
|
generateMid,
|
|
54
53
|
FRAME_DATA,
|
|
@@ -79,7 +78,11 @@ export const STATE = Object.freeze({
|
|
|
79
78
|
FAILED: "failed",
|
|
80
79
|
});
|
|
81
80
|
|
|
82
|
-
const
|
|
81
|
+
export const TEXT_KEEPALIVE_PING = "viveworker.keepalive.ping.v1";
|
|
82
|
+
export const TEXT_KEEPALIVE_PONG = "viveworker.keepalive.pong.v1";
|
|
83
|
+
|
|
84
|
+
const DEFAULT_PING_INTERVAL_MS = 75_000; // CF idle timeout is ~100s
|
|
85
|
+
const MIN_PING_INTERVAL_MS = 5_000;
|
|
83
86
|
const DEFAULT_BACKOFF_MS = [1_000, 2_000, 4_000, 8_000, 16_000, 30_000];
|
|
84
87
|
const DEFAULT_HANDSHAKE_TIMEOUT_MS = 30_000;
|
|
85
88
|
const MAX_PRE_CONNECT_BACKOFF_MS = 4_000;
|
|
@@ -169,6 +172,7 @@ export class RemotePairingTransport {
|
|
|
169
172
|
this._onHandshakeComplete = opts.onHandshakeComplete ?? noop;
|
|
170
173
|
|
|
171
174
|
this._pingIntervalMs = opts.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS;
|
|
175
|
+
if (Number(this._pingIntervalMs) <= 0) this._pingIntervalMs = 0;
|
|
172
176
|
this._backoffMs = (opts.backoffMs ?? DEFAULT_BACKOFF_MS).slice();
|
|
173
177
|
if (this._backoffMs.length === 0) this._backoffMs = [1_000];
|
|
174
178
|
this._handshakeTimeoutMs = opts.handshakeTimeoutMs ?? DEFAULT_HANDSHAKE_TIMEOUT_MS;
|
|
@@ -207,7 +211,7 @@ export class RemotePairingTransport {
|
|
|
207
211
|
this._reconnectAttempt = 0;
|
|
208
212
|
/** @type {ReturnType<typeof setTimeout> | null} */
|
|
209
213
|
this._reconnectTimer = null;
|
|
210
|
-
/** @type {ReturnType<typeof
|
|
214
|
+
/** @type {ReturnType<typeof setTimeout> | null} */
|
|
211
215
|
this._pingTimer = null;
|
|
212
216
|
/** @type {ReturnType<typeof setTimeout> | null} */
|
|
213
217
|
this._handshakeTimer = null;
|
|
@@ -229,6 +233,7 @@ export class RemotePairingTransport {
|
|
|
229
233
|
// ---- sequencing (monotonic across the transport lifetime) ----
|
|
230
234
|
this._outboundSeq = 0;
|
|
231
235
|
this._lastSeenPeerSeq = 0;
|
|
236
|
+
this._lastActivityAtMs = 0;
|
|
232
237
|
}
|
|
233
238
|
|
|
234
239
|
// -------------------------------------------------------------------------
|
|
@@ -395,6 +400,7 @@ export class RemotePairingTransport {
|
|
|
395
400
|
try { this._ws?.close(CLOSE_NORMAL, "closed during open"); } catch {}
|
|
396
401
|
return;
|
|
397
402
|
}
|
|
403
|
+
this._markActivity({ reschedulePing: false });
|
|
398
404
|
if (this._session) {
|
|
399
405
|
// We have a live noise session from a previous connection — try to
|
|
400
406
|
// resume rather than re-handshaking. `lastSeenPeerSeq` tells the relay
|
|
@@ -421,6 +427,21 @@ export class RemotePairingTransport {
|
|
|
421
427
|
}
|
|
422
428
|
|
|
423
429
|
_handleMessage(evt) {
|
|
430
|
+
if (typeof evt?.data === "string") {
|
|
431
|
+
if (evt.data === TEXT_KEEPALIVE_PONG) {
|
|
432
|
+
this._markActivity();
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
if (evt.data === TEXT_KEEPALIVE_PING) {
|
|
436
|
+
this._markActivity();
|
|
437
|
+
this._wireSend(TEXT_KEEPALIVE_PONG);
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
this._onError(new Error("unexpected text websocket frame"));
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
this._markActivity();
|
|
424
445
|
let frame;
|
|
425
446
|
try {
|
|
426
447
|
frame = decode(asU8(evt.data));
|
|
@@ -720,18 +741,41 @@ export class RemotePairingTransport {
|
|
|
720
741
|
|
|
721
742
|
_startPing() {
|
|
722
743
|
this._stopPing();
|
|
723
|
-
this.
|
|
724
|
-
|
|
725
|
-
|
|
744
|
+
if (!this._pingIntervalMs) return;
|
|
745
|
+
if (!this._lastActivityAtMs) this._lastActivityAtMs = Date.now();
|
|
746
|
+
this._schedulePing();
|
|
726
747
|
}
|
|
727
748
|
|
|
728
749
|
_stopPing() {
|
|
729
750
|
if (this._pingTimer != null) {
|
|
730
|
-
|
|
751
|
+
clearTimeout(this._pingTimer);
|
|
731
752
|
this._pingTimer = null;
|
|
732
753
|
}
|
|
733
754
|
}
|
|
734
755
|
|
|
756
|
+
_schedulePing() {
|
|
757
|
+
if (!this._pingIntervalMs || this._closed || !this._ws || this._ws.readyState !== 1) return;
|
|
758
|
+
const interval = Math.max(MIN_PING_INTERVAL_MS, Number(this._pingIntervalMs) || DEFAULT_PING_INTERVAL_MS);
|
|
759
|
+
const idleFor = Date.now() - (this._lastActivityAtMs || Date.now());
|
|
760
|
+
const delay = Math.max(1, interval - idleFor);
|
|
761
|
+
this._pingTimer = setTimeout(() => {
|
|
762
|
+
this._pingTimer = null;
|
|
763
|
+
if (this._closed || !this._ws || this._ws.readyState !== 1) return;
|
|
764
|
+
const nextIdleFor = Date.now() - (this._lastActivityAtMs || Date.now());
|
|
765
|
+
if (nextIdleFor >= interval) {
|
|
766
|
+
this._sendPing();
|
|
767
|
+
}
|
|
768
|
+
this._schedulePing();
|
|
769
|
+
}, delay);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
_markActivity({ reschedulePing = true } = {}) {
|
|
773
|
+
this._lastActivityAtMs = Date.now();
|
|
774
|
+
if (!reschedulePing || this._pingTimer == null) return;
|
|
775
|
+
this._stopPing();
|
|
776
|
+
this._schedulePing();
|
|
777
|
+
}
|
|
778
|
+
|
|
735
779
|
// -------------------------------------------------------------------------
|
|
736
780
|
// Frame sends
|
|
737
781
|
// -------------------------------------------------------------------------
|
|
@@ -751,22 +795,27 @@ export class RemotePairingTransport {
|
|
|
751
795
|
}
|
|
752
796
|
|
|
753
797
|
_sendPing() {
|
|
754
|
-
this._wireSend(
|
|
798
|
+
if (this._wireSend(TEXT_KEEPALIVE_PING, { countAsActivity: false })) {
|
|
799
|
+
this._markActivity({ reschedulePing: false });
|
|
800
|
+
}
|
|
755
801
|
}
|
|
756
802
|
|
|
757
803
|
_sendResumeReq(lastSeenSeq) {
|
|
758
804
|
this._wireSend(encodeResumeReq(lastSeenSeq));
|
|
759
805
|
}
|
|
760
806
|
|
|
761
|
-
_wireSend(bytes) {
|
|
807
|
+
_wireSend(bytes, { countAsActivity = true } = {}) {
|
|
762
808
|
if (!this._ws || this._ws.readyState !== 1 /* OPEN */) {
|
|
763
809
|
this._log.warn?.("send while WS not open — dropping");
|
|
764
|
-
return;
|
|
810
|
+
return false;
|
|
765
811
|
}
|
|
766
812
|
try {
|
|
767
813
|
this._ws.send(bytes);
|
|
814
|
+
if (countAsActivity) this._markActivity();
|
|
815
|
+
return true;
|
|
768
816
|
} catch (err) {
|
|
769
817
|
this._onError(err);
|
|
818
|
+
return false;
|
|
770
819
|
}
|
|
771
820
|
}
|
|
772
821
|
|