@livedesk/client 0.1.250 → 0.1.252
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/README.md +10 -10
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/bin/client-version.js +1 -1
- package/bin/livedesk-client-node.js +91 -37
- package/bin/livedesk-client-update-bootstrap.cjs +12 -12
- package/bin/livedesk-client.js +136 -122
- package/package.json +7 -7
- package/src/runtime/agent-process-lifecycle.js +10 -10
- package/src/runtime/agent-shell.js +66 -0
- package/src/runtime/client-runtime-server.js +12 -11
- package/src/runtime/linux-video-acceleration.js +1 -1
- package/src/runtime/windows-owned-process-manifest.js +9 -9
- package/tests/client-version.test.mjs +2 -2
package/bin/livedesk-client.js
CHANGED
|
@@ -54,9 +54,10 @@ const SESSION_REFRESH_SKEW_SECONDS = 60;
|
|
|
54
54
|
const SUPABASE_REQUEST_TIMEOUT_MS = 8_000;
|
|
55
55
|
const HUB_TARGET_CACHE_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1000;
|
|
56
56
|
const HUB_TARGET_CACHE_FUTURE_SKEW_MS = 5 * 60 * 1000;
|
|
57
|
-
const WINDOWS_STARTUP_SCRIPT_NAME = '
|
|
58
|
-
const
|
|
59
|
-
const
|
|
57
|
+
const WINDOWS_STARTUP_SCRIPT_NAME = 'VuvoDesk Desktop.vbs';
|
|
58
|
+
const WINDOWS_STARTUP_LEGACY_DESKTOP_SCRIPT_NAME = 'LiveDesk Desktop.vbs';
|
|
59
|
+
const WINDOWS_STARTUP_LEGACY_SCRIPT_NAME = 'LiveDesk Client.vbs';
|
|
60
|
+
const WINDOWS_STARTUP_LEGACY_CMD_NAME = 'LiveDesk Client.cmd';
|
|
60
61
|
const SUPABASE_URL = process.env.LIVEDESK_SUPABASE_URL || 'https://otbyfkjxrkngvjziawki.supabase.co';
|
|
61
62
|
const SUPABASE_PUBLISHABLE_KEY = process.env.LIVEDESK_SUPABASE_PUBLISHABLE_KEY || 'sb_publishable_NpUs0RDJH2YnllsqTKO6TQ_1jTdSsNQ';
|
|
62
63
|
const CLIENT_STATE_DIR = process.env.LIVEDESK_CLIENT_STATE_DIR || join(os.homedir(), '.livedesk-client');
|
|
@@ -107,7 +108,7 @@ function readWindowsManifestOwnerFromEnvironment(env = process.env) {
|
|
|
107
108
|
|| !owner.ownerInstanceMarker.startsWith(`${process.pid}:`)
|
|
108
109
|
|| !/^\d+$/.test(owner.ownerStartOrder)) {
|
|
109
110
|
console.warn(
|
|
110
|
-
'[
|
|
111
|
+
'[VuvoDesk Client] Windows process-manifest publication is disabled because '
|
|
111
112
|
+ 'the unified launcher owner tuple is incomplete or does not name this exact process.'
|
|
112
113
|
);
|
|
113
114
|
return null;
|
|
@@ -232,10 +233,10 @@ export function buildUnifiedRoleRestartEnvironment(role, baseEnv = process.env,
|
|
|
232
233
|
const normalizedRole = String(role || '').trim().toLowerCase();
|
|
233
234
|
const waitPid = Number(runtimePid);
|
|
234
235
|
if (normalizedRole !== 'hub' && normalizedRole !== 'client') {
|
|
235
|
-
throw new Error(`Unsupported
|
|
236
|
+
throw new Error(`Unsupported VuvoDesk role restart: ${role}`);
|
|
236
237
|
}
|
|
237
238
|
if (!Number.isInteger(waitPid) || waitPid <= 1) {
|
|
238
|
-
throw new Error(`Invalid
|
|
239
|
+
throw new Error(`Invalid VuvoDesk runtime PID for role restart: ${runtimePid}`);
|
|
239
240
|
}
|
|
240
241
|
return {
|
|
241
242
|
...baseEnv,
|
|
@@ -251,13 +252,13 @@ export function buildUnifiedRoleRestartEnvironment(role, baseEnv = process.env,
|
|
|
251
252
|
|
|
252
253
|
async function spawnUnifiedRoleRestart(role) {
|
|
253
254
|
if (isTruthy(process.env.LIVEDESK_DESKTOP_HOST)) {
|
|
254
|
-
console.log(`[
|
|
255
|
+
console.log(`[VuvoDesk] Desktop supervisor will restart the runtime as ${role}.`);
|
|
255
256
|
return false;
|
|
256
257
|
}
|
|
257
258
|
const unifiedEntry = String(process.env.LIVEDESK_UNIFIED_LAUNCHER_ENTRY || '').trim();
|
|
258
259
|
const restartEnvironment = buildUnifiedRoleRestartEnvironment(role);
|
|
259
260
|
if (!unifiedEntry || !existsSync(unifiedEntry)) {
|
|
260
|
-
throw new Error(`
|
|
261
|
+
throw new Error(`VuvoDesk unified role launcher is unavailable: ${unifiedEntry || 'missing path'}`);
|
|
261
262
|
}
|
|
262
263
|
const handoff = await startRoleTransitionSupervisor({
|
|
263
264
|
role,
|
|
@@ -270,7 +271,7 @@ async function spawnUnifiedRoleRestart(role) {
|
|
|
270
271
|
cwd: process.cwd()
|
|
271
272
|
});
|
|
272
273
|
console.log(
|
|
273
|
-
`[
|
|
274
|
+
`[VuvoDesk] Role transition supervisor claimed target=${role} `
|
|
274
275
|
+ `pid=${handoff.supervisorPid}. Diagnostics: ${handoff.logPath}`
|
|
275
276
|
);
|
|
276
277
|
return true;
|
|
@@ -278,18 +279,18 @@ async function spawnUnifiedRoleRestart(role) {
|
|
|
278
279
|
|
|
279
280
|
function printHelp() {
|
|
280
281
|
process.stdout.write(`
|
|
281
|
-
|
|
282
|
+
VuvoDesk Client
|
|
282
283
|
|
|
283
284
|
Usage:
|
|
284
285
|
npx -y --prefer-online livedesk@latest client
|
|
285
286
|
npx -y --prefer-online livedesk@latest client 3
|
|
286
287
|
|
|
287
288
|
Default flow:
|
|
288
|
-
Opens a
|
|
289
|
+
Opens a VuvoDesk connection page with Google sign-in or a 6-digit PIN.
|
|
289
290
|
After auth, that page stays open as this computer's client dashboard.
|
|
290
291
|
Saved Google sign-in or saved PIN starts the client automatically.
|
|
291
292
|
Omit the number for first-available placement, or pass 1-999 to pin a slot.
|
|
292
|
-
If no
|
|
293
|
+
If no VuvoDesk Hub is active yet, the client waits for a Hub-online event and
|
|
293
294
|
uses adaptive registry retries as a fallback.
|
|
294
295
|
|
|
295
296
|
Options:
|
|
@@ -356,7 +357,7 @@ function normalizeClientTransport(value) {
|
|
|
356
357
|
export function parseClientTransportOption(value) {
|
|
357
358
|
const transport = String(value || '').trim().toLowerCase();
|
|
358
359
|
if (!['auto', 'tcp', 'legacy', 'ws', 'wss', 'udp-p2p', 'udp-relay'].includes(transport)) {
|
|
359
|
-
throw new Error(`Unsupported
|
|
360
|
+
throw new Error(`Unsupported VuvoDesk Client transport: ${value || '(missing)'}`);
|
|
360
361
|
}
|
|
361
362
|
return normalizeClientTransport(transport);
|
|
362
363
|
}
|
|
@@ -515,7 +516,7 @@ export function parseLauncherArgs(argv) {
|
|
|
515
516
|
if (arg === '--relay') {
|
|
516
517
|
const candidate = String(argv[index + 1] || '').trim().replace(/^tcp:\/\//i, '');
|
|
517
518
|
if (!parseManagerEndpoint(candidate)) {
|
|
518
|
-
throw new Error(`Invalid
|
|
519
|
+
throw new Error(`Invalid VuvoDesk relay endpoint: ${argv[index + 1] || '(missing)'}`);
|
|
519
520
|
}
|
|
520
521
|
relay = candidate;
|
|
521
522
|
relayExplicit = true;
|
|
@@ -656,8 +657,8 @@ function hubClientPortPreflightError(preflight) {
|
|
|
656
657
|
error: recoveryError
|
|
657
658
|
? `${recoveryError} The current Hub was not changed.`
|
|
658
659
|
: preflight?.code === 'hub-client-port-in-use'
|
|
659
|
-
? `
|
|
660
|
-
: `
|
|
660
|
+
? `VuvoDesk cannot switch this computer to Hub because TCP ${port} is already in use. Change or stop the owning service, then try Switch to Hub again. The current Hub was not changed.`
|
|
661
|
+
: `VuvoDesk cannot switch this computer to Hub because TCP ${port} is unavailable. Check the local port configuration, then try again. The current Hub was not changed.`
|
|
661
662
|
};
|
|
662
663
|
}
|
|
663
664
|
|
|
@@ -737,11 +738,15 @@ function getWindowsStartupScriptPath() {
|
|
|
737
738
|
return join(getWindowsStartupDir(), WINDOWS_STARTUP_SCRIPT_NAME);
|
|
738
739
|
}
|
|
739
740
|
|
|
740
|
-
function getLegacyWindowsStartupCommandPath() {
|
|
741
|
-
return join(getWindowsStartupDir(), WINDOWS_STARTUP_LEGACY_CMD_NAME);
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
function
|
|
741
|
+
function getLegacyWindowsStartupCommandPath() {
|
|
742
|
+
return join(getWindowsStartupDir(), WINDOWS_STARTUP_LEGACY_CMD_NAME);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
function getLegacyWindowsDesktopStartupScriptPath() {
|
|
746
|
+
return join(getWindowsStartupDir(), WINDOWS_STARTUP_LEGACY_DESKTOP_SCRIPT_NAME);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
function getLegacyWindowsStartupScriptPath() {
|
|
745
750
|
return join(getWindowsStartupDir(), WINDOWS_STARTUP_LEGACY_SCRIPT_NAME);
|
|
746
751
|
}
|
|
747
752
|
|
|
@@ -749,8 +754,12 @@ function isWindowsStartupSupported() {
|
|
|
749
754
|
return os.platform() === 'win32';
|
|
750
755
|
}
|
|
751
756
|
|
|
752
|
-
function isWindowsStartupRegistered() {
|
|
753
|
-
return isWindowsStartupSupported() && (
|
|
757
|
+
function isWindowsStartupRegistered() {
|
|
758
|
+
return isWindowsStartupSupported() && (
|
|
759
|
+
existsSync(getWindowsStartupScriptPath())
|
|
760
|
+
|| existsSync(getLegacyWindowsDesktopStartupScriptPath())
|
|
761
|
+
|| existsSync(getLegacyWindowsStartupScriptPath())
|
|
762
|
+
);
|
|
754
763
|
}
|
|
755
764
|
|
|
756
765
|
function quoteCommandArg(value) {
|
|
@@ -822,9 +831,10 @@ function registerWindowsStartup(startupArgs = []) {
|
|
|
822
831
|
''
|
|
823
832
|
].join('\r\n');
|
|
824
833
|
|
|
825
|
-
mkdirSync(startupDir, { recursive: true });
|
|
826
|
-
writeFileSync(scriptPath, script, 'utf8');
|
|
827
|
-
rmSync(
|
|
834
|
+
mkdirSync(startupDir, { recursive: true });
|
|
835
|
+
writeFileSync(scriptPath, script, 'utf8');
|
|
836
|
+
rmSync(getLegacyWindowsDesktopStartupScriptPath(), { force: true });
|
|
837
|
+
rmSync(getLegacyWindowsStartupScriptPath(), { force: true });
|
|
828
838
|
rmSync(getLegacyWindowsStartupCommandPath(), { force: true });
|
|
829
839
|
return { changed: true, supported: true, path: scriptPath };
|
|
830
840
|
}
|
|
@@ -834,8 +844,12 @@ function unregisterWindowsStartup() {
|
|
|
834
844
|
return { changed: false, supported: false, path: '' };
|
|
835
845
|
}
|
|
836
846
|
const scriptPath = getWindowsStartupScriptPath();
|
|
837
|
-
const existed = existsSync(scriptPath)
|
|
838
|
-
|
|
847
|
+
const existed = existsSync(scriptPath)
|
|
848
|
+
|| existsSync(getLegacyWindowsDesktopStartupScriptPath())
|
|
849
|
+
|| existsSync(getLegacyWindowsStartupScriptPath())
|
|
850
|
+
|| existsSync(getLegacyWindowsStartupCommandPath());
|
|
851
|
+
rmSync(scriptPath, { force: true });
|
|
852
|
+
rmSync(getLegacyWindowsDesktopStartupScriptPath(), { force: true });
|
|
839
853
|
rmSync(getLegacyWindowsStartupScriptPath(), { force: true });
|
|
840
854
|
rmSync(getLegacyWindowsStartupCommandPath(), { force: true });
|
|
841
855
|
return { changed: existed, supported: true, path: scriptPath };
|
|
@@ -1461,7 +1475,7 @@ function renderOAuthCallbackPage({ title, message, tone = 'neutral' }) {
|
|
|
1461
1475
|
<div class="mark">LD</div>
|
|
1462
1476
|
<h1>${escapeHtml(title)}</h1>
|
|
1463
1477
|
<p>${escapeHtml(message)}</p>
|
|
1464
|
-
<small>
|
|
1478
|
+
<small>VuvoDesk Client</small>
|
|
1465
1479
|
</main>
|
|
1466
1480
|
</body>
|
|
1467
1481
|
</html>`;
|
|
@@ -1491,7 +1505,7 @@ function renderConnectionChoicePage({ autoGoogle = false, error = '', pin = '',
|
|
|
1491
1505
|
<head>
|
|
1492
1506
|
<meta charset="utf-8">
|
|
1493
1507
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
1494
|
-
<title>Connect
|
|
1508
|
+
<title>Connect VuvoDesk</title>
|
|
1495
1509
|
<style>
|
|
1496
1510
|
:root { color-scheme: light; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
|
|
1497
1511
|
* { box-sizing: border-box; }
|
|
@@ -1798,7 +1812,7 @@ function renderConnectionChoicePage({ autoGoogle = false, error = '', pin = '',
|
|
|
1798
1812
|
<div class="topbar">
|
|
1799
1813
|
<div class="mark">LD</div>
|
|
1800
1814
|
<div class="brand-copy">
|
|
1801
|
-
<strong>
|
|
1815
|
+
<strong>VuvoDesk</strong>
|
|
1802
1816
|
<span>Client connect</span>
|
|
1803
1817
|
</div>
|
|
1804
1818
|
</div>
|
|
@@ -1843,7 +1857,7 @@ function renderConnectionChoicePage({ autoGoogle = false, error = '', pin = '',
|
|
|
1843
1857
|
<div class="screen"></div>
|
|
1844
1858
|
<div class="screen"></div>
|
|
1845
1859
|
</div>
|
|
1846
|
-
<div class="caption">
|
|
1860
|
+
<div class="caption">VuvoDesk places this computer on the Hub wall after sign-in or PIN approval.</div>
|
|
1847
1861
|
</div>
|
|
1848
1862
|
</aside>
|
|
1849
1863
|
</div>
|
|
@@ -1968,20 +1982,20 @@ function getClientRouteMeta(route, state = {}) {
|
|
|
1968
1982
|
case '/sync-server':
|
|
1969
1983
|
return {
|
|
1970
1984
|
title: 'Sync Server',
|
|
1971
|
-
body: 'Use Sync Server to turn this Client computer into the
|
|
1985
|
+
body: 'Use Sync Server to turn this Client computer into the VuvoDesk Hub.',
|
|
1972
1986
|
rows: [['Current role', 'Client'], ['Assigned Hub', manager], ['Connection', manager === 'Not connected' ? 'Waiting' : 'Connected'], ['Screen streaming', 'Ready'], ['Remote control', 'Ready'], ['File sharing', 'Enabled']]
|
|
1973
1987
|
};
|
|
1974
1988
|
case '/permissions':
|
|
1975
1989
|
return {
|
|
1976
1990
|
title: 'Permissions',
|
|
1977
|
-
body: '
|
|
1978
|
-
rows: [['Screen capture', 'Checked by RemoteFast'], ['Remote input', 'Checked by RemoteFast'], ['System audio', 'Checked when requested'], ['Files', 'Desktop/
|
|
1991
|
+
body: 'VuvoDesk checks platform permissions without repeatedly opening system dialogs.',
|
|
1992
|
+
rows: [['Screen capture', 'Checked by RemoteFast'], ['Remote input', 'Checked by RemoteFast'], ['System audio', 'Checked when requested'], ['Files', 'Desktop/VuvoDeskFiles'], ['Firewall', 'Outbound client connection']]
|
|
1979
1993
|
};
|
|
1980
1994
|
case '/shared-files':
|
|
1981
1995
|
return {
|
|
1982
1996
|
title: 'Shared Files',
|
|
1983
|
-
body: 'Files received from the Hub are stored in the configured
|
|
1984
|
-
rows: [['Default folder', 'Desktop/
|
|
1997
|
+
body: 'Files received from the Hub are stored in the configured VuvoDesk folder.',
|
|
1998
|
+
rows: [['Default folder', 'Desktop/VuvoDeskFiles'], ['Transfer mode', 'Chunked and resumable'], ['Status', 'Available when connected']]
|
|
1985
1999
|
};
|
|
1986
2000
|
case '/diagnostics':
|
|
1987
2001
|
return {
|
|
@@ -1992,14 +2006,14 @@ function getClientRouteMeta(route, state = {}) {
|
|
|
1992
2006
|
case '/settings':
|
|
1993
2007
|
return {
|
|
1994
2008
|
title: 'Settings',
|
|
1995
|
-
body: 'Client preferences and role state are managed by the unified
|
|
2009
|
+
body: 'Client preferences and role state are managed by the unified VuvoDesk launcher.',
|
|
1996
2010
|
rows: [['Role', 'Client'], ['Startup', state.startup ? 'Enabled' : 'Manual'], ['Device ID', state.deviceId || 'Not assigned'], ['Update', 'Use livedesk@latest'], ['Logout', 'Clears the saved session']]
|
|
1997
2011
|
};
|
|
1998
2012
|
default:
|
|
1999
2013
|
return {
|
|
2000
2014
|
title: 'This Computer',
|
|
2001
|
-
body: 'Your computer is connected to
|
|
2002
|
-
rows: [['Device', state.deviceId || 'Not assigned'], ['Operating system', state.system?.os || state.system?.platform || '-'], ['
|
|
2015
|
+
body: 'Your computer is connected to VuvoDesk and ready to provide screen, control, audio, and file capabilities.',
|
|
2016
|
+
rows: [['Device', state.deviceId || 'Not assigned'], ['Operating system', state.system?.os || state.system?.platform || '-'], ['VuvoDesk', state.appVersion || state.system?.packageVersion || '-'], ['Current Hub', manager]]
|
|
2003
2017
|
};
|
|
2004
2018
|
}
|
|
2005
2019
|
}
|
|
@@ -2120,8 +2134,8 @@ function renderConnectionDashboardPage(state = {}) {
|
|
|
2120
2134
|
? agentState === 'running' ? 'Agent running' : 'Client starting'
|
|
2121
2135
|
: 'Finding Hub';
|
|
2122
2136
|
const message = state.message || (manager
|
|
2123
|
-
? 'The
|
|
2124
|
-
: 'Sign-in is complete. The client is looking for the active
|
|
2137
|
+
? 'The VuvoDesk client is running from the terminal. Keep this tab open as the local client dashboard.'
|
|
2138
|
+
: 'Sign-in is complete. The client is looking for the active VuvoDesk Hub and will start automatically.');
|
|
2125
2139
|
const stateJson = JSON.stringify({
|
|
2126
2140
|
authLabel,
|
|
2127
2141
|
connectedAt,
|
|
@@ -2142,7 +2156,7 @@ function renderConnectionDashboardPage(state = {}) {
|
|
|
2142
2156
|
<head>
|
|
2143
2157
|
<meta charset="utf-8">
|
|
2144
2158
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
2145
|
-
<title>
|
|
2159
|
+
<title>VuvoDesk Client Dashboard</title>
|
|
2146
2160
|
<style>
|
|
2147
2161
|
:root { color-scheme: light; font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; }
|
|
2148
2162
|
* { box-sizing: border-box; }
|
|
@@ -2476,7 +2490,7 @@ function renderConnectionDashboardPage(state = {}) {
|
|
|
2476
2490
|
<div class="brand">
|
|
2477
2491
|
<div class="mark">LD</div>
|
|
2478
2492
|
<div class="brand-copy">
|
|
2479
|
-
<strong>
|
|
2493
|
+
<strong>VuvoDesk</strong>
|
|
2480
2494
|
<span>Client dashboard</span>
|
|
2481
2495
|
</div>
|
|
2482
2496
|
</div>
|
|
@@ -2487,7 +2501,7 @@ function renderConnectionDashboardPage(state = {}) {
|
|
|
2487
2501
|
</div>
|
|
2488
2502
|
<div class="muted-card role-action-card">
|
|
2489
2503
|
<strong>Make this computer the Hub</strong>
|
|
2490
|
-
<span>Sync Server stops the Client runtime, registers this computer as the Hub, and restarts
|
|
2504
|
+
<span>Sync Server stops the Client runtime, registers this computer as the Hub, and restarts VuvoDesk with the Hub Wall.</span>
|
|
2491
2505
|
<button id="sync-server-button" type="button">Sync Server</button>
|
|
2492
2506
|
<span id="role-action-message" aria-live="polite"></span>
|
|
2493
2507
|
</div>
|
|
@@ -2623,7 +2637,7 @@ function renderConnectionDashboardPage(state = {}) {
|
|
|
2623
2637
|
if (!response.ok || result?.ok === false) {
|
|
2624
2638
|
throw new Error(result?.error || 'Role change failed.');
|
|
2625
2639
|
}
|
|
2626
|
-
if (message) message.textContent = 'Hub role accepted.
|
|
2640
|
+
if (message) message.textContent = 'Hub role accepted. VuvoDesk is restarting with the Hub Wall.';
|
|
2627
2641
|
navigateToRuntimePort(5179);
|
|
2628
2642
|
} catch (error) {
|
|
2629
2643
|
if (message) message.textContent = error?.message || String(error);
|
|
@@ -2777,7 +2791,7 @@ function readRequestBody(req, maxBytes = 4096) {
|
|
|
2777
2791
|
export async function resolveManagerFromPin(supabase, pin, options = {}) {
|
|
2778
2792
|
const normalizedPin = normalizePairingPin(pin);
|
|
2779
2793
|
if (!normalizedPin) {
|
|
2780
|
-
throw new Error('Enter a 6-digit
|
|
2794
|
+
throw new Error('Enter a 6-digit VuvoDesk PIN.');
|
|
2781
2795
|
}
|
|
2782
2796
|
const cacheOwnerKey = hubCacheOwnerForPin(normalizedPin);
|
|
2783
2797
|
const resolved = await resolveManagerWithCachedRecovery(cacheOwnerKey, {
|
|
@@ -2817,7 +2831,7 @@ export async function resolveManagerFromPin(supabase, pin, options = {}) {
|
|
|
2817
2831
|
if (!target) {
|
|
2818
2832
|
throw createHubDiscoveryError(
|
|
2819
2833
|
'pin-direct-unreachable',
|
|
2820
|
-
`No reachable
|
|
2834
|
+
`No reachable VuvoDesk Hub endpoint yet. Checked ${endpointCandidates.join(', ')}.`
|
|
2821
2835
|
);
|
|
2822
2836
|
}
|
|
2823
2837
|
return {
|
|
@@ -2840,7 +2854,7 @@ export async function resolveManagerFromPin(supabase, pin, options = {}) {
|
|
|
2840
2854
|
? 'Fresh PIN registry lookup was unavailable; using the saved PIN-bound identity for the bounded encrypted relay path.'
|
|
2841
2855
|
: resolved.discoverySource === 'cache'
|
|
2842
2856
|
? `Fresh PIN registry lookup was unavailable; using the responding saved direct Hub endpoint ${resolved.manager}.`
|
|
2843
|
-
: `Found current PIN-bound
|
|
2857
|
+
: `Found current PIN-bound VuvoDesk Hub at ${resolved.manager}.`);
|
|
2844
2858
|
return resolved;
|
|
2845
2859
|
}
|
|
2846
2860
|
|
|
@@ -2849,7 +2863,7 @@ async function waitForManagerFromSavedPin(supabase, pin, options = {}) {
|
|
|
2849
2863
|
const shouldStop = typeof options.shouldStop === 'function' ? options.shouldStop : () => false;
|
|
2850
2864
|
let attempts = 0;
|
|
2851
2865
|
let lastMessage = '';
|
|
2852
|
-
console.log('Waiting for a
|
|
2866
|
+
console.log('Waiting for a VuvoDesk Hub from saved PIN. VuvoDesk uses adaptive retries and reacts immediately to local reconnect triggers.');
|
|
2853
2867
|
while (true) {
|
|
2854
2868
|
if (shouldStop()) {
|
|
2855
2869
|
return null;
|
|
@@ -2867,7 +2881,7 @@ async function waitForManagerFromSavedPin(supabase, pin, options = {}) {
|
|
|
2867
2881
|
const message = formatDiscoveryError(err);
|
|
2868
2882
|
if (message !== lastMessage || attempts === 1 || attempts % 6 === 0) {
|
|
2869
2883
|
const suffix = attempts === 1 ? '' : ` attempt ${attempts}`;
|
|
2870
|
-
console.log(`Still waiting for
|
|
2884
|
+
console.log(`Still waiting for VuvoDesk Hub by saved PIN${suffix}: ${message}`);
|
|
2871
2885
|
lastMessage = message;
|
|
2872
2886
|
}
|
|
2873
2887
|
await waitForDiscoveryTrigger(getDiscoveryRetryDelay(attempts, configuredIntervalMs), null, shouldStop);
|
|
@@ -2888,8 +2902,8 @@ async function startConnectionChoiceServer(supabase, options = {}) {
|
|
|
2888
2902
|
let pendingStartup = desktopStartupManaged ? false : isWindowsStartupRegistered();
|
|
2889
2903
|
let listeningPort = port;
|
|
2890
2904
|
let completed = false;
|
|
2891
|
-
let completedTitle = '
|
|
2892
|
-
let completedMessage = 'The
|
|
2905
|
+
let completedTitle = 'VuvoDesk connection complete';
|
|
2906
|
+
let completedMessage = 'The VuvoDesk client is starting automatically.';
|
|
2893
2907
|
const dashboardState = {
|
|
2894
2908
|
deviceId,
|
|
2895
2909
|
appVersion: process.env.LIVEDESK_NPM_LAUNCHER_VERSION || readPackageVersion(),
|
|
@@ -3004,13 +3018,13 @@ async function startConnectionChoiceServer(supabase, options = {}) {
|
|
|
3004
3018
|
}
|
|
3005
3019
|
if (savedSession?.access_token) {
|
|
3006
3020
|
applyStartupPreference(pendingStartup, startupArgs);
|
|
3007
|
-
complete({ type: 'google', session: savedSession }, '
|
|
3021
|
+
complete({ type: 'google', session: savedSession }, 'VuvoDesk client dashboard', 'Saved Google sign-in found. The client is starting automatically.');
|
|
3008
3022
|
return;
|
|
3009
3023
|
}
|
|
3010
3024
|
if (!savedPin) {
|
|
3011
3025
|
return;
|
|
3012
3026
|
}
|
|
3013
|
-
dashboardState.message = 'Saved
|
|
3027
|
+
dashboardState.message = 'Saved VuvoDesk PIN found. Waiting for the Hub to become reachable.';
|
|
3014
3028
|
try {
|
|
3015
3029
|
const resolved = await waitForManagerFromSavedPin(supabase, savedPin, {
|
|
3016
3030
|
allowRelayFallback: options.allowRelayFallback === true,
|
|
@@ -3024,7 +3038,7 @@ async function startConnectionChoiceServer(supabase, options = {}) {
|
|
|
3024
3038
|
return;
|
|
3025
3039
|
}
|
|
3026
3040
|
applyStartupPreference(pendingStartup, startupArgs);
|
|
3027
|
-
complete({ type: 'pin', ...resolved }, '
|
|
3041
|
+
complete({ type: 'pin', ...resolved }, 'VuvoDesk client dashboard', 'Saved PIN accepted. The client is starting automatically.');
|
|
3028
3042
|
} catch (err) {
|
|
3029
3043
|
if (!completed) {
|
|
3030
3044
|
dashboardState.message = `Saved PIN did not connect: ${formatDiscoveryError(err)}`;
|
|
@@ -3211,7 +3225,7 @@ async function startConnectionChoiceServer(supabase, options = {}) {
|
|
|
3211
3225
|
writeSavedSessionToFile(existingSession);
|
|
3212
3226
|
applyStartupPreference(pendingStartup, startupArgs);
|
|
3213
3227
|
const choice = { type: 'google', session: existingSession };
|
|
3214
|
-
complete(choice, '
|
|
3228
|
+
complete(choice, 'VuvoDesk client dashboard', 'Signed in. The client is starting automatically.');
|
|
3215
3229
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
3216
3230
|
res.end(renderDashboard());
|
|
3217
3231
|
return;
|
|
@@ -3256,7 +3270,7 @@ async function startConnectionChoiceServer(supabase, options = {}) {
|
|
|
3256
3270
|
writeSavedPin(pin);
|
|
3257
3271
|
applyStartupPreference(pendingStartup, startupArgs);
|
|
3258
3272
|
const choice = { type: 'pin', ...resolved };
|
|
3259
|
-
complete(choice, '
|
|
3273
|
+
complete(choice, 'VuvoDesk client dashboard', 'PIN accepted. The client is starting automatically.');
|
|
3260
3274
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
3261
3275
|
res.end(renderDashboard());
|
|
3262
3276
|
} catch (err) {
|
|
@@ -3271,7 +3285,7 @@ async function startConnectionChoiceServer(supabase, options = {}) {
|
|
|
3271
3285
|
if (error) {
|
|
3272
3286
|
res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
3273
3287
|
res.end(renderOAuthCallbackPage({
|
|
3274
|
-
title: '
|
|
3288
|
+
title: 'VuvoDesk sign-in failed',
|
|
3275
3289
|
message: error,
|
|
3276
3290
|
tone: 'error'
|
|
3277
3291
|
}));
|
|
@@ -3292,10 +3306,10 @@ async function startConnectionChoiceServer(supabase, options = {}) {
|
|
|
3292
3306
|
}
|
|
3293
3307
|
const { data: sessionData, error: exchangeError } = await supabase.auth.exchangeCodeForSession(code);
|
|
3294
3308
|
if (exchangeError || !sessionData?.session) {
|
|
3295
|
-
const message = exchangeError || new Error('Google sign-in did not return a
|
|
3309
|
+
const message = exchangeError || new Error('Google sign-in did not return a VuvoDesk session.');
|
|
3296
3310
|
res.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
3297
3311
|
res.end(renderOAuthCallbackPage({
|
|
3298
|
-
title: '
|
|
3312
|
+
title: 'VuvoDesk sign-in failed',
|
|
3299
3313
|
message: message instanceof Error ? message.message : String(message),
|
|
3300
3314
|
tone: 'error'
|
|
3301
3315
|
}));
|
|
@@ -3309,7 +3323,7 @@ async function startConnectionChoiceServer(supabase, options = {}) {
|
|
|
3309
3323
|
writeSavedSessionToFile(sessionData.session);
|
|
3310
3324
|
applyStartupPreference(pendingStartup, startupArgs);
|
|
3311
3325
|
const choice = { type: 'google', session: sessionData.session };
|
|
3312
|
-
complete(choice, '
|
|
3326
|
+
complete(choice, 'VuvoDesk client dashboard', 'Signed in. The client is starting automatically.');
|
|
3313
3327
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
3314
3328
|
res.end(renderDashboard());
|
|
3315
3329
|
return;
|
|
@@ -3318,14 +3332,14 @@ async function startConnectionChoiceServer(supabase, options = {}) {
|
|
|
3318
3332
|
res.writeHead(404, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
3319
3333
|
res.end(renderChoice({
|
|
3320
3334
|
autoGoogle: false,
|
|
3321
|
-
error: 'Unknown
|
|
3335
|
+
error: 'Unknown VuvoDesk connection route.'
|
|
3322
3336
|
}));
|
|
3323
3337
|
})().catch(err => {
|
|
3324
3338
|
if (!res.headersSent) {
|
|
3325
3339
|
res.writeHead(500, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
3326
3340
|
}
|
|
3327
3341
|
res.end(renderOAuthCallbackPage({
|
|
3328
|
-
title: '
|
|
3342
|
+
title: 'VuvoDesk connection failed',
|
|
3329
3343
|
message: err instanceof Error ? err.message : String(err),
|
|
3330
3344
|
tone: 'error'
|
|
3331
3345
|
}));
|
|
@@ -3352,7 +3366,7 @@ async function startConnectionChoiceServer(supabase, options = {}) {
|
|
|
3352
3366
|
}
|
|
3353
3367
|
} catch (err) {
|
|
3354
3368
|
if (err?.code === 'EADDRINUSE') {
|
|
3355
|
-
throw new Error(`
|
|
3369
|
+
throw new Error(`VuvoDesk connection page port ${port} is already in use. Close the app using it or run with --auth-port <port> and add that callback URL in Supabase Auth redirect URLs.`);
|
|
3356
3370
|
}
|
|
3357
3371
|
throw err;
|
|
3358
3372
|
}
|
|
@@ -3514,10 +3528,10 @@ async function chooseClientConnection(supabase, options = {}) {
|
|
|
3514
3528
|
await connectionPage.start();
|
|
3515
3529
|
options.onStarted?.(connectionPage);
|
|
3516
3530
|
if (options.openBrowser !== false) {
|
|
3517
|
-
console.log('Opening
|
|
3531
|
+
console.log('Opening VuvoDesk Client page...');
|
|
3518
3532
|
openBrowser(connectionPage.url);
|
|
3519
3533
|
if (!options.savedSession?.access_token && !options.savedPin) {
|
|
3520
|
-
console.log('[
|
|
3534
|
+
console.log('[VuvoDesk Client] Waiting for Google sign-in. The browser starts sign-in automatically; video starts after authentication.');
|
|
3521
3535
|
}
|
|
3522
3536
|
}
|
|
3523
3537
|
const choice = await connectionPage.waitForChoice;
|
|
@@ -3536,7 +3550,7 @@ async function chooseClientConnection(supabase, options = {}) {
|
|
|
3536
3550
|
relayEndpoint: options.relayEndpoint
|
|
3537
3551
|
});
|
|
3538
3552
|
if (options.openBrowser !== false) {
|
|
3539
|
-
console.log('Opening
|
|
3553
|
+
console.log('Opening VuvoDesk connection page...');
|
|
3540
3554
|
openBrowser(connectionPage.url);
|
|
3541
3555
|
}
|
|
3542
3556
|
const choice = await connectionPage.waitForChoice;
|
|
@@ -3887,7 +3901,7 @@ export async function runFreshHubRegistryLookup(resolveFreshTarget, options = {}
|
|
|
3887
3901
|
controller.abort('hub-registry-timeout');
|
|
3888
3902
|
throw createHubDiscoveryError(
|
|
3889
3903
|
'hub-registry-timeout',
|
|
3890
|
-
`Fresh
|
|
3904
|
+
`Fresh VuvoDesk Hub registry lookup exceeded ${timeoutMs}ms.`
|
|
3891
3905
|
);
|
|
3892
3906
|
}
|
|
3893
3907
|
if (outcome.type === 'error') {
|
|
@@ -3989,11 +4003,11 @@ function normalizeEndpointCandidates(target) {
|
|
|
3989
4003
|
|
|
3990
4004
|
export async function resolveManagerFromSupabase(supabase, options = {}) {
|
|
3991
4005
|
if (options.signal?.aborted) {
|
|
3992
|
-
throw createHubDiscoveryError('hub-registry-aborted', '
|
|
4006
|
+
throw createHubDiscoveryError('hub-registry-aborted', 'VuvoDesk Hub registry lookup was cancelled.');
|
|
3993
4007
|
}
|
|
3994
4008
|
await refreshSessionIfNeeded(supabase);
|
|
3995
4009
|
if (options.signal?.aborted) {
|
|
3996
|
-
throw createHubDiscoveryError('hub-registry-aborted', '
|
|
4010
|
+
throw createHubDiscoveryError('hub-registry-aborted', 'VuvoDesk Hub registry lookup was cancelled.');
|
|
3997
4011
|
}
|
|
3998
4012
|
let query = supabase
|
|
3999
4013
|
.from('livedesk_remote_host_targets')
|
|
@@ -4009,26 +4023,26 @@ export async function resolveManagerFromSupabase(supabase, options = {}) {
|
|
|
4009
4023
|
if (!data?.active) {
|
|
4010
4024
|
throw createHubDiscoveryError(
|
|
4011
4025
|
'hub-registry-missing',
|
|
4012
|
-
'No active
|
|
4026
|
+
'No active VuvoDesk Hub was found for this Google account. Start VuvoDesk Hub on the screen wall computer and sign in there first.'
|
|
4013
4027
|
);
|
|
4014
4028
|
}
|
|
4015
4029
|
if (data.expires_at && Date.parse(data.expires_at) <= Date.now()) {
|
|
4016
4030
|
throw createHubDiscoveryError(
|
|
4017
4031
|
'hub-registry-expired',
|
|
4018
|
-
'The
|
|
4032
|
+
'The VuvoDesk Hub record is expired. Open the Hub screen again while signed in.'
|
|
4019
4033
|
);
|
|
4020
4034
|
}
|
|
4021
4035
|
if (!data.pair_token) {
|
|
4022
4036
|
throw createHubDiscoveryError(
|
|
4023
4037
|
'hub-registry-pair-token-missing',
|
|
4024
|
-
'The
|
|
4038
|
+
'The VuvoDesk Hub record is missing its private connection token.'
|
|
4025
4039
|
);
|
|
4026
4040
|
}
|
|
4027
4041
|
const endpointCandidates = normalizeEndpointCandidates(data);
|
|
4028
4042
|
if (endpointCandidates.length === 0) {
|
|
4029
4043
|
throw createHubDiscoveryError(
|
|
4030
4044
|
'hub-registry-endpoints-missing',
|
|
4031
|
-
'The
|
|
4045
|
+
'The VuvoDesk Hub record does not contain a reachable local address.'
|
|
4032
4046
|
);
|
|
4033
4047
|
}
|
|
4034
4048
|
const target = await chooseManagerConnectionTarget(endpointCandidates, {
|
|
@@ -4038,7 +4052,7 @@ export async function resolveManagerFromSupabase(supabase, options = {}) {
|
|
|
4038
4052
|
if (!target) {
|
|
4039
4053
|
throw createHubDiscoveryError(
|
|
4040
4054
|
'hub-direct-unreachable',
|
|
4041
|
-
`No reachable
|
|
4055
|
+
`No reachable VuvoDesk Hub endpoint yet. Checked ${endpointCandidates.join(', ')}.`
|
|
4042
4056
|
);
|
|
4043
4057
|
}
|
|
4044
4058
|
return {
|
|
@@ -4069,11 +4083,11 @@ async function registerClientDeviceWithSupabase(supabase, options = {}) {
|
|
|
4069
4083
|
});
|
|
4070
4084
|
const result = Array.isArray(data) ? data[0] : data;
|
|
4071
4085
|
if (error || result?.ok === false) {
|
|
4072
|
-
console.warn(`[
|
|
4086
|
+
console.warn(`[VuvoDesk Client] Device role registration unavailable: ${error?.message || result?.reason || 'unknown-error'}`);
|
|
4073
4087
|
}
|
|
4074
4088
|
return error ? null : result;
|
|
4075
4089
|
} catch (error) {
|
|
4076
|
-
console.warn(`[
|
|
4090
|
+
console.warn(`[VuvoDesk Client] Device role registration unavailable: ${error?.message || error}`);
|
|
4077
4091
|
return null;
|
|
4078
4092
|
}
|
|
4079
4093
|
}
|
|
@@ -4088,7 +4102,7 @@ export async function waitForManagerFromSupabase(supabase, options = {}) {
|
|
|
4088
4102
|
let lastMessage = '';
|
|
4089
4103
|
let wakeListener = null;
|
|
4090
4104
|
let initialError = options.initialError || null;
|
|
4091
|
-
console.log('Waiting for a
|
|
4105
|
+
console.log('Waiting for a VuvoDesk Hub. VuvoDesk is listening for Hub-online events with adaptive registry retries as a fallback.');
|
|
4092
4106
|
try {
|
|
4093
4107
|
while (true) {
|
|
4094
4108
|
if (shouldStop()) {
|
|
@@ -4101,7 +4115,7 @@ export async function waitForManagerFromSupabase(supabase, options = {}) {
|
|
|
4101
4115
|
});
|
|
4102
4116
|
} catch (error) {
|
|
4103
4117
|
if (attempts === 0) {
|
|
4104
|
-
console.warn(`[
|
|
4118
|
+
console.warn(`[VuvoDesk Client] Hub-online event channel unavailable; adaptive registry retries remain active: ${error?.message || error}`);
|
|
4105
4119
|
}
|
|
4106
4120
|
}
|
|
4107
4121
|
}
|
|
@@ -4126,7 +4140,7 @@ export async function waitForManagerFromSupabase(supabase, options = {}) {
|
|
|
4126
4140
|
if (outcome.type === 'hub-online') {
|
|
4127
4141
|
wakeListener?.close();
|
|
4128
4142
|
wakeListener = null;
|
|
4129
|
-
console.log('[
|
|
4143
|
+
console.log('[VuvoDesk Client] Hub-online event received. Rechecking the registry now.');
|
|
4130
4144
|
continue;
|
|
4131
4145
|
}
|
|
4132
4146
|
return outcome.target;
|
|
@@ -4134,7 +4148,7 @@ export async function waitForManagerFromSupabase(supabase, options = {}) {
|
|
|
4134
4148
|
const message = formatDiscoveryError(err);
|
|
4135
4149
|
if (message !== lastMessage || attempts === 1 || attempts % 6 === 0) {
|
|
4136
4150
|
const suffix = attempts === 1 ? '' : ` attempt ${attempts}`;
|
|
4137
|
-
console.log(`Still waiting for
|
|
4151
|
+
console.log(`Still waiting for VuvoDesk Hub${suffix}: ${message}`);
|
|
4138
4152
|
lastMessage = message;
|
|
4139
4153
|
}
|
|
4140
4154
|
}
|
|
@@ -4147,7 +4161,7 @@ export async function waitForManagerFromSupabase(supabase, options = {}) {
|
|
|
4147
4161
|
if (trigger.type === 'hub-online') {
|
|
4148
4162
|
wakeListener?.close();
|
|
4149
4163
|
wakeListener = null;
|
|
4150
|
-
console.log('[
|
|
4164
|
+
console.log('[VuvoDesk Client] Hub-online event received. Rechecking the registry now.');
|
|
4151
4165
|
}
|
|
4152
4166
|
}
|
|
4153
4167
|
} finally {
|
|
@@ -4178,10 +4192,10 @@ async function prepareLoginConnection(parsed, existingConnectionPage = null, get
|
|
|
4178
4192
|
try {
|
|
4179
4193
|
supabase = await getSupabaseClient();
|
|
4180
4194
|
} catch (error) {
|
|
4181
|
-
console.warn(`
|
|
4195
|
+
console.warn(`VuvoDesk auth provider is unavailable: ${error?.message || error}`);
|
|
4182
4196
|
existingConnectionPage?.update({
|
|
4183
4197
|
lastError: 'auth-provider-unavailable',
|
|
4184
|
-
message: 'The local Client is ready.
|
|
4198
|
+
message: 'The local Client is ready. VuvoDesk sign-in is temporarily unavailable; retry from this page.'
|
|
4185
4199
|
});
|
|
4186
4200
|
if (!existingConnectionPage) throw error;
|
|
4187
4201
|
}
|
|
@@ -4191,7 +4205,7 @@ async function prepareLoginConnection(parsed, existingConnectionPage = null, get
|
|
|
4191
4205
|
try {
|
|
4192
4206
|
savedSession = await refreshSessionIfNeeded(supabase);
|
|
4193
4207
|
} catch (err) {
|
|
4194
|
-
console.warn(`
|
|
4208
|
+
console.warn(`VuvoDesk saved sign-in could not be refreshed: ${err?.message || err}`);
|
|
4195
4209
|
existingConnectionPage?.update({
|
|
4196
4210
|
lastError: 'auth-provider-delayed',
|
|
4197
4211
|
message: 'The local Client is ready. Saved sign-in is delayed; retry sign-in from this page.'
|
|
@@ -4234,8 +4248,8 @@ async function prepareLoginConnection(parsed, existingConnectionPage = null, get
|
|
|
4234
4248
|
existingConnectionPage.acceptChoice?.(
|
|
4235
4249
|
choice,
|
|
4236
4250
|
choice.type === 'google'
|
|
4237
|
-
? 'Saved sign-in found. Finding the
|
|
4238
|
-
: 'Client credentials accepted. Finding the
|
|
4251
|
+
? 'Saved sign-in found. Finding the VuvoDesk Hub.'
|
|
4252
|
+
: 'Client credentials accepted. Finding the VuvoDesk Hub.'
|
|
4239
4253
|
);
|
|
4240
4254
|
}
|
|
4241
4255
|
if (choice.type === 'pin') {
|
|
@@ -4251,13 +4265,13 @@ async function prepareLoginConnection(parsed, existingConnectionPage = null, get
|
|
|
4251
4265
|
assignedHubId: choice.hubDeviceId || '',
|
|
4252
4266
|
message: connectionTransport === 'relay-fallback'
|
|
4253
4267
|
? directProbeSkipReason
|
|
4254
|
-
? `
|
|
4255
|
-
: `
|
|
4256
|
-
: `Connected to
|
|
4268
|
+
? `VuvoDesk Hub found at ${manager}; the private endpoint is outside this local subnet, so encrypted relay/P2P negotiation starts immediately.`
|
|
4269
|
+
: `VuvoDesk Hub found at ${manager}; starting encrypted relay fallback.`
|
|
4270
|
+
: `Connected to VuvoDesk Hub at ${manager}.`
|
|
4257
4271
|
});
|
|
4258
4272
|
console.log(connectionTransport === 'relay-fallback'
|
|
4259
|
-
? '
|
|
4260
|
-
: 'Connected by
|
|
4273
|
+
? 'VuvoDesk PIN accepted. Direct TCP is unavailable; starting encrypted relay fallback.'
|
|
4274
|
+
: 'Connected by VuvoDesk PIN.');
|
|
4261
4275
|
} else {
|
|
4262
4276
|
const cacheOwnerKey = hubCacheOwnerForSession(choice.session);
|
|
4263
4277
|
let session = choice.session;
|
|
@@ -4277,7 +4291,7 @@ async function prepareLoginConnection(parsed, existingConnectionPage = null, get
|
|
|
4277
4291
|
} catch (error) {
|
|
4278
4292
|
if (!isRefreshTokenAlreadyUsedError(error)) throw error;
|
|
4279
4293
|
session = readSavedSessionFromFile() || choice.session;
|
|
4280
|
-
console.warn('[
|
|
4294
|
+
console.warn('[VuvoDesk Client] Another auth owner rotated the refresh token. Keeping the runtime alive while the saved session catches up.');
|
|
4281
4295
|
}
|
|
4282
4296
|
return await resolveManagerFromSupabase(supabase, {
|
|
4283
4297
|
allowRelayFallback,
|
|
@@ -4297,7 +4311,7 @@ async function prepareLoginConnection(parsed, existingConnectionPage = null, get
|
|
|
4297
4311
|
}
|
|
4298
4312
|
choice.session = session;
|
|
4299
4313
|
const email = session?.user?.email ? ` as ${session.user.email}` : '';
|
|
4300
|
-
console.log(`Signed in to
|
|
4314
|
+
console.log(`Signed in to VuvoDesk${email}.`);
|
|
4301
4315
|
if (!resolved) {
|
|
4302
4316
|
resolved = await waitForManagerFromSupabase(supabase, {
|
|
4303
4317
|
allowRelayFallback,
|
|
@@ -4341,16 +4355,16 @@ async function prepareLoginConnection(parsed, existingConnectionPage = null, get
|
|
|
4341
4355
|
assignedHubId: resolved.hubDeviceId || '',
|
|
4342
4356
|
message: connectionTransport === 'relay-fallback'
|
|
4343
4357
|
? directProbeSkipReason
|
|
4344
|
-
? `
|
|
4345
|
-
: `
|
|
4346
|
-
: `Connected to
|
|
4358
|
+
? `VuvoDesk Hub found at ${manager}; the private endpoint is outside this local subnet, so encrypted relay/P2P negotiation starts immediately.`
|
|
4359
|
+
: `VuvoDesk Hub found at ${manager}; starting encrypted relay fallback.`
|
|
4360
|
+
: `Connected to VuvoDesk Hub at ${manager}.`
|
|
4347
4361
|
});
|
|
4348
4362
|
}
|
|
4349
4363
|
console.log(connectionTransport === 'relay-fallback'
|
|
4350
4364
|
? directProbeSkipReason
|
|
4351
|
-
? `Found
|
|
4352
|
-
: `Found
|
|
4353
|
-
: `Found
|
|
4365
|
+
? `Found VuvoDesk 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.`
|
|
4366
|
+
: `Found VuvoDesk Hub record at ${manager}; direct TCP did not answer, so RemoteFast will use encrypted relay control and prefer UDP P2P video.`
|
|
4367
|
+
: `Found VuvoDesk Hub at ${manager}.`);
|
|
4354
4368
|
}
|
|
4355
4369
|
// Exact-package updates preserve the already paired Hub endpoint and pass
|
|
4356
4370
|
// --no-login so the replacement cannot block on browser auth. The unified
|
|
@@ -4372,7 +4386,7 @@ async function prepareLoginConnection(parsed, existingConnectionPage = null, get
|
|
|
4372
4386
|
if (existingConnectionPage) {
|
|
4373
4387
|
existingConnectionPage.acceptChoice?.(
|
|
4374
4388
|
initialChoice,
|
|
4375
|
-
`Using the existing
|
|
4389
|
+
`Using the existing VuvoDesk Hub pairing at ${manager}.`
|
|
4376
4390
|
);
|
|
4377
4391
|
connectionPage = existingConnectionPage;
|
|
4378
4392
|
} else {
|
|
@@ -4383,7 +4397,7 @@ async function prepareLoginConnection(parsed, existingConnectionPage = null, get
|
|
|
4383
4397
|
savedSession: preservedSession,
|
|
4384
4398
|
openBrowser: false,
|
|
4385
4399
|
initialChoice,
|
|
4386
|
-
initialChoiceMessage: `Using the existing
|
|
4400
|
+
initialChoiceMessage: `Using the existing VuvoDesk Hub pairing at ${manager}.`
|
|
4387
4401
|
});
|
|
4388
4402
|
connectionPage = explicitConnection.connectionPage || null;
|
|
4389
4403
|
}
|
|
@@ -4742,7 +4756,7 @@ function buildFastEnvironment(prepared) {
|
|
|
4742
4756
|
: path.includes('@ffmpeg-installer')
|
|
4743
4757
|
? '@ffmpeg-installer/ffmpeg'
|
|
4744
4758
|
: path;
|
|
4745
|
-
console.error(`
|
|
4759
|
+
console.error(`VuvoDesk client ${readPackageVersion()} ffmpeg candidates: ${merged.map(describe).join(', ') || 'none'}`);
|
|
4746
4760
|
}
|
|
4747
4761
|
return env;
|
|
4748
4762
|
}
|
|
@@ -4750,7 +4764,7 @@ function buildFastEnvironment(prepared) {
|
|
|
4750
4764
|
async function waitForProcessExit(pid, timeoutMs = 120_000) {
|
|
4751
4765
|
const processId = Number(pid);
|
|
4752
4766
|
if (!Number.isInteger(processId) || processId <= 0 || processId === process.pid) {
|
|
4753
|
-
throw new Error('
|
|
4767
|
+
throw new Error('VuvoDesk update did not receive a valid previous client process id.');
|
|
4754
4768
|
}
|
|
4755
4769
|
const startedAt = Date.now();
|
|
4756
4770
|
while (Date.now() - startedAt < timeoutMs) {
|
|
@@ -4764,7 +4778,7 @@ async function waitForProcessExit(pid, timeoutMs = 120_000) {
|
|
|
4764
4778
|
if (!alive) return;
|
|
4765
4779
|
await new Promise(resolve => setTimeout(resolve, 250));
|
|
4766
4780
|
}
|
|
4767
|
-
throw new Error(`Previous
|
|
4781
|
+
throw new Error(`Previous VuvoDesk client process ${processId} did not exit before the update deadline.`);
|
|
4768
4782
|
}
|
|
4769
4783
|
|
|
4770
4784
|
function describePreflightFailure(result) {
|
|
@@ -4993,7 +5007,7 @@ function spawnAgent(command, args, env = process.env, onStart = null) {
|
|
|
4993
5007
|
await drainOwnedUnixAgentTree(child);
|
|
4994
5008
|
} catch (error) {
|
|
4995
5009
|
console.error(
|
|
4996
|
-
`[
|
|
5010
|
+
`[VuvoDesk Client] Could not drain exited Agent tree pid=${child.pid || 'unknown'}: `
|
|
4997
5011
|
+ `${error?.message || error}`
|
|
4998
5012
|
);
|
|
4999
5013
|
// Block this lifecycle iteration from spawning a
|
|
@@ -5052,12 +5066,12 @@ export async function runClientRuntime(argv = process.argv.slice(2), runtimeOpti
|
|
|
5052
5066
|
unregisterWindowsStartup();
|
|
5053
5067
|
}
|
|
5054
5068
|
if (!isTruthy(process.env.LIVEDESK_UNIFIED_RUNTIME)) {
|
|
5055
|
-
console.warn('[
|
|
5069
|
+
console.warn('[VuvoDesk] @livedesk/client is deprecated. Use the unified livedesk package or VuvoDesk Desktop.');
|
|
5056
5070
|
}
|
|
5057
5071
|
const parsed = parseLauncherArgs(argv);
|
|
5058
5072
|
if (parsed.retiredModelOptionsDiscarded > 0) {
|
|
5059
5073
|
console.warn(
|
|
5060
|
-
'[
|
|
5074
|
+
'[VuvoDesk Client] Ignored retired Client options. '
|
|
5061
5075
|
+ 'Computer commands are handled by the Hub Codex Agent.'
|
|
5062
5076
|
);
|
|
5063
5077
|
}
|
|
@@ -5084,7 +5098,7 @@ export async function runClientRuntime(argv = process.argv.slice(2), runtimeOpti
|
|
|
5084
5098
|
const accelerationLabel = linuxVideoAccelerationStatus.ready
|
|
5085
5099
|
? `${linuxVideoAccelerationStatus.encoder} via ${linuxVideoAccelerationStatus.ffmpegPath}`
|
|
5086
5100
|
: linuxVideoAccelerationStatus.message || linuxVideoAccelerationStatus.state;
|
|
5087
|
-
console.log(`[
|
|
5101
|
+
console.log(`[VuvoDesk Client] Linux video acceleration: ${accelerationLabel}`);
|
|
5088
5102
|
}
|
|
5089
5103
|
|
|
5090
5104
|
let supabaseClientPromise = null;
|
|
@@ -5133,9 +5147,9 @@ export async function runClientRuntime(argv = process.argv.slice(2), runtimeOpti
|
|
|
5133
5147
|
void startupChoice.catch(rejectStarted);
|
|
5134
5148
|
connectionPage = await started;
|
|
5135
5149
|
connectionPage.update({
|
|
5136
|
-
message: 'Checking saved sign-in and the current
|
|
5150
|
+
message: 'Checking saved sign-in and the current VuvoDesk Hub.'
|
|
5137
5151
|
});
|
|
5138
|
-
console.log(`[
|
|
5152
|
+
console.log(`[VuvoDesk Client] Local status is ready at ${connectionPage.url}`);
|
|
5139
5153
|
}
|
|
5140
5154
|
while (true) {
|
|
5141
5155
|
const savedSlot = readSavedDeviceSlot(parsed.deviceId);
|
|
@@ -5177,8 +5191,8 @@ export async function runClientRuntime(argv = process.argv.slice(2), runtimeOpti
|
|
|
5177
5191
|
...patch
|
|
5178
5192
|
},
|
|
5179
5193
|
message: patch.state === 'running'
|
|
5180
|
-
? `
|
|
5181
|
-
: '
|
|
5194
|
+
? `VuvoDesk client agent is running with ${patch.engine || 'agent'} mode.`
|
|
5195
|
+
: 'VuvoDesk client agent is launching.'
|
|
5182
5196
|
});
|
|
5183
5197
|
};
|
|
5184
5198
|
let result;
|
|
@@ -5289,7 +5303,7 @@ export async function runClientRuntime(argv = process.argv.slice(2), runtimeOpti
|
|
|
5289
5303
|
pid: 0,
|
|
5290
5304
|
error: result.lifecycleFailure
|
|
5291
5305
|
},
|
|
5292
|
-
message: '
|
|
5306
|
+
message: 'VuvoDesk stopped because the previous Windows capture tree could not be drained safely.'
|
|
5293
5307
|
});
|
|
5294
5308
|
connectionPage?.close?.();
|
|
5295
5309
|
throw new Error(result.lifecycleFailure);
|
|
@@ -5311,10 +5325,10 @@ export async function runClientRuntime(argv = process.argv.slice(2), runtimeOpti
|
|
|
5311
5325
|
pid: 0
|
|
5312
5326
|
},
|
|
5313
5327
|
message: restart.reason === 'slot-updated'
|
|
5314
|
-
? `Slot ${String(restart.slotNumber || prepared.slot || '').padStart(3, '0')} saved. Restarting the
|
|
5315
|
-
: 'Linux hardware video is ready. Restarting the
|
|
5328
|
+
? `Slot ${String(restart.slotNumber || prepared.slot || '').padStart(3, '0')} saved. Restarting the VuvoDesk Agent.`
|
|
5329
|
+
: 'Linux hardware video is ready. Restarting the VuvoDesk Agent.'
|
|
5316
5330
|
});
|
|
5317
|
-
console.log(`[
|
|
5331
|
+
console.log(`[VuvoDesk Client] Restarting Agent after ${restart.reason}.`);
|
|
5318
5332
|
continue;
|
|
5319
5333
|
}
|
|
5320
5334
|
const terminalShutdownRequested = disposeAgentTerminationHandlers.isTerminating?.() === true;
|
|
@@ -5335,10 +5349,10 @@ export async function runClientRuntime(argv = process.argv.slice(2), runtimeOpti
|
|
|
5335
5349
|
clearCachedHubTarget();
|
|
5336
5350
|
}
|
|
5337
5351
|
console.error(pairChanged
|
|
5338
|
-
? '
|
|
5352
|
+
? 'VuvoDesk Hub pair token changed. Refreshing Hub discovery now.'
|
|
5339
5353
|
: result?.signal
|
|
5340
|
-
? `
|
|
5341
|
-
: '
|
|
5354
|
+
? `VuvoDesk Agent ended unexpectedly with signal ${result.signal}. Keeping the Client alive and reconnecting to the Hub now.`
|
|
5355
|
+
: 'VuvoDesk Hub connection ended. Looking for the current Hub now.');
|
|
5342
5356
|
connectionPage?.update({
|
|
5343
5357
|
agent: {
|
|
5344
5358
|
requestedEngine: prepared.engine,
|
|
@@ -5346,7 +5360,7 @@ export async function runClientRuntime(argv = process.argv.slice(2), runtimeOpti
|
|
|
5346
5360
|
pid: 0
|
|
5347
5361
|
},
|
|
5348
5362
|
manager: '',
|
|
5349
|
-
message: 'The previous Hub is unavailable.
|
|
5363
|
+
message: 'The previous Hub is unavailable. VuvoDesk is finding the current Hub automatically.'
|
|
5350
5364
|
});
|
|
5351
5365
|
continue;
|
|
5352
5366
|
}
|