livedesk 0.1.613 → 0.1.615
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.js +28 -15
- package/client/bin/livedesk-client-node.js +65 -76
- package/client/bin/livedesk-client.js +29 -20
- package/client/package.json +6 -6
- package/electron/electron-builder.linux-x64-dev.yml +9 -0
- package/electron/electron-builder.linux-x64.yml +17 -11
- package/electron/electron-builder.mac-arm64-dev.yml +13 -0
- package/electron/electron-builder.mac-x64-dev.yml +13 -0
- package/hub/package.json +2 -2
- package/hub/src/remote-hub.js +94 -28
- package/package.json +6 -6
- package/runtime-core/package.json +2 -2
- package/runtime-core/src/direct-secure-transport.js +752 -0
- package/runtime-core/src/index.js +11 -1
- package/scripts/sync-web-dist.js +4 -2
- package/web/dist/app.webmanifest +1 -1
- package/web/dist/assets/{LiveDeskApp-DSNzN6O2.js → LiveDeskApp-D3LPrsCg.js} +1 -1
- package/web/dist/assets/index-CKNX8KEY.css +1 -0
- package/web/dist/assets/{index-C5gpdiTy.js → index-Dw4bFnPQ.js} +8 -8
- package/web/dist/index.html +2 -2
- package/web/dist/livedesk-build-evidence.json +17 -17
- package/web/dist/sw.js +1 -1
- package/web/dist/assets/index-CpHNEjHy.css +0 -1
package/bin/livedesk.js
CHANGED
|
@@ -5,8 +5,9 @@ import net from 'node:net';
|
|
|
5
5
|
import { dirname, join, resolve } from 'node:path';
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
7
7
|
import { execFile, spawn } from 'node:child_process';
|
|
8
|
-
import {
|
|
9
|
-
|
|
8
|
+
import {
|
|
9
|
+
chmodSync,
|
|
10
|
+
createWriteStream,
|
|
10
11
|
existsSync,
|
|
11
12
|
mkdirSync,
|
|
12
13
|
readFileSync,
|
|
@@ -597,12 +598,18 @@ function normalizePort(value, fallback) {
|
|
|
597
598
|
return number;
|
|
598
599
|
}
|
|
599
600
|
|
|
600
|
-
function readStablePairToken() {
|
|
601
|
-
try {
|
|
602
|
-
const state = JSON.parse(readFileSync(MANAGER_STATE_PATH, 'utf8'));
|
|
603
|
-
const pairToken = String(state?.pairToken || '').trim();
|
|
604
|
-
if (pairToken.length >= 20) {
|
|
605
|
-
|
|
601
|
+
function readStablePairToken() {
|
|
602
|
+
try {
|
|
603
|
+
const state = JSON.parse(readFileSync(MANAGER_STATE_PATH, 'utf8'));
|
|
604
|
+
const pairToken = String(state?.pairToken || '').trim();
|
|
605
|
+
if (pairToken.length >= 20) {
|
|
606
|
+
try {
|
|
607
|
+
chmodSync(MANAGER_STATE_DIR, 0o700);
|
|
608
|
+
chmodSync(MANAGER_STATE_PATH, 0o600);
|
|
609
|
+
} catch {
|
|
610
|
+
// Windows and restricted filesystems may not expose POSIX modes.
|
|
611
|
+
}
|
|
612
|
+
return pairToken;
|
|
606
613
|
}
|
|
607
614
|
} catch {
|
|
608
615
|
// A missing or unreadable state file is repaired below.
|
|
@@ -615,13 +622,19 @@ function getStablePairToken() {
|
|
|
615
622
|
if (existing) {
|
|
616
623
|
return existing;
|
|
617
624
|
}
|
|
618
|
-
const pairToken = randomBytes(
|
|
619
|
-
try {
|
|
620
|
-
mkdirSync(MANAGER_STATE_DIR, { recursive: true });
|
|
621
|
-
writeFileSync(MANAGER_STATE_PATH, JSON.stringify({
|
|
622
|
-
pairToken,
|
|
623
|
-
createdAt: new Date().toISOString()
|
|
624
|
-
}, null, 2));
|
|
625
|
+
const pairToken = randomBytes(32).toString('hex');
|
|
626
|
+
try {
|
|
627
|
+
mkdirSync(MANAGER_STATE_DIR, { recursive: true, mode: 0o700 });
|
|
628
|
+
writeFileSync(MANAGER_STATE_PATH, JSON.stringify({
|
|
629
|
+
pairToken,
|
|
630
|
+
createdAt: new Date().toISOString()
|
|
631
|
+
}, null, 2), { encoding: 'utf8', mode: 0o600 });
|
|
632
|
+
try {
|
|
633
|
+
chmodSync(MANAGER_STATE_DIR, 0o700);
|
|
634
|
+
chmodSync(MANAGER_STATE_PATH, 0o600);
|
|
635
|
+
} catch {
|
|
636
|
+
// Windows and restricted filesystems may not expose POSIX modes.
|
|
637
|
+
}
|
|
625
638
|
} catch {
|
|
626
639
|
// If disk persistence is blocked, keep this manager process usable.
|
|
627
640
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import os from 'os';
|
|
3
|
+
import os from 'os';
|
|
5
4
|
import path from 'path';
|
|
6
5
|
import crypto from 'crypto';
|
|
7
6
|
import { existsSync, promises as fs, statfsSync } from 'fs';
|
|
8
7
|
import { spawn } from 'child_process';
|
|
9
8
|
import { createRequire } from 'node:module';
|
|
10
|
-
import { fileURLToPath } from 'node:url';
|
|
11
|
-
import { resolveAgentShellCommand } from '../src/runtime/agent-shell.js';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
import { resolveAgentShellCommand } from '../src/runtime/agent-shell.js';
|
|
11
|
+
import { connectSecureDirectSocket } from '../../runtime-core/src/direct-secure-transport.js';
|
|
12
12
|
|
|
13
13
|
const require = createRequire(import.meta.url);
|
|
14
14
|
const CLIENT_UPDATE_BOOTSTRAP_PATH = fileURLToPath(
|
|
@@ -488,21 +488,7 @@ function parseManagerAddress(value) {
|
|
|
488
488
|
};
|
|
489
489
|
}
|
|
490
490
|
|
|
491
|
-
function
|
|
492
|
-
const value = String(host || '').trim().toLowerCase().replace(/^\[|\]$/g, '');
|
|
493
|
-
if (!value || value === 'localhost' || value === '::1') return true;
|
|
494
|
-
if (net.isIPv4(value)) {
|
|
495
|
-
const octets = value.split('.').map(Number);
|
|
496
|
-
return octets[0] === 10
|
|
497
|
-
|| (octets[0] === 172 && octets[1] >= 16 && octets[1] <= 31)
|
|
498
|
-
|| (octets[0] === 192 && octets[1] === 168)
|
|
499
|
-
|| octets[0] === 127;
|
|
500
|
-
}
|
|
501
|
-
if (net.isIPv6(value)) return value.startsWith('fc') || value.startsWith('fd') || value.startsWith('fe80:');
|
|
502
|
-
return false;
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
function normalizeDeviceId(value) {
|
|
491
|
+
function normalizeDeviceId(value) {
|
|
506
492
|
return String(value || '').trim().replace(/[^a-zA-Z0-9_.:-]/g, '-').slice(0, 128);
|
|
507
493
|
}
|
|
508
494
|
|
|
@@ -2005,18 +1991,24 @@ async function handleRemoteCommand(socket, options, message, nextFrameSeq, activ
|
|
|
2005
1991
|
});
|
|
2006
1992
|
}
|
|
2007
1993
|
|
|
2008
|
-
function connectOnce(options, deviceId) {
|
|
2009
|
-
const manager = parseManagerAddress(options.manager);
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
1994
|
+
async function connectOnce(options, deviceId) {
|
|
1995
|
+
const manager = parseManagerAddress(options.manager);
|
|
1996
|
+
let socket;
|
|
1997
|
+
try {
|
|
1998
|
+
socket = await connectSecureDirectSocket(
|
|
1999
|
+
manager,
|
|
2000
|
+
options.pair,
|
|
2001
|
+
'control');
|
|
2002
|
+
} catch (error) {
|
|
2003
|
+
if ((error?.code === 'LIVEDESK_INVALID_PAIR_TOKEN'
|
|
2004
|
+
|| error?.message === 'invalid-pair-token')
|
|
2005
|
+
&& options.exitOnInvalidPair) {
|
|
2006
|
+
error.exitCode = EXIT_INVALID_PAIR_TOKEN;
|
|
2007
|
+
}
|
|
2008
|
+
throw error;
|
|
2009
|
+
}
|
|
2010
|
+
return new Promise((resolve, reject) => {
|
|
2011
|
+
let buffer = '';
|
|
2020
2012
|
let heartbeatTimer = null;
|
|
2021
2013
|
let resolved = false;
|
|
2022
2014
|
let frameSeq = 0;
|
|
@@ -2050,47 +2042,7 @@ function connectOnce(options, deviceId) {
|
|
|
2050
2042
|
socket.setNoDelay(true);
|
|
2051
2043
|
socket.setKeepAlive(true, options.heartbeatMs);
|
|
2052
2044
|
|
|
2053
|
-
socket.
|
|
2054
|
-
writeJsonLine(socket, {
|
|
2055
|
-
type: 'hello',
|
|
2056
|
-
pairToken: options.pair,
|
|
2057
|
-
deviceId,
|
|
2058
|
-
deviceName: options.name,
|
|
2059
|
-
slotNumber: options.slotNumber || undefined,
|
|
2060
|
-
hostname: os.hostname(),
|
|
2061
|
-
platform: os.platform(),
|
|
2062
|
-
arch: os.arch(),
|
|
2063
|
-
pid: process.pid,
|
|
2064
|
-
agentVersion: AGENT_VERSION,
|
|
2065
|
-
productVersion: PRODUCT_VERSION,
|
|
2066
|
-
capabilities: {
|
|
2067
|
-
status: true,
|
|
2068
|
-
thumbnail: options.thumbnailEnabled,
|
|
2069
|
-
liveStream: options.liveEnabled,
|
|
2070
|
-
monitorSelection: true,
|
|
2071
|
-
screenCount: 1,
|
|
2072
|
-
monitorCount: 1,
|
|
2073
|
-
control: false,
|
|
2074
|
-
audio: false,
|
|
2075
|
-
remoteAudio: false,
|
|
2076
|
-
fileTransfer: true,
|
|
2077
|
-
remoteFiles: true,
|
|
2078
|
-
fileTransferMaxBytes: MAX_FILE_TRANSFER_BYTES,
|
|
2079
|
-
computerAgent: options.taskEnabled,
|
|
2080
|
-
taskDispatch: options.taskEnabled,
|
|
2081
|
-
clientUpdate: true,
|
|
2082
|
-
clientUpdateVerifiedProof: true,
|
|
2083
|
-
productVersion: PRODUCT_VERSION,
|
|
2084
|
-
agentApproval: options.taskEnabled,
|
|
2085
|
-
agentAudit: options.taskEnabled,
|
|
2086
|
-
agentTools: [...NODE_AGENT_OPERATIONS],
|
|
2087
|
-
elevation: typeof process.getuid === 'function' ? process.getuid() === 0 : false,
|
|
2088
|
-
externalEffects: options.taskEnabled
|
|
2089
|
-
}
|
|
2090
|
-
});
|
|
2091
|
-
});
|
|
2092
|
-
|
|
2093
|
-
socket.on('data', chunk => {
|
|
2045
|
+
socket.on('data', chunk => {
|
|
2094
2046
|
buffer += chunk;
|
|
2095
2047
|
let newlineIndex = buffer.indexOf('\n');
|
|
2096
2048
|
while (newlineIndex >= 0) {
|
|
@@ -2157,10 +2109,47 @@ function connectOnce(options, deviceId) {
|
|
|
2157
2109
|
}
|
|
2158
2110
|
});
|
|
2159
2111
|
|
|
2160
|
-
socket.once('error', err => finish(err));
|
|
2161
|
-
socket.once('close', () => finish());
|
|
2162
|
-
|
|
2163
|
-
|
|
2112
|
+
socket.once('error', err => finish(err));
|
|
2113
|
+
socket.once('close', () => finish());
|
|
2114
|
+
writeJsonLine(socket, {
|
|
2115
|
+
type: 'hello',
|
|
2116
|
+
pairToken: options.pair,
|
|
2117
|
+
deviceId,
|
|
2118
|
+
deviceName: options.name,
|
|
2119
|
+
slotNumber: options.slotNumber || undefined,
|
|
2120
|
+
hostname: os.hostname(),
|
|
2121
|
+
platform: os.platform(),
|
|
2122
|
+
arch: os.arch(),
|
|
2123
|
+
pid: process.pid,
|
|
2124
|
+
agentVersion: AGENT_VERSION,
|
|
2125
|
+
productVersion: PRODUCT_VERSION,
|
|
2126
|
+
capabilities: {
|
|
2127
|
+
status: true,
|
|
2128
|
+
thumbnail: options.thumbnailEnabled,
|
|
2129
|
+
liveStream: options.liveEnabled,
|
|
2130
|
+
monitorSelection: true,
|
|
2131
|
+
screenCount: 1,
|
|
2132
|
+
monitorCount: 1,
|
|
2133
|
+
control: false,
|
|
2134
|
+
audio: false,
|
|
2135
|
+
remoteAudio: false,
|
|
2136
|
+
fileTransfer: true,
|
|
2137
|
+
remoteFiles: true,
|
|
2138
|
+
fileTransferMaxBytes: MAX_FILE_TRANSFER_BYTES,
|
|
2139
|
+
computerAgent: options.taskEnabled,
|
|
2140
|
+
taskDispatch: options.taskEnabled,
|
|
2141
|
+
clientUpdate: true,
|
|
2142
|
+
clientUpdateVerifiedProof: true,
|
|
2143
|
+
productVersion: PRODUCT_VERSION,
|
|
2144
|
+
agentApproval: options.taskEnabled,
|
|
2145
|
+
agentAudit: options.taskEnabled,
|
|
2146
|
+
agentTools: [...NODE_AGENT_OPERATIONS],
|
|
2147
|
+
elevation: typeof process.getuid === 'function' ? process.getuid() === 0 : false,
|
|
2148
|
+
externalEffects: options.taskEnabled
|
|
2149
|
+
}
|
|
2150
|
+
});
|
|
2151
|
+
});
|
|
2152
|
+
}
|
|
2164
2153
|
|
|
2165
2154
|
async function connectWithRetry(options) {
|
|
2166
2155
|
if (!options.pair) {
|
|
@@ -25,8 +25,9 @@ import {
|
|
|
25
25
|
inspectLinuxVideoAcceleration,
|
|
26
26
|
installLinuxVideoAcceleration
|
|
27
27
|
} from '../src/runtime/linux-video-acceleration.js';
|
|
28
|
-
import { normalizeRuntimeAuthSession } from '../../runtime-core/src/auth-session.js';
|
|
29
|
-
import { startRoleTransitionSupervisor } from '../../runtime-core/src/role-transition-supervisor.js';
|
|
28
|
+
import { normalizeRuntimeAuthSession } from '../../runtime-core/src/auth-session.js';
|
|
29
|
+
import { startRoleTransitionSupervisor } from '../../runtime-core/src/role-transition-supervisor.js';
|
|
30
|
+
import { connectSecureDirectSocket } from '../../runtime-core/src/direct-secure-transport.js';
|
|
30
31
|
|
|
31
32
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
32
33
|
const require = createRequire(import.meta.url);
|
|
@@ -3674,7 +3675,7 @@ export function shouldSkipAutomaticDirectProbe(endpoint, options = {}) {
|
|
|
3674
3675
|
&& !isEndpointOnLocalNetwork(endpoint, options.networkInterfaces);
|
|
3675
3676
|
}
|
|
3676
3677
|
|
|
3677
|
-
function requestHubSlotAssignment({ manager, pairToken, deviceId, slotNumber, timeoutMs = 5000 }) {
|
|
3678
|
+
async function requestHubSlotAssignment({ manager, pairToken, deviceId, slotNumber, timeoutMs = 5000 }) {
|
|
3678
3679
|
const endpoint = parseManagerEndpoint(manager);
|
|
3679
3680
|
const normalizedPairToken = String(pairToken || '').trim();
|
|
3680
3681
|
const normalizedDeviceId = String(deviceId || '').trim();
|
|
@@ -3692,9 +3693,19 @@ function requestHubSlotAssignment({ manager, pairToken, deviceId, slotNumber, ti
|
|
|
3692
3693
|
return Promise.resolve({ ok: false, error: 'invalid-slot-number' });
|
|
3693
3694
|
}
|
|
3694
3695
|
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3696
|
+
let socket;
|
|
3697
|
+
try {
|
|
3698
|
+
socket = await connectSecureDirectSocket(
|
|
3699
|
+
endpoint,
|
|
3700
|
+
normalizedPairToken,
|
|
3701
|
+
'control',
|
|
3702
|
+
{ connectTimeoutMs: timeoutMs, handshakeTimeoutMs: timeoutMs });
|
|
3703
|
+
} catch (error) {
|
|
3704
|
+
return { ok: false, error: error?.message || 'hub-slot-secure-connection-failed' };
|
|
3705
|
+
}
|
|
3706
|
+
|
|
3707
|
+
return new Promise(resolveAssignment => {
|
|
3708
|
+
let settled = false;
|
|
3698
3709
|
let buffer = '';
|
|
3699
3710
|
const settle = result => {
|
|
3700
3711
|
if (settled) return;
|
|
@@ -3705,15 +3716,7 @@ function requestHubSlotAssignment({ manager, pairToken, deviceId, slotNumber, ti
|
|
|
3705
3716
|
};
|
|
3706
3717
|
socket.setEncoding('utf8');
|
|
3707
3718
|
socket.setTimeout(timeoutMs);
|
|
3708
|
-
socket.
|
|
3709
|
-
socket.write(`${JSON.stringify({
|
|
3710
|
-
type: 'slot.assign',
|
|
3711
|
-
pairToken: normalizedPairToken,
|
|
3712
|
-
deviceId: normalizedDeviceId,
|
|
3713
|
-
slotNumber: Number(normalizedSlot)
|
|
3714
|
-
})}\n`);
|
|
3715
|
-
});
|
|
3716
|
-
socket.on('data', chunk => {
|
|
3719
|
+
socket.on('data', chunk => {
|
|
3717
3720
|
buffer += chunk;
|
|
3718
3721
|
const newlineIndex = buffer.indexOf('\n');
|
|
3719
3722
|
if (newlineIndex < 0) return;
|
|
@@ -3730,11 +3733,17 @@ function requestHubSlotAssignment({ manager, pairToken, deviceId, slotNumber, ti
|
|
|
3730
3733
|
});
|
|
3731
3734
|
socket.once('timeout', () => settle({ ok: false, error: 'hub-slot-request-timeout' }));
|
|
3732
3735
|
socket.once('error', error => settle({ ok: false, error: error?.message || 'hub-slot-request-failed' }));
|
|
3733
|
-
socket.once('close', () => {
|
|
3734
|
-
if (!settled) settle({ ok: false, error: 'hub-slot-response-missing' });
|
|
3735
|
-
});
|
|
3736
|
-
|
|
3737
|
-
|
|
3736
|
+
socket.once('close', () => {
|
|
3737
|
+
if (!settled) settle({ ok: false, error: 'hub-slot-response-missing' });
|
|
3738
|
+
});
|
|
3739
|
+
socket.write(`${JSON.stringify({
|
|
3740
|
+
type: 'slot.assign',
|
|
3741
|
+
pairToken: normalizedPairToken,
|
|
3742
|
+
deviceId: normalizedDeviceId,
|
|
3743
|
+
slotNumber: Number(normalizedSlot)
|
|
3744
|
+
})}\n`);
|
|
3745
|
+
});
|
|
3746
|
+
}
|
|
3738
3747
|
|
|
3739
3748
|
export function canConnectToEndpoint(endpoint, timeoutMs = ENDPOINT_PROBE_TIMEOUT_MS) {
|
|
3740
3749
|
const parsedEndpoint = parseManagerEndpoint(endpoint);
|
package/client/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livedesk/client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.264",
|
|
4
4
|
"description": "VuvoDesk local remote client",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -36,16 +36,16 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
|
38
38
|
"ffmpeg-static": "^5.3.0",
|
|
39
|
-
"@livedesk/runtime-core": "0.1.
|
|
39
|
+
"@livedesk/runtime-core": "0.1.7",
|
|
40
40
|
"@supabase/supabase-js": "^2.110.0",
|
|
41
41
|
"node-screenshots": "^0.2.8",
|
|
42
42
|
"ws": "^8.18.3"
|
|
43
43
|
},
|
|
44
44
|
"optionalDependencies": {
|
|
45
|
-
"@livedesk/fast-linux-x64": "0.1.
|
|
46
|
-
"@livedesk/fast-osx-arm64": "0.1.
|
|
47
|
-
"@livedesk/fast-osx-x64": "0.1.
|
|
48
|
-
"@livedesk/fast-win-x64": "0.1.
|
|
45
|
+
"@livedesk/fast-linux-x64": "0.1.460",
|
|
46
|
+
"@livedesk/fast-osx-arm64": "0.1.460",
|
|
47
|
+
"@livedesk/fast-osx-x64": "0.1.460",
|
|
48
|
+
"@livedesk/fast-win-x64": "0.1.460"
|
|
49
49
|
},
|
|
50
50
|
"publishConfig": {
|
|
51
51
|
"access": "public"
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
extends: file:packages/livedesk/electron/electron-builder.linux-x64.yml
|
|
2
|
+
artifactName: VuvoDesk-${version}-dev-${arch}.${ext}
|
|
3
|
+
directories:
|
|
4
|
+
output: dist/desktop-dev
|
|
5
|
+
extraMetadata:
|
|
6
|
+
desktopUpdateChannel: dev
|
|
7
|
+
publish:
|
|
8
|
+
provider: generic
|
|
9
|
+
url: https://livedesk-desktop-updates.lovecrdm.workers.dev/dev/linux/x64
|
|
@@ -23,14 +23,20 @@ asarUnpack:
|
|
|
23
23
|
- hub/**
|
|
24
24
|
- runtime-core/**
|
|
25
25
|
- web/**
|
|
26
|
-
linux:
|
|
27
|
-
icon: packages/livedesk/electron/icon.png
|
|
28
|
-
category: Utility
|
|
29
|
-
executableName: livedesk
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
26
|
+
linux:
|
|
27
|
+
icon: packages/livedesk/electron/icon.png
|
|
28
|
+
category: Utility
|
|
29
|
+
executableName: livedesk
|
|
30
|
+
maintainer: "lovecrdm <lovecrdm@gmail.com>"
|
|
31
|
+
target:
|
|
32
|
+
- target: AppImage
|
|
33
|
+
arch:
|
|
34
|
+
- x64
|
|
35
|
+
- target: deb
|
|
36
|
+
arch:
|
|
37
|
+
- x64
|
|
38
|
+
extraMetadata:
|
|
39
|
+
homepage: https://vuvodesk.com
|
|
40
|
+
publish:
|
|
41
|
+
provider: generic
|
|
42
|
+
url: https://livedesk-desktop-updates.lovecrdm.workers.dev/stable/linux/x64
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
extends: file:packages/livedesk/electron/electron-builder.mac-arm64.yml
|
|
2
|
+
artifactName: VuvoDesk-${version}-dev-${arch}.${ext}
|
|
3
|
+
directories:
|
|
4
|
+
output: dist/desktop-dev
|
|
5
|
+
extraMetadata:
|
|
6
|
+
desktopUpdateChannel: dev
|
|
7
|
+
mac:
|
|
8
|
+
identity: "-"
|
|
9
|
+
hardenedRuntime: false
|
|
10
|
+
notarize: false
|
|
11
|
+
publish:
|
|
12
|
+
provider: generic
|
|
13
|
+
url: https://livedesk-desktop-updates.lovecrdm.workers.dev/dev/macos/arm64
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
extends: file:packages/livedesk/electron/electron-builder.mac-x64.yml
|
|
2
|
+
artifactName: VuvoDesk-${version}-dev-${arch}.${ext}
|
|
3
|
+
directories:
|
|
4
|
+
output: dist/desktop-dev
|
|
5
|
+
extraMetadata:
|
|
6
|
+
desktopUpdateChannel: dev
|
|
7
|
+
mac:
|
|
8
|
+
identity: "-"
|
|
9
|
+
hardenedRuntime: false
|
|
10
|
+
notarize: false
|
|
11
|
+
publish:
|
|
12
|
+
provider: generic
|
|
13
|
+
url: https://livedesk-desktop-updates.lovecrdm.workers.dev/dev/macos/x64
|
package/hub/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",
|
package/hub/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,
|
|
@@ -2397,9 +2404,12 @@ export function createRemoteHub(options = {}) {
|
|
|
2397
2404
|
const hostInstanceId = safeString(options.hostInstanceId ?? env.LIVEDESK_HUB_INSTANCE_ID ?? env.MINDEXEC_BRIDGE_INSTANCE_ID ?? crypto.randomUUID(), 128) || crypto.randomUUID();
|
|
2398
2405
|
const publicEndpoint = safeString(env.LIVEDESK_REMOTE_PUBLIC_ENDPOINT || env.MINDEXEC_REMOTE_PUBLIC_ENDPOINT || env.REMOTE_HUB_PUBLIC_ENDPOINT, 256);
|
|
2399
2406
|
const publicHost = safeString(env.LIVEDESK_REMOTE_PUBLIC_HOST || env.MINDEXEC_REMOTE_PUBLIC_HOST || env.REMOTE_HUB_PUBLIC_HOST, 128);
|
|
2400
|
-
const pairToken = safeString(
|
|
2401
|
-
options.pairToken || env.REMOTE_HUB_PAIR_TOKEN || env.LIVEDESK_CLIENT_PAIR_TOKEN || env.MINDEXEC_REMOTE_PAIR_TOKEN || crypto.randomBytes(
|
|
2402
|
-
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);
|
|
2403
2413
|
const relayControl = options.relayControl && typeof options.relayControl === 'object'
|
|
2404
2414
|
? options.relayControl
|
|
2405
2415
|
: createHubRelayControl({
|
|
@@ -7949,10 +7959,24 @@ export function createRemoteHub(options = {}) {
|
|
|
7949
7959
|
function handleAgentMessage(socket, state, message) {
|
|
7950
7960
|
if (!message || typeof message !== 'object') {
|
|
7951
7961
|
return;
|
|
7952
|
-
}
|
|
7953
|
-
|
|
7954
|
-
if (!state.authenticated) {
|
|
7955
|
-
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) {
|
|
7956
7980
|
message = {
|
|
7957
7981
|
...message,
|
|
7958
7982
|
capabilities: {
|
|
@@ -7988,7 +8012,7 @@ export function createRemoteHub(options = {}) {
|
|
|
7988
8012
|
return;
|
|
7989
8013
|
}
|
|
7990
8014
|
|
|
7991
|
-
const channel = safeString(message.channel || message.Channel, 40).toLowerCase();
|
|
8015
|
+
const channel = safeString(message.channel || message.Channel, 40).toLowerCase() || 'control';
|
|
7992
8016
|
if (channel === 'input') {
|
|
7993
8017
|
const device = attachInputSocket(socket, message);
|
|
7994
8018
|
if (!device) {
|
|
@@ -8538,18 +8562,60 @@ export function createRemoteHub(options = {}) {
|
|
|
8538
8562
|
if (firstChunk) {
|
|
8539
8563
|
processData(firstChunk);
|
|
8540
8564
|
}
|
|
8541
|
-
}
|
|
8542
|
-
|
|
8543
|
-
function handleSocket(socket) {
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
if (
|
|
8547
|
-
|
|
8548
|
-
|
|
8549
|
-
|
|
8550
|
-
|
|
8551
|
-
|
|
8552
|
-
});
|
|
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
|
+
});
|
|
8553
8619
|
|
|
8554
8620
|
socket.once('error', () => {
|
|
8555
8621
|
// The transport-specific handler owns logging after the first byte.
|
|
@@ -8571,10 +8637,10 @@ export function createRemoteHub(options = {}) {
|
|
|
8571
8637
|
started = true;
|
|
8572
8638
|
boundPort = candidateServer.address()?.port || port;
|
|
8573
8639
|
lastError = '';
|
|
8574
|
-
logEvent('remote', `VuvoDesk Hub client endpoint listening on tcp://${host}:${boundPort}`, 'success');
|
|
8575
|
-
if (host === '0.0.0.0' || host === '::') {
|
|
8576
|
-
logWarn('remote', 'VuvoDesk Hub client endpoint is externally reachable.
|
|
8577
|
-
}
|
|
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
|
+
}
|
|
8578
8644
|
emitRemoteEvent('RemoteHubStarted', null);
|
|
8579
8645
|
resolve();
|
|
8580
8646
|
});
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "livedesk",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"livedeskClientVersion": "0.1.
|
|
3
|
+
"version": "0.1.615",
|
|
4
|
+
"livedeskClientVersion": "0.1.264",
|
|
5
5
|
"buildFlavor": "production",
|
|
6
6
|
"description": "VuvoDesk Hub and client launcher",
|
|
7
7
|
"type": "module",
|
|
@@ -51,10 +51,10 @@
|
|
|
51
51
|
"ws": "^8.18.3"
|
|
52
52
|
},
|
|
53
53
|
"optionalDependencies": {
|
|
54
|
-
"@livedesk/fast-linux-x64": "0.1.
|
|
55
|
-
"@livedesk/fast-osx-arm64": "0.1.
|
|
56
|
-
"@livedesk/fast-osx-x64": "0.1.
|
|
57
|
-
"@livedesk/fast-win-x64": "0.1.
|
|
54
|
+
"@livedesk/fast-linux-x64": "0.1.460",
|
|
55
|
+
"@livedesk/fast-osx-arm64": "0.1.460",
|
|
56
|
+
"@livedesk/fast-osx-x64": "0.1.460",
|
|
57
|
+
"@livedesk/fast-win-x64": "0.1.460"
|
|
58
58
|
},
|
|
59
59
|
"publishConfig": {
|
|
60
60
|
"access": "public"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livedesk/runtime-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Shared VuvoDesk runtime role and lifecycle contracts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"src/"
|
|
9
9
|
],
|
|
10
10
|
"scripts": {
|
|
11
|
-
"check": "node --check src/index.js",
|
|
11
|
+
"check": "node --check src/index.js && node --check src/direct-secure-transport.js",
|
|
12
12
|
"prepublishOnly": "node ../../scripts/livedesk-release-git-gate.mjs"
|
|
13
13
|
},
|
|
14
14
|
"engines": {
|