@livedesk/client 0.1.219 → 0.1.221
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/bin/livedesk-client.js +98 -12
- package/package.json +52 -52
package/bin/livedesk-client.js
CHANGED
|
@@ -2791,6 +2791,8 @@ export async function resolveManagerFromPin(supabase, pin, options = {}) {
|
|
|
2791
2791
|
endpointCandidates,
|
|
2792
2792
|
directReachable: target.directReachable,
|
|
2793
2793
|
connectionTransport: target.connectionTransport,
|
|
2794
|
+
directProbeSkipped: target.directProbeSkipped === true,
|
|
2795
|
+
directProbeSkipReason: String(target.directProbeSkipReason || ''),
|
|
2794
2796
|
discoverySource: 'supabase'
|
|
2795
2797
|
};
|
|
2796
2798
|
}
|
|
@@ -3511,6 +3513,63 @@ function parseManagerEndpoint(value) {
|
|
|
3511
3513
|
return { host, port };
|
|
3512
3514
|
}
|
|
3513
3515
|
|
|
3516
|
+
function ipv4ToUnsignedInteger(address) {
|
|
3517
|
+
const octets = String(address || '').split('.').map(Number);
|
|
3518
|
+
if (octets.length !== 4
|
|
3519
|
+
|| octets.some(octet => !Number.isInteger(octet) || octet < 0 || octet > 255)) {
|
|
3520
|
+
return null;
|
|
3521
|
+
}
|
|
3522
|
+
return (((octets[0] << 24) >>> 0)
|
|
3523
|
+
+ (octets[1] << 16)
|
|
3524
|
+
+ (octets[2] << 8)
|
|
3525
|
+
+ octets[3]) >>> 0;
|
|
3526
|
+
}
|
|
3527
|
+
|
|
3528
|
+
function isAutomaticPrivateIpv4(address) {
|
|
3529
|
+
const value = ipv4ToUnsignedInteger(address);
|
|
3530
|
+
if (value === null) return false;
|
|
3531
|
+
const first = value >>> 24;
|
|
3532
|
+
const second = (value >>> 16) & 0xff;
|
|
3533
|
+
return first === 10
|
|
3534
|
+
|| first === 127
|
|
3535
|
+
|| (first === 100 && second >= 64 && second <= 127)
|
|
3536
|
+
|| (first === 169 && second === 254)
|
|
3537
|
+
|| (first === 172 && second >= 16 && second <= 31)
|
|
3538
|
+
|| (first === 192 && second === 168);
|
|
3539
|
+
}
|
|
3540
|
+
|
|
3541
|
+
export function isEndpointOnLocalNetwork(endpoint, networkInterfaces = os.networkInterfaces()) {
|
|
3542
|
+
const parsedEndpoint = parseManagerEndpoint(endpoint);
|
|
3543
|
+
if (!parsedEndpoint || !net.isIPv4(parsedEndpoint.host)) return false;
|
|
3544
|
+
const target = ipv4ToUnsignedInteger(parsedEndpoint.host);
|
|
3545
|
+
if (target === null) return false;
|
|
3546
|
+
if ((target >>> 24) === 127) return true;
|
|
3547
|
+
for (const entries of Object.values(networkInterfaces || {})) {
|
|
3548
|
+
for (const entry of Array.isArray(entries) ? entries : []) {
|
|
3549
|
+
if (!entry || entry.internal || (entry.family !== 'IPv4' && entry.family !== 4)) continue;
|
|
3550
|
+
const address = ipv4ToUnsignedInteger(entry.address);
|
|
3551
|
+
const netmask = ipv4ToUnsignedInteger(entry.netmask);
|
|
3552
|
+
if (address === null || netmask === null) continue;
|
|
3553
|
+
if (address === target || ((address & netmask) >>> 0) === ((target & netmask) >>> 0)) {
|
|
3554
|
+
return true;
|
|
3555
|
+
}
|
|
3556
|
+
}
|
|
3557
|
+
}
|
|
3558
|
+
return false;
|
|
3559
|
+
}
|
|
3560
|
+
|
|
3561
|
+
export function shouldSkipAutomaticDirectProbe(endpoint, options = {}) {
|
|
3562
|
+
if (options.forcePrivateDirectProbe === true
|
|
3563
|
+
|| /^(1|true|yes|on)$/i.test(String(process.env.LIVEDESK_FORCE_PRIVATE_DIRECT_PROBE || '').trim())) {
|
|
3564
|
+
return false;
|
|
3565
|
+
}
|
|
3566
|
+
const parsedEndpoint = parseManagerEndpoint(endpoint);
|
|
3567
|
+
return !!parsedEndpoint
|
|
3568
|
+
&& net.isIPv4(parsedEndpoint.host)
|
|
3569
|
+
&& isAutomaticPrivateIpv4(parsedEndpoint.host)
|
|
3570
|
+
&& !isEndpointOnLocalNetwork(endpoint, options.networkInterfaces);
|
|
3571
|
+
}
|
|
3572
|
+
|
|
3514
3573
|
function requestHubSlotAssignment({ manager, pairToken, deviceId, slotNumber, timeoutMs = 5000 }) {
|
|
3515
3574
|
const endpoint = parseManagerEndpoint(manager);
|
|
3516
3575
|
const normalizedPairToken = String(pairToken || '').trim();
|
|
@@ -3639,10 +3698,21 @@ export async function chooseManagerConnectionTarget(candidates, options = {}) {
|
|
|
3639
3698
|
.filter(Boolean))];
|
|
3640
3699
|
if (endpoints.length === 0) return null;
|
|
3641
3700
|
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3701
|
+
// A signed 192.168/10/172.16 endpoint from another site cannot normally be
|
|
3702
|
+
// reached from this Client. Skip that full TCP timeout only in automatic
|
|
3703
|
+
// relay-capable discovery. Direct-only mode and an explicit override still
|
|
3704
|
+
// probe it for routed VPN and port-forwarding deployments.
|
|
3705
|
+
const skippedPrivateEndpoints = options.allowRelayFallback === true
|
|
3706
|
+
? endpoints.filter(endpoint => shouldSkipAutomaticDirectProbe(endpoint, options))
|
|
3707
|
+
: [];
|
|
3708
|
+
const skippedPrivateSet = new Set(skippedPrivateEndpoints);
|
|
3709
|
+
const directCandidates = endpoints.filter(endpoint => !skippedPrivateSet.has(endpoint));
|
|
3710
|
+
const manager = directCandidates.length > 0
|
|
3711
|
+
? await chooseReachableEndpoint(directCandidates, {
|
|
3712
|
+
requireReachable: true,
|
|
3713
|
+
probeEndpoint: options.probeEndpoint
|
|
3714
|
+
})
|
|
3715
|
+
: '';
|
|
3646
3716
|
if (manager) {
|
|
3647
3717
|
return {
|
|
3648
3718
|
manager,
|
|
@@ -3655,12 +3725,16 @@ export async function chooseManagerConnectionTarget(candidates, options = {}) {
|
|
|
3655
3725
|
}
|
|
3656
3726
|
return {
|
|
3657
3727
|
// The account/PIN response is the authority for this retry candidate.
|
|
3658
|
-
// RemoteFast
|
|
3659
|
-
// pair-token-authenticated encrypted relay rather than keeping the
|
|
3660
|
-
// launcher in an indefinite TCP reachability loop.
|
|
3728
|
+
// RemoteFast uses relay control to negotiate encrypted UDP P2P video.
|
|
3661
3729
|
manager: endpoints[0],
|
|
3662
3730
|
directReachable: false,
|
|
3663
|
-
connectionTransport: 'relay-fallback'
|
|
3731
|
+
connectionTransport: 'relay-fallback',
|
|
3732
|
+
...(skippedPrivateEndpoints.length > 0
|
|
3733
|
+
? {
|
|
3734
|
+
directProbeSkipped: true,
|
|
3735
|
+
directProbeSkipReason: 'private-endpoint-outside-local-subnet'
|
|
3736
|
+
}
|
|
3737
|
+
: {})
|
|
3664
3738
|
};
|
|
3665
3739
|
}
|
|
3666
3740
|
|
|
@@ -3929,7 +4003,9 @@ export async function resolveManagerFromSupabase(supabase, options = {}) {
|
|
|
3929
4003
|
hubDeviceId: String(data.node_id || '').trim(),
|
|
3930
4004
|
endpointCandidates,
|
|
3931
4005
|
directReachable: target.directReachable,
|
|
3932
|
-
connectionTransport: target.connectionTransport
|
|
4006
|
+
connectionTransport: target.connectionTransport,
|
|
4007
|
+
directProbeSkipped: target.directProbeSkipped === true,
|
|
4008
|
+
directProbeSkipReason: String(target.directProbeSkipReason || '')
|
|
3933
4009
|
};
|
|
3934
4010
|
}
|
|
3935
4011
|
|
|
@@ -4050,6 +4126,7 @@ async function prepareLoginConnection(parsed, existingConnectionPage = null, get
|
|
|
4050
4126
|
let discoverySource = '';
|
|
4051
4127
|
let connectionTransport = '';
|
|
4052
4128
|
let directReachable = false;
|
|
4129
|
+
let directProbeSkipReason = '';
|
|
4053
4130
|
const allowRelayFallback = transportAllowsRelay(parsed.transport);
|
|
4054
4131
|
|
|
4055
4132
|
if (shouldLogin) {
|
|
@@ -4123,12 +4200,15 @@ async function prepareLoginConnection(parsed, existingConnectionPage = null, get
|
|
|
4123
4200
|
discoverySource = choice.discoverySource || 'supabase';
|
|
4124
4201
|
connectionTransport = choice.connectionTransport || 'direct-tcp';
|
|
4125
4202
|
directReachable = choice.directReachable !== false;
|
|
4203
|
+
directProbeSkipReason = String(choice.directProbeSkipReason || '');
|
|
4126
4204
|
connectionPage?.update({
|
|
4127
4205
|
endpointCandidates: choice.endpointCandidates || [],
|
|
4128
4206
|
manager,
|
|
4129
4207
|
assignedHubId: choice.hubDeviceId || '',
|
|
4130
4208
|
message: connectionTransport === 'relay-fallback'
|
|
4131
|
-
?
|
|
4209
|
+
? directProbeSkipReason
|
|
4210
|
+
? `LiveDesk Hub found at ${manager}; the private endpoint is outside this local subnet, so encrypted relay/P2P negotiation starts immediately.`
|
|
4211
|
+
: `LiveDesk Hub found at ${manager}; starting encrypted relay fallback.`
|
|
4132
4212
|
: `Connected to LiveDesk Hub at ${manager}.`
|
|
4133
4213
|
});
|
|
4134
4214
|
console.log(connectionTransport === 'relay-fallback'
|
|
@@ -4210,17 +4290,22 @@ async function prepareLoginConnection(parsed, existingConnectionPage = null, get
|
|
|
4210
4290
|
pair = resolved.pair;
|
|
4211
4291
|
connectionTransport = resolved.connectionTransport || 'direct-tcp';
|
|
4212
4292
|
directReachable = resolved.directReachable !== false;
|
|
4293
|
+
directProbeSkipReason = String(resolved.directProbeSkipReason || '');
|
|
4213
4294
|
connectionPage?.update({
|
|
4214
4295
|
endpointCandidates: resolved.endpointCandidates || [],
|
|
4215
4296
|
manager,
|
|
4216
4297
|
assignedHubId: resolved.hubDeviceId || '',
|
|
4217
4298
|
message: connectionTransport === 'relay-fallback'
|
|
4218
|
-
?
|
|
4299
|
+
? directProbeSkipReason
|
|
4300
|
+
? `LiveDesk Hub found at ${manager}; the private endpoint is outside this local subnet, so encrypted relay/P2P negotiation starts immediately.`
|
|
4301
|
+
: `LiveDesk Hub found at ${manager}; starting encrypted relay fallback.`
|
|
4219
4302
|
: `Connected to LiveDesk Hub at ${manager}.`
|
|
4220
4303
|
});
|
|
4221
4304
|
}
|
|
4222
4305
|
console.log(connectionTransport === 'relay-fallback'
|
|
4223
|
-
?
|
|
4306
|
+
? directProbeSkipReason
|
|
4307
|
+
? `Found LiveDesk Hub record at ${manager}; private endpoint is outside this local subnet, so Direct TCP was skipped and encrypted relay control will negotiate UDP P2P video.`
|
|
4308
|
+
: `Found LiveDesk Hub record at ${manager}; direct TCP did not answer, so RemoteFast will use encrypted relay control and prefer UDP P2P video.`
|
|
4224
4309
|
: `Found LiveDesk Hub at ${manager}.`);
|
|
4225
4310
|
}
|
|
4226
4311
|
// Exact-package updates preserve the already paired Hub endpoint and pass
|
|
@@ -4285,6 +4370,7 @@ async function prepareLoginConnection(parsed, existingConnectionPage = null, get
|
|
|
4285
4370
|
forwarded,
|
|
4286
4371
|
connectionTransport: connectionTransport || 'direct-tcp',
|
|
4287
4372
|
directReachable: connectionTransport === 'relay-fallback' ? false : (directReachable || !shouldLogin),
|
|
4373
|
+
directProbeSkipReason,
|
|
4288
4374
|
discoverySource,
|
|
4289
4375
|
rediscoverOnDisconnect: shouldLogin,
|
|
4290
4376
|
rediscoverOnInvalidPair: shouldLogin
|
package/package.json
CHANGED
|
@@ -1,52 +1,52 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@livedesk/client",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "LiveDesk local remote client",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"bin": {
|
|
7
|
-
"client": "bin/livedesk-client.js",
|
|
8
|
-
"livedesk-client": "bin/livedesk-client.js",
|
|
9
|
-
"livedesk-client-node": "bin/livedesk-client-node.js",
|
|
10
|
-
"livedesk-client-fast": "bin/livedesk-client-fast.js"
|
|
11
|
-
},
|
|
12
|
-
"files": [
|
|
13
|
-
"bin/",
|
|
14
|
-
"src/",
|
|
15
|
-
"tests/",
|
|
16
|
-
"README.md",
|
|
17
|
-
"THIRD_PARTY_NOTICES.md"
|
|
18
|
-
],
|
|
19
|
-
"scripts": {
|
|
20
|
-
"check": "node --check bin/client-version.js && node --check bin/livedesk-client.js && node --check bin/livedesk-client-node.js && node --check bin/livedesk-client-update-bootstrap.cjs && node --check bin/livedesk-client-fast.js",
|
|
21
|
-
"test:version": "node --test tests/client-version.test.mjs",
|
|
22
|
-
"pack:dry": "npm pack --dry-run"
|
|
23
|
-
},
|
|
24
|
-
"keywords": [
|
|
25
|
-
"livedesk",
|
|
26
|
-
"remote",
|
|
27
|
-
"agent",
|
|
28
|
-
"local",
|
|
29
|
-
"desktop"
|
|
30
|
-
],
|
|
31
|
-
"license": "MIT",
|
|
32
|
-
"engines": {
|
|
33
|
-
"node": ">=20"
|
|
34
|
-
},
|
|
35
|
-
"dependencies": {
|
|
36
|
-
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
|
37
|
-
"ffmpeg-static": "^5.3.0",
|
|
38
|
-
"@livedesk/runtime-core": "0.1.1",
|
|
39
|
-
"@supabase/supabase-js": "^2.110.0",
|
|
40
|
-
"node-screenshots": "^0.2.8",
|
|
41
|
-
"ws": "^8.18.3"
|
|
42
|
-
},
|
|
43
|
-
"optionalDependencies": {
|
|
44
|
-
"@livedesk/fast-linux-x64": "0.1.
|
|
45
|
-
"@livedesk/fast-osx-arm64": "0.1.
|
|
46
|
-
"@livedesk/fast-osx-x64": "0.1.
|
|
47
|
-
"@livedesk/fast-win-x64": "0.1.
|
|
48
|
-
},
|
|
49
|
-
"publishConfig": {
|
|
50
|
-
"access": "public"
|
|
51
|
-
}
|
|
52
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@livedesk/client",
|
|
3
|
+
"version": "0.1.221",
|
|
4
|
+
"description": "LiveDesk local remote client",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"client": "bin/livedesk-client.js",
|
|
8
|
+
"livedesk-client": "bin/livedesk-client.js",
|
|
9
|
+
"livedesk-client-node": "bin/livedesk-client-node.js",
|
|
10
|
+
"livedesk-client-fast": "bin/livedesk-client-fast.js"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"bin/",
|
|
14
|
+
"src/",
|
|
15
|
+
"tests/",
|
|
16
|
+
"README.md",
|
|
17
|
+
"THIRD_PARTY_NOTICES.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"check": "node --check bin/client-version.js && node --check bin/livedesk-client.js && node --check bin/livedesk-client-node.js && node --check bin/livedesk-client-update-bootstrap.cjs && node --check bin/livedesk-client-fast.js",
|
|
21
|
+
"test:version": "node --test tests/client-version.test.mjs",
|
|
22
|
+
"pack:dry": "npm pack --dry-run"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"livedesk",
|
|
26
|
+
"remote",
|
|
27
|
+
"agent",
|
|
28
|
+
"local",
|
|
29
|
+
"desktop"
|
|
30
|
+
],
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=20"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
|
37
|
+
"ffmpeg-static": "^5.3.0",
|
|
38
|
+
"@livedesk/runtime-core": "0.1.1",
|
|
39
|
+
"@supabase/supabase-js": "^2.110.0",
|
|
40
|
+
"node-screenshots": "^0.2.8",
|
|
41
|
+
"ws": "^8.18.3"
|
|
42
|
+
},
|
|
43
|
+
"optionalDependencies": {
|
|
44
|
+
"@livedesk/fast-linux-x64": "0.1.426",
|
|
45
|
+
"@livedesk/fast-osx-arm64": "0.1.426",
|
|
46
|
+
"@livedesk/fast-osx-x64": "0.1.426",
|
|
47
|
+
"@livedesk/fast-win-x64": "0.1.426"
|
|
48
|
+
},
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "public"
|
|
51
|
+
}
|
|
52
|
+
}
|