@livedesk/hub 0.1.28 → 0.1.30
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": "@livedesk/hub",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30",
|
|
4
4
|
"description": "LiveDesk local Hub API and browser frame bridge",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/server.js",
|
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
"scripts": {
|
|
12
12
|
"dev": "node src/server.js",
|
|
13
13
|
"start": "node src/server.js",
|
|
14
|
-
"check": "node --check src/server.js && node --check src/remote-hub.js"
|
|
14
|
+
"check": "node --check src/server.js && node --check src/remote-hub.js",
|
|
15
|
+
"prepublishOnly": "node ../../scripts/livedesk-release-git-gate.mjs"
|
|
15
16
|
},
|
|
16
17
|
"dependencies": {
|
|
17
18
|
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
|
18
|
-
"@livedesk/runtime-core": "0.1.
|
|
19
|
+
"@livedesk/runtime-core": "0.1.2",
|
|
19
20
|
"@openai/codex-sdk": "0.145.0",
|
|
20
21
|
"cors": "^2.8.5",
|
|
21
22
|
"express": "^4.21.2",
|
|
@@ -511,6 +511,7 @@ export function createHubRelayControl({
|
|
|
511
511
|
let timerOwnerHighWater = 0;
|
|
512
512
|
let awaitingDrain = false;
|
|
513
513
|
let lastError = '';
|
|
514
|
+
let lastDisconnectReason = '';
|
|
514
515
|
let lastConnectedAt = '';
|
|
515
516
|
let lastTransitionAt = '';
|
|
516
517
|
let state = enabled ? 'idle' : 'disabled';
|
|
@@ -982,8 +983,15 @@ export function createHubRelayControl({
|
|
|
982
983
|
return;
|
|
983
984
|
}
|
|
984
985
|
if (message.type === 'relay.close') {
|
|
986
|
+
const reason = safeText(message.reason, 80) || 'relay-server-closed';
|
|
987
|
+
if (!peerId) {
|
|
988
|
+
lastError = reason;
|
|
989
|
+
lastDisconnectReason = reason;
|
|
990
|
+
connection?.destroy();
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
985
993
|
try {
|
|
986
|
-
closePeer(requirePeerId(message.peerId),
|
|
994
|
+
closePeer(requirePeerId(message.peerId), reason, { notify: false });
|
|
987
995
|
} catch {
|
|
988
996
|
// Invalid untrusted routing fields are ignored.
|
|
989
997
|
}
|
|
@@ -1121,6 +1129,12 @@ export function createHubRelayControl({
|
|
|
1121
1129
|
if (connection !== socket || generation !== connectionGeneration) return;
|
|
1122
1130
|
clearConnectAttemptTimer();
|
|
1123
1131
|
connection = null;
|
|
1132
|
+
const disconnectReason = safeText(lastError, 120) || 'relay-socket-closed';
|
|
1133
|
+
lastDisconnectReason = disconnectReason;
|
|
1134
|
+
logWarn(
|
|
1135
|
+
'relay',
|
|
1136
|
+
`Encrypted relay control disconnected reason=${disconnectReason} peers=${peers.size}`
|
|
1137
|
+
);
|
|
1124
1138
|
clearReceiveChunks();
|
|
1125
1139
|
clearQueue();
|
|
1126
1140
|
closeAllPeers('relay-connection-lost');
|
|
@@ -1340,6 +1354,7 @@ export function createHubRelayControl({
|
|
|
1340
1354
|
queueDropCount: counters.queueDrops,
|
|
1341
1355
|
lastConnectedAt,
|
|
1342
1356
|
lastTransitionAt,
|
|
1357
|
+
lastDisconnectReason,
|
|
1343
1358
|
lastError
|
|
1344
1359
|
};
|
|
1345
1360
|
}
|
|
@@ -96,7 +96,8 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
96
96
|
let rendezvousAddresses = new Set();
|
|
97
97
|
let rendezvousRefreshTimer = null;
|
|
98
98
|
let rendezvousRefreshPromise = null;
|
|
99
|
-
let frameExpiryTimer = null;
|
|
99
|
+
let frameExpiryTimer = null;
|
|
100
|
+
const peerProbeBurstDelaysMs = [0, 120, 360];
|
|
100
101
|
|
|
101
102
|
function emit(type, event = {}) {
|
|
102
103
|
eventHandler(type, event);
|
|
@@ -230,6 +231,33 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
230
231
|
return true;
|
|
231
232
|
}
|
|
232
233
|
|
|
234
|
+
function clearPeerProbeTimers(session) {
|
|
235
|
+
if (!session?.peerProbeTimers) return;
|
|
236
|
+
for (const timer of session.peerProbeTimers) clearTimeout(timer);
|
|
237
|
+
session.peerProbeTimers.clear();
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function schedulePeerProbeBurst(session, endpoint) {
|
|
241
|
+
clearPeerProbeTimers(session);
|
|
242
|
+
for (const delayMs of peerProbeBurstDelaysMs) {
|
|
243
|
+
const sendProbe = () => {
|
|
244
|
+
if (sessions.get(session.deviceId) !== session
|
|
245
|
+
|| !sameEndpoint(session.peerEndpoint, endpoint)) return;
|
|
246
|
+
sendPacket(session, 'probe', { punch: true }, Buffer.alloc(0), endpoint);
|
|
247
|
+
};
|
|
248
|
+
if (delayMs === 0) {
|
|
249
|
+
sendProbe();
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
const timer = setTimeout(() => {
|
|
253
|
+
session.peerProbeTimers.delete(timer);
|
|
254
|
+
sendProbe();
|
|
255
|
+
}, delayMs);
|
|
256
|
+
timer.unref?.();
|
|
257
|
+
session.peerProbeTimers.add(timer);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
233
261
|
function onRendezvousPeer(message, rinfo) {
|
|
234
262
|
const session = sessionsBySessionId.get(safeText(message.roomId, 160));
|
|
235
263
|
if (!session || message.protocol !== UDP_P2P_PROTOCOL || !isTrustedRendezvousSource(rinfo)) return;
|
|
@@ -248,9 +276,9 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
248
276
|
};
|
|
249
277
|
session.hubRendezvousObservedAt = Date.now();
|
|
250
278
|
}
|
|
251
|
-
session.peerEndpoint = { address, port };
|
|
279
|
+
session.peerEndpoint = { address, port };
|
|
252
280
|
session.lastPeerAt = Date.now();
|
|
253
|
-
|
|
281
|
+
schedulePeerProbeBurst(session, session.peerEndpoint);
|
|
254
282
|
emit('UdpPeerEndpointObserved', { deviceId: session.deviceId, endpoint: `${address}:${port}` });
|
|
255
283
|
}
|
|
256
284
|
|
|
@@ -298,7 +326,8 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
298
326
|
emit('UdpProbeReceived', { deviceId: session.deviceId });
|
|
299
327
|
return;
|
|
300
328
|
}
|
|
301
|
-
if (packet.type === 'ready') {
|
|
329
|
+
if (packet.type === 'ready') {
|
|
330
|
+
clearPeerProbeTimers(session);
|
|
302
331
|
session.ready = true;
|
|
303
332
|
sendPacket(session, 'pong', { state: 'ready' });
|
|
304
333
|
session.sendControl?.({
|
|
@@ -396,8 +425,9 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
396
425
|
const id = safeText(deviceId, 160);
|
|
397
426
|
if (!id) return null;
|
|
398
427
|
const previous = sessions.get(id);
|
|
399
|
-
previous?.sendControl?.({ type: 'udp.session.close', reason: 'replaced' });
|
|
400
|
-
if (previous) {
|
|
428
|
+
previous?.sendControl?.({ type: 'udp.session.close', reason: 'replaced' });
|
|
429
|
+
if (previous) {
|
|
430
|
+
clearPeerProbeTimers(previous);
|
|
401
431
|
sessions.delete(id);
|
|
402
432
|
sessionsBySessionId.delete(previous.sessionId);
|
|
403
433
|
sessionsByRouteKey.delete(previous.routeKey);
|
|
@@ -432,7 +462,8 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
432
462
|
authenticatedDatagrams: 0,
|
|
433
463
|
rejectedDatagrams: 0
|
|
434
464
|
},
|
|
435
|
-
ready: false,
|
|
465
|
+
ready: false,
|
|
466
|
+
peerProbeTimers: new Set(),
|
|
436
467
|
createdAt: Date.now(),
|
|
437
468
|
lastPacketAt: 0,
|
|
438
469
|
framesReceived: 0
|
|
@@ -466,7 +497,8 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
466
497
|
const id = safeText(deviceId, 160);
|
|
467
498
|
const session = sessions.get(id);
|
|
468
499
|
if (!session || (tcpSessionId && session.tcpSessionId !== tcpSessionId)) return;
|
|
469
|
-
|
|
500
|
+
clearPeerProbeTimers(session);
|
|
501
|
+
sessions.delete(id);
|
|
470
502
|
sessionsBySessionId.delete(session.sessionId);
|
|
471
503
|
sessionsByRouteKey.delete(session.routeKey);
|
|
472
504
|
session.reassembler.clear();
|
|
@@ -562,7 +594,10 @@ export function createHubUdpTransport({ env = process.env, logEvent = () => {},
|
|
|
562
594
|
async function close() {
|
|
563
595
|
stopRendezvousRefreshTimer();
|
|
564
596
|
stopFrameExpiryTimer();
|
|
565
|
-
for (const session of sessions.values())
|
|
597
|
+
for (const session of sessions.values()) {
|
|
598
|
+
clearPeerProbeTimers(session);
|
|
599
|
+
session.reassembler.clear();
|
|
600
|
+
}
|
|
566
601
|
sessions.clear();
|
|
567
602
|
sessionsBySessionId.clear();
|
|
568
603
|
sessionsByRouteKey.clear();
|