@livedesk/hub 0.1.53 → 0.1.55
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 +2 -2
- package/src/remote-clipboard-contract.mjs +4 -2
- package/src/remote-hub.js +98 -31
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livedesk/hub",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.55",
|
|
4
4
|
"description": "VuvoDesk local Hub API and browser frame bridge",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/server.js",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
|
19
|
-
"@livedesk/runtime-core": "0.1.
|
|
19
|
+
"@livedesk/runtime-core": "0.1.7",
|
|
20
20
|
"@openai/codex-sdk": "0.145.0",
|
|
21
21
|
"cors": "^2.8.5",
|
|
22
22
|
"express": "^4.21.2",
|
|
@@ -465,8 +465,10 @@ export function normalizeRemoteClipboardCommandResult(action, operationId, value
|
|
|
465
465
|
response.manifest = normalizeRemoteClipboardManifest(result.manifest, operationId);
|
|
466
466
|
response.kind = response.manifest.contentKind;
|
|
467
467
|
} else {
|
|
468
|
-
const
|
|
469
|
-
|
|
468
|
+
const explicitContentKind = String(result.contentKind || '').trim().toLowerCase();
|
|
469
|
+
const legacyKind = String(result.kind || '').trim().toLowerCase();
|
|
470
|
+
if (explicitContentKind) response.kind = normalizeContentKind(explicitContentKind);
|
|
471
|
+
else if (['text', 'image', 'files'].includes(legacyKind)) response.kind = legacyKind;
|
|
470
472
|
}
|
|
471
473
|
for (const key of ['receivedBytes', 'totalBytes']) {
|
|
472
474
|
if (result[key] !== undefined) {
|
package/src/remote-hub.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
|
-
import net from 'net';
|
|
2
|
-
import os from 'os';
|
|
3
|
-
import crypto from 'crypto';
|
|
4
|
-
import {
|
|
1
|
+
import net from 'net';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import crypto from 'crypto';
|
|
4
|
+
import {
|
|
5
|
+
acceptSecureDirectSocket,
|
|
6
|
+
isDirectSecureChannel,
|
|
7
|
+
isLoopbackSocketAddress,
|
|
8
|
+
isPrivateSocketAddress,
|
|
9
|
+
isSecureDirectHandshakeStart
|
|
10
|
+
} from '../../runtime-core/src/direct-secure-transport.js';
|
|
11
|
+
import { createHubRelayControl } from './transport/relay-hub-control.js';
|
|
5
12
|
import { parseExactLiveStreamMonitorIndex } from './live-stream-monitor-contract.js';
|
|
6
13
|
import {
|
|
7
14
|
BoundedSegmentedBuffer,
|
|
@@ -1334,9 +1341,10 @@ function normalizeRemoteInputEvent(value = {}) {
|
|
|
1334
1341
|
// input must prove the exact monitor owned by its capture generation.
|
|
1335
1342
|
monitorIndex,
|
|
1336
1343
|
normalizedX: Number.isFinite(normalizedX) ? Math.max(0, Math.min(1, normalizedX)) : undefined,
|
|
1337
|
-
normalizedY: Number.isFinite(normalizedY) ? Math.max(0, Math.min(1, normalizedY)) : undefined,
|
|
1338
|
-
button: safeString(input.button || input.Button, 24),
|
|
1339
|
-
|
|
1344
|
+
normalizedY: Number.isFinite(normalizedY) ? Math.max(0, Math.min(1, normalizedY)) : undefined,
|
|
1345
|
+
button: safeString(input.button || input.Button, 24),
|
|
1346
|
+
inputSource: safeString(input.inputSource || input.InputSource, 48).toLowerCase(),
|
|
1347
|
+
deltaX: Number.isFinite(deltaX) ? Math.max(-4096, Math.min(4096, deltaX)) : 0,
|
|
1340
1348
|
deltaY: Number.isFinite(deltaY) ? Math.max(-4096, Math.min(4096, deltaY)) : 0,
|
|
1341
1349
|
key,
|
|
1342
1350
|
code,
|
|
@@ -2396,9 +2404,12 @@ export function createRemoteHub(options = {}) {
|
|
|
2396
2404
|
const hostInstanceId = safeString(options.hostInstanceId ?? env.LIVEDESK_HUB_INSTANCE_ID ?? env.MINDEXEC_BRIDGE_INSTANCE_ID ?? crypto.randomUUID(), 128) || crypto.randomUUID();
|
|
2397
2405
|
const publicEndpoint = safeString(env.LIVEDESK_REMOTE_PUBLIC_ENDPOINT || env.MINDEXEC_REMOTE_PUBLIC_ENDPOINT || env.REMOTE_HUB_PUBLIC_ENDPOINT, 256);
|
|
2398
2406
|
const publicHost = safeString(env.LIVEDESK_REMOTE_PUBLIC_HOST || env.MINDEXEC_REMOTE_PUBLIC_HOST || env.REMOTE_HUB_PUBLIC_HOST, 128);
|
|
2399
|
-
const pairToken = safeString(
|
|
2400
|
-
options.pairToken || env.REMOTE_HUB_PAIR_TOKEN || env.LIVEDESK_CLIENT_PAIR_TOKEN || env.MINDEXEC_REMOTE_PAIR_TOKEN || crypto.randomBytes(
|
|
2401
|
-
256);
|
|
2407
|
+
const pairToken = safeString(
|
|
2408
|
+
options.pairToken || env.REMOTE_HUB_PAIR_TOKEN || env.LIVEDESK_CLIENT_PAIR_TOKEN || env.MINDEXEC_REMOTE_PAIR_TOKEN || crypto.randomBytes(32).toString('hex'),
|
|
2409
|
+
256);
|
|
2410
|
+
const allowInsecurePrivateDirect = isEnabledValue(
|
|
2411
|
+
options.allowInsecureDirect ?? env.LIVEDESK_ALLOW_INSECURE_DIRECT,
|
|
2412
|
+
false);
|
|
2402
2413
|
const relayControl = options.relayControl && typeof options.relayControl === 'object'
|
|
2403
2414
|
? options.relayControl
|
|
2404
2415
|
: createHubRelayControl({
|
|
@@ -7948,10 +7959,24 @@ export function createRemoteHub(options = {}) {
|
|
|
7948
7959
|
function handleAgentMessage(socket, state, message) {
|
|
7949
7960
|
if (!message || typeof message !== 'object') {
|
|
7950
7961
|
return;
|
|
7951
|
-
}
|
|
7952
|
-
|
|
7953
|
-
if (!state.authenticated) {
|
|
7954
|
-
if (
|
|
7962
|
+
}
|
|
7963
|
+
|
|
7964
|
+
if (!state.authenticated) {
|
|
7965
|
+
if (socket.__liveDeskSecureDirect === true) {
|
|
7966
|
+
const applicationChannel = message.type === 'slot.assign'
|
|
7967
|
+
? 'control'
|
|
7968
|
+
: message.type === 'hello'
|
|
7969
|
+
? (safeString(message.channel || message.Channel, 40).toLowerCase() || 'control')
|
|
7970
|
+
: '';
|
|
7971
|
+
if (applicationChannel
|
|
7972
|
+
&& (!isDirectSecureChannel(applicationChannel)
|
|
7973
|
+
|| applicationChannel !== socket.__liveDeskSecureChannel)) {
|
|
7974
|
+
writeJsonLine(socket, { type: 'error', error: 'direct-secure-channel-mismatch' });
|
|
7975
|
+
socket.end();
|
|
7976
|
+
return;
|
|
7977
|
+
}
|
|
7978
|
+
}
|
|
7979
|
+
if (message.type === 'hello' && socket.__liveDeskRelayControl === true) {
|
|
7955
7980
|
message = {
|
|
7956
7981
|
...message,
|
|
7957
7982
|
capabilities: {
|
|
@@ -7987,7 +8012,7 @@ export function createRemoteHub(options = {}) {
|
|
|
7987
8012
|
return;
|
|
7988
8013
|
}
|
|
7989
8014
|
|
|
7990
|
-
const channel = safeString(message.channel || message.Channel, 40).toLowerCase();
|
|
8015
|
+
const channel = safeString(message.channel || message.Channel, 40).toLowerCase() || 'control';
|
|
7991
8016
|
if (channel === 'input') {
|
|
7992
8017
|
const device = attachInputSocket(socket, message);
|
|
7993
8018
|
if (!device) {
|
|
@@ -8537,18 +8562,60 @@ export function createRemoteHub(options = {}) {
|
|
|
8537
8562
|
if (firstChunk) {
|
|
8538
8563
|
processData(firstChunk);
|
|
8539
8564
|
}
|
|
8540
|
-
}
|
|
8541
|
-
|
|
8542
|
-
function handleSocket(socket) {
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
if (
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
|
|
8551
|
-
});
|
|
8565
|
+
}
|
|
8566
|
+
|
|
8567
|
+
function handleSocket(socket) {
|
|
8568
|
+
allSockets.add(socket);
|
|
8569
|
+
const prefaceTimer = setTimeout(() => {
|
|
8570
|
+
if (!socket.destroyed) socket.destroy();
|
|
8571
|
+
}, 10_000);
|
|
8572
|
+
prefaceTimer.unref?.();
|
|
8573
|
+
socket.once('close', () => {
|
|
8574
|
+
clearTimeout(prefaceTimer);
|
|
8575
|
+
allSockets.delete(socket);
|
|
8576
|
+
});
|
|
8577
|
+
socket.once('data', chunk => {
|
|
8578
|
+
clearTimeout(prefaceTimer);
|
|
8579
|
+
const firstChunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
8580
|
+
if (isSecureDirectHandshakeStart(firstChunk)) {
|
|
8581
|
+
void acceptSecureDirectSocket(socket, firstChunk, pairToken)
|
|
8582
|
+
.then(secureSocket => {
|
|
8583
|
+
allSockets.delete(socket);
|
|
8584
|
+
handleTcpAgentSocket(secureSocket);
|
|
8585
|
+
})
|
|
8586
|
+
.catch(error => {
|
|
8587
|
+
if (socket.destroyed) return;
|
|
8588
|
+
if (error?.code === 'LIVEDESK_INVALID_PAIR_TOKEN_REJECTED') {
|
|
8589
|
+
const forcedCloseTimer = setTimeout(() => socket.destroy(), 1_000);
|
|
8590
|
+
forcedCloseTimer.unref?.();
|
|
8591
|
+
socket.once('close', () => clearTimeout(forcedCloseTimer));
|
|
8592
|
+
if (typeof socket.destroySoon === 'function') socket.destroySoon();
|
|
8593
|
+
else socket.end();
|
|
8594
|
+
return;
|
|
8595
|
+
}
|
|
8596
|
+
socket.destroy();
|
|
8597
|
+
});
|
|
8598
|
+
return;
|
|
8599
|
+
}
|
|
8600
|
+
|
|
8601
|
+
const remoteAddress = socket.remoteAddress;
|
|
8602
|
+
const allowLegacyPlaintext = isLoopbackSocketAddress(remoteAddress)
|
|
8603
|
+
|| (allowInsecurePrivateDirect && isPrivateSocketAddress(remoteAddress));
|
|
8604
|
+
if (isRemoteHubWebSocketUpgradeStart(firstChunk)) {
|
|
8605
|
+
if (!allowLegacyPlaintext) {
|
|
8606
|
+
socket.destroy();
|
|
8607
|
+
return;
|
|
8608
|
+
}
|
|
8609
|
+
handleWebSocketUpgradeSocket(socket, firstChunk);
|
|
8610
|
+
return;
|
|
8611
|
+
}
|
|
8612
|
+
|
|
8613
|
+
if (!allowLegacyPlaintext) {
|
|
8614
|
+
socket.destroy();
|
|
8615
|
+
return;
|
|
8616
|
+
}
|
|
8617
|
+
handleTcpAgentSocket(socket, firstChunk);
|
|
8618
|
+
});
|
|
8552
8619
|
|
|
8553
8620
|
socket.once('error', () => {
|
|
8554
8621
|
// The transport-specific handler owns logging after the first byte.
|
|
@@ -8570,10 +8637,10 @@ export function createRemoteHub(options = {}) {
|
|
|
8570
8637
|
started = true;
|
|
8571
8638
|
boundPort = candidateServer.address()?.port || port;
|
|
8572
8639
|
lastError = '';
|
|
8573
|
-
logEvent('remote', `VuvoDesk Hub client endpoint listening on tcp://${host}:${boundPort}`, 'success');
|
|
8574
|
-
if (host === '0.0.0.0' || host === '::') {
|
|
8575
|
-
logWarn('remote', 'VuvoDesk Hub client endpoint is externally reachable.
|
|
8576
|
-
}
|
|
8640
|
+
logEvent('remote', `VuvoDesk Hub client endpoint listening on tcp://${host}:${boundPort}`, 'success');
|
|
8641
|
+
if (host === '0.0.0.0' || host === '::') {
|
|
8642
|
+
logWarn('remote', 'VuvoDesk Hub client endpoint is externally reachable. Non-loopback Direct connections require authenticated encryption.');
|
|
8643
|
+
}
|
|
8577
8644
|
emitRemoteEvent('RemoteHubStarted', null);
|
|
8578
8645
|
resolve();
|
|
8579
8646
|
});
|