@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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livedesk/client",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.252",
|
|
4
|
+
"description": "VuvoDesk local remote client",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"client": "bin/livedesk-client.js",
|
|
@@ -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.6",
|
|
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.448",
|
|
46
|
+
"@livedesk/fast-osx-arm64": "0.1.448",
|
|
47
|
+
"@livedesk/fast-osx-x64": "0.1.448",
|
|
48
|
+
"@livedesk/fast-win-x64": "0.1.448"
|
|
49
49
|
},
|
|
50
50
|
"publishConfig": {
|
|
51
51
|
"access": "public"
|
|
@@ -217,7 +217,7 @@ export function createWindowsProcessTreeTracker(child, {
|
|
|
217
217
|
waitImpl = waitMilliseconds,
|
|
218
218
|
publishTrackedRecords = null,
|
|
219
219
|
reportPublisherError = error => console.warn(
|
|
220
|
-
`[
|
|
220
|
+
`[VuvoDesk Client] Windows owned-process manifest warning: ${error?.message || error}`
|
|
221
221
|
)
|
|
222
222
|
} = {}) {
|
|
223
223
|
const rootPid = Number(child?.pid || 0);
|
|
@@ -501,7 +501,7 @@ export async function drainOwnedWindowsAgentTree(child, {
|
|
|
501
501
|
: `Exact Windows Agent tree retained pid(s): ${remaining.map(record => record.pid).join(', ')}`
|
|
502
502
|
);
|
|
503
503
|
reportTerminationFailure(
|
|
504
|
-
`[
|
|
504
|
+
`[VuvoDesk Client] Exact Windows Agent tree could not be drained after `
|
|
505
505
|
+ `${attempts} bounded attempt(s): ${error?.message || error}`
|
|
506
506
|
);
|
|
507
507
|
return { ok: false, error, remaining };
|
|
@@ -549,7 +549,7 @@ export async function drainOwnedWindowsAgentTreeUntilStopped(child, {
|
|
|
549
549
|
return result;
|
|
550
550
|
}
|
|
551
551
|
reportTerminationFailure(
|
|
552
|
-
`[
|
|
552
|
+
`[VuvoDesk Client] Exact Windows Agent tree is not drained yet `
|
|
553
553
|
+ `(attempt ${attempt}); the launcher will remain alive and retry.`
|
|
554
554
|
);
|
|
555
555
|
await waitImpl(Math.max(100, Number(retryForeverMs) || 1000));
|
|
@@ -639,7 +639,7 @@ export async function drainOwnedUnixAgentTree(child, {
|
|
|
639
639
|
signalOwnedGroup(gracefulSignal);
|
|
640
640
|
} catch (error) {
|
|
641
641
|
reportTerminationFailure(
|
|
642
|
-
`[
|
|
642
|
+
`[VuvoDesk Client] Exited Agent group pid=${processId} rejected ${gracefulSignal} `
|
|
643
643
|
+ `(${error?.message || error}); forced cleanup will continue.`
|
|
644
644
|
);
|
|
645
645
|
}
|
|
@@ -660,7 +660,7 @@ export async function drainOwnedUnixAgentTree(child, {
|
|
|
660
660
|
if (!failureReported) {
|
|
661
661
|
failureReported = true;
|
|
662
662
|
reportTerminationFailure(
|
|
663
|
-
`[
|
|
663
|
+
`[VuvoDesk Client] Exited Agent group pid=${processId} rejected SIGKILL `
|
|
664
664
|
+ `(${error?.message || error}). The launcher will not start a replacement until the group drains.`
|
|
665
665
|
);
|
|
666
666
|
}
|
|
@@ -669,7 +669,7 @@ export async function drainOwnedUnixAgentTree(child, {
|
|
|
669
669
|
if (!failureReported) {
|
|
670
670
|
failureReported = true;
|
|
671
671
|
reportTerminationFailure(
|
|
672
|
-
`[
|
|
672
|
+
`[VuvoDesk Client] Exited Agent group pid=${processId} still has capture descendants after SIGKILL. `
|
|
673
673
|
+ 'The launcher will not start a replacement until the group drains.'
|
|
674
674
|
);
|
|
675
675
|
}
|
|
@@ -767,7 +767,7 @@ export function installAgentTerminationHandlers({
|
|
|
767
767
|
return;
|
|
768
768
|
}
|
|
769
769
|
reportTerminationFailure(
|
|
770
|
-
`[
|
|
770
|
+
`[VuvoDesk Client] Terminal shutdown retained an exact Windows Agent tree: `
|
|
771
771
|
+ `${result.error?.message || 'bounded drain failed'}. Retrying before exit.`
|
|
772
772
|
);
|
|
773
773
|
child.livedeskTreeStopPromise = null;
|
|
@@ -799,7 +799,7 @@ export function installAgentTerminationHandlers({
|
|
|
799
799
|
signalAgentTree(child, signal, platform, signalProcess);
|
|
800
800
|
} catch (error) {
|
|
801
801
|
reportTerminationFailure(
|
|
802
|
-
`[
|
|
802
|
+
`[VuvoDesk Client] Agent tree pid=${processId || 'unknown'} could not receive ${signal} `
|
|
803
803
|
+ `(${error?.message || error}). Forced cleanup will continue.`
|
|
804
804
|
);
|
|
805
805
|
}
|
|
@@ -816,7 +816,7 @@ export function installAgentTerminationHandlers({
|
|
|
816
816
|
if (!hardFailureReported) {
|
|
817
817
|
hardFailureReported = true;
|
|
818
818
|
reportTerminationFailure(
|
|
819
|
-
`[
|
|
819
|
+
`[VuvoDesk Client] Agent tree pid=${processId || 'unknown'} rejected SIGKILL `
|
|
820
820
|
+ `(${error?.message || error}). The launcher will remain alive and retry.`
|
|
821
821
|
);
|
|
822
822
|
}
|
|
@@ -826,7 +826,7 @@ export function installAgentTerminationHandlers({
|
|
|
826
826
|
if (!hardFailureReported) {
|
|
827
827
|
hardFailureReported = true;
|
|
828
828
|
reportTerminationFailure(
|
|
829
|
-
`[
|
|
829
|
+
`[VuvoDesk Client] Agent tree pid=${processId || 'unknown'} is still alive after SIGKILL. `
|
|
830
830
|
+ 'The launcher will remain alive and keep retrying so capture descendants are not orphaned.'
|
|
831
831
|
);
|
|
832
832
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
|
|
3
|
+
export const AGENT_SHELL_NAMES = Object.freeze([
|
|
4
|
+
'auto',
|
|
5
|
+
'powershell',
|
|
6
|
+
'pwsh',
|
|
7
|
+
'cmd',
|
|
8
|
+
'sh',
|
|
9
|
+
'bash',
|
|
10
|
+
'zsh'
|
|
11
|
+
]);
|
|
12
|
+
|
|
13
|
+
function unixShellPath(shell, platform, fileExists) {
|
|
14
|
+
if (shell === 'sh') return '/bin/sh';
|
|
15
|
+
if (shell === 'bash') return '/bin/bash';
|
|
16
|
+
if (shell === 'zsh') return '/bin/zsh';
|
|
17
|
+
if (shell === 'pwsh') return 'pwsh';
|
|
18
|
+
if (shell === 'powershell' || shell === 'cmd') {
|
|
19
|
+
throw new Error(`${shell} is not available on ${platform}.`);
|
|
20
|
+
}
|
|
21
|
+
if (platform === 'darwin' && fileExists('/bin/zsh')) return '/bin/zsh';
|
|
22
|
+
if (fileExists('/bin/bash')) return '/bin/bash';
|
|
23
|
+
return '/bin/sh';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function resolveAgentShellCommand({
|
|
27
|
+
platform = process.platform,
|
|
28
|
+
shell = 'auto',
|
|
29
|
+
command = '',
|
|
30
|
+
fileExists = existsSync
|
|
31
|
+
} = {}) {
|
|
32
|
+
const normalizedShell = String(shell || 'auto').trim().toLowerCase();
|
|
33
|
+
if (!AGENT_SHELL_NAMES.includes(normalizedShell)) {
|
|
34
|
+
throw new Error(`Unsupported shell: ${normalizedShell || 'empty'}.`);
|
|
35
|
+
}
|
|
36
|
+
if (platform === 'win32') {
|
|
37
|
+
if (['sh', 'bash', 'zsh'].includes(normalizedShell)) {
|
|
38
|
+
throw new Error(`${normalizedShell} is not available on Windows.`);
|
|
39
|
+
}
|
|
40
|
+
if (normalizedShell === 'cmd') {
|
|
41
|
+
return {
|
|
42
|
+
shell: 'cmd',
|
|
43
|
+
executable: process.env.ComSpec || 'cmd.exe',
|
|
44
|
+
args: ['/d', '/s', '/c', String(command)]
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const powershellCore = normalizedShell === 'pwsh';
|
|
48
|
+
return {
|
|
49
|
+
shell: powershellCore ? 'pwsh' : 'powershell',
|
|
50
|
+
executable: powershellCore ? 'pwsh.exe' : 'powershell.exe',
|
|
51
|
+
args: ['-NoLogo', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', String(command)]
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const executable = unixShellPath(normalizedShell, platform, fileExists);
|
|
56
|
+
if (executable.startsWith('/') && !fileExists(executable)) {
|
|
57
|
+
throw new Error(`${normalizedShell} is not installed at ${executable}.`);
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
shell: normalizedShell === 'auto'
|
|
61
|
+
? executable.endsWith('/zsh') ? 'zsh' : executable.endsWith('/bash') ? 'bash' : 'sh'
|
|
62
|
+
: normalizedShell,
|
|
63
|
+
executable,
|
|
64
|
+
args: ['-lc', String(command)]
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -20,8 +20,9 @@ const SUPABASE_URL = process.env.LIVEDESK_SUPABASE_URL || 'https://otbyfkjxrkngv
|
|
|
20
20
|
const SUPABASE_PUBLISHABLE_KEY = process.env.LIVEDESK_SUPABASE_PUBLISHABLE_KEY || 'sb_publishable_NpUs0RDJH2YnllsqTKO6TQ_1jTdSsNQ';
|
|
21
21
|
const CLIENT_STATE_DIR = process.env.LIVEDESK_STATE_DIR || (process.env.LIVEDESK_UNIFIED_RUNTIME === '1' ? join(os.homedir(), '.livedesk') : join(os.homedir(), '.livedesk-client'));
|
|
22
22
|
const CLIENT_AUTH_PATH = join(CLIENT_STATE_DIR, 'auth.json');
|
|
23
|
-
const DEFAULT_TRUSTED_WEB_ORIGINS = Object.freeze([
|
|
24
|
-
'https://
|
|
23
|
+
const DEFAULT_TRUSTED_WEB_ORIGINS = Object.freeze([
|
|
24
|
+
'https://vuvodesk.com',
|
|
25
|
+
'https://livedesk.pages.dev',
|
|
25
26
|
'http://127.0.0.1:5173',
|
|
26
27
|
'http://localhost:5173',
|
|
27
28
|
'http://127.0.0.1:4173',
|
|
@@ -1050,7 +1051,7 @@ function isTrustedLocalRequest(req, host, port, options = {}) {
|
|
|
1050
1051
|
|
|
1051
1052
|
// Chromium can mark modulepreload/stylesheet requests as cross-site. Static
|
|
1052
1053
|
// assets are not privileged. An opaque Origin (`null`) is never accepted for
|
|
1053
|
-
// an API route because it cannot distinguish the
|
|
1054
|
+
// an API route because it cannot distinguish the VuvoDesk shell from an
|
|
1054
1055
|
// attacker-controlled file, sandbox, or data page.
|
|
1055
1056
|
if (options.allowStaticCrossSite === true) {
|
|
1056
1057
|
return true;
|
|
@@ -1107,7 +1108,7 @@ export function createClientRuntimeServer(options = {}) {
|
|
|
1107
1108
|
const initialChoiceMessage = normalizeString(
|
|
1108
1109
|
options.initialChoiceMessage,
|
|
1109
1110
|
1000
|
|
1110
|
-
) || 'Existing
|
|
1111
|
+
) || 'Existing VuvoDesk credentials accepted. Starting the Client.';
|
|
1111
1112
|
const trustedWebOrigins = createTrustedWebOrigins(port);
|
|
1112
1113
|
const webDist = resolve(String(options.webDist || process.env.LIVEDESK_WEB_DIST || '').trim() || join(process.cwd(), 'apps', 'web', 'dist'));
|
|
1113
1114
|
const deviceId = normalizeString(options.deviceId || process.env.LIVEDESK_DEVICE_ID, 160);
|
|
@@ -1334,7 +1335,7 @@ export function createClientRuntimeServer(options = {}) {
|
|
|
1334
1335
|
state.lastError = '';
|
|
1335
1336
|
if (state.message.startsWith('Client authentication needs attention:')) {
|
|
1336
1337
|
state.message = state.agent.state === 'running'
|
|
1337
|
-
? 'Connected to the
|
|
1338
|
+
? 'Connected to the VuvoDesk Hub.'
|
|
1338
1339
|
: normalizeString(message, 1000) || 'Client credentials accepted. Finding the Hub.';
|
|
1339
1340
|
}
|
|
1340
1341
|
}
|
|
@@ -1453,7 +1454,7 @@ export function createClientRuntimeServer(options = {}) {
|
|
|
1453
1454
|
: pathname.replace(/^\//, '');
|
|
1454
1455
|
const filePath = resolve(webDist, requested);
|
|
1455
1456
|
if (!filePath.startsWith(`${webDist}${process.platform === 'win32' ? '\\' : '/'}`) || !existsSync(filePath)) {
|
|
1456
|
-
sendText(res, 404, '
|
|
1457
|
+
sendText(res, 404, 'VuvoDesk web build is not available. Run npm run build.');
|
|
1457
1458
|
return;
|
|
1458
1459
|
}
|
|
1459
1460
|
try {
|
|
@@ -1500,7 +1501,7 @@ export function createClientRuntimeServer(options = {}) {
|
|
|
1500
1501
|
return;
|
|
1501
1502
|
}
|
|
1502
1503
|
if (pathname === '/api/health') {
|
|
1503
|
-
respondJson(200, { ok: true, product: 'LiveDesk', role: 'client', timestamp: new Date().toISOString() });
|
|
1504
|
+
respondJson(200, { ok: true, product: 'LiveDesk', role: 'client', timestamp: new Date().toISOString() });
|
|
1504
1505
|
return;
|
|
1505
1506
|
}
|
|
1506
1507
|
if (pathname === '/api/client/diagnostics') {
|
|
@@ -1582,7 +1583,7 @@ export function createClientRuntimeServer(options = {}) {
|
|
|
1582
1583
|
};
|
|
1583
1584
|
if (!writeSavedSession(session)) throw new Error('refresh-token-required');
|
|
1584
1585
|
state.auth.persisted = true;
|
|
1585
|
-
complete({ type: 'google', session }, 'Signed in. Finding the
|
|
1586
|
+
complete({ type: 'google', session }, 'Signed in. Finding the VuvoDesk Hub.');
|
|
1586
1587
|
res.writeHead(303, { Location: '/', 'Cache-Control': 'no-store' });
|
|
1587
1588
|
res.end();
|
|
1588
1589
|
} catch (error) {
|
|
@@ -1629,7 +1630,7 @@ export function createClientRuntimeServer(options = {}) {
|
|
|
1629
1630
|
respondJson(500, { ok: false, error: message });
|
|
1630
1631
|
return;
|
|
1631
1632
|
}
|
|
1632
|
-
complete({ type: 'google', session }, 'Signed in. Finding the
|
|
1633
|
+
complete({ type: 'google', session }, 'Signed in. Finding the VuvoDesk Hub.');
|
|
1633
1634
|
respondJson(200, { ok: true, authenticated: true, persisted: true, role: 'client' });
|
|
1634
1635
|
return;
|
|
1635
1636
|
}
|
|
@@ -1812,7 +1813,7 @@ export function createClientRuntimeServer(options = {}) {
|
|
|
1812
1813
|
state.manager = '';
|
|
1813
1814
|
state.agent.state = 'reconnecting';
|
|
1814
1815
|
state.lastError = '';
|
|
1815
|
-
state.message = 'Refreshing the
|
|
1816
|
+
state.message = 'Refreshing the VuvoDesk Hub discovery now.';
|
|
1816
1817
|
runtime.update({ state: RuntimeState.RESOLVING_ROLE }, 'client-reconnect-requested');
|
|
1817
1818
|
runtime.emit('client.reconnect.requested', { deviceId, previousManager: previousManager || '' });
|
|
1818
1819
|
await options.onReconnect?.();
|
|
@@ -1895,7 +1896,7 @@ export function createClientRuntimeServer(options = {}) {
|
|
|
1895
1896
|
const hydratedSession = await hydrateClientAccountProfile(normalizedSession);
|
|
1896
1897
|
if (!writeSavedSession(hydratedSession)) throw new Error('refresh-token-required');
|
|
1897
1898
|
state.auth.persisted = true;
|
|
1898
|
-
complete({ type: 'google', session: hydratedSession }, 'Saved sign-in found. Finding the
|
|
1899
|
+
complete({ type: 'google', session: hydratedSession }, 'Saved sign-in found. Finding the VuvoDesk Hub.');
|
|
1899
1900
|
} catch (error) {
|
|
1900
1901
|
recordAuthAttempt('rejected', `session-persistence-failed:${normalizeString(error?.message || error, 160)}`);
|
|
1901
1902
|
}
|
|
@@ -385,6 +385,6 @@ export async function installLinuxVideoAcceleration(currentStatus, options = {})
|
|
|
385
385
|
...refreshed,
|
|
386
386
|
busy: false,
|
|
387
387
|
restartAgent: true,
|
|
388
|
-
message: 'VAAPI hardware video is ready. Restarting the
|
|
388
|
+
message: 'VAAPI hardware video is ready. Restarting the VuvoDesk Agent…'
|
|
389
389
|
};
|
|
390
390
|
}
|
|
@@ -135,13 +135,13 @@ export function writeWindowsOwnedProcessManifest({
|
|
|
135
135
|
if (!exactOwner) {
|
|
136
136
|
return {
|
|
137
137
|
ok: false,
|
|
138
|
-
error: resultError('An exact Windows
|
|
138
|
+
error: resultError('An exact Windows VuvoDesk launcher owner is required.')
|
|
139
139
|
};
|
|
140
140
|
}
|
|
141
141
|
if (!exactRecords) {
|
|
142
142
|
return {
|
|
143
143
|
ok: false,
|
|
144
|
-
error: resultError('Every Windows
|
|
144
|
+
error: resultError('Every Windows VuvoDesk process record requires exact PID and CreationDate identity.')
|
|
145
145
|
};
|
|
146
146
|
}
|
|
147
147
|
if (!Number.isFinite(nowMs)) {
|
|
@@ -177,7 +177,7 @@ function readExactWindowsOwnedProcessManifest({
|
|
|
177
177
|
return {
|
|
178
178
|
ok: false,
|
|
179
179
|
records: [],
|
|
180
|
-
error: resultError('An exact Windows
|
|
180
|
+
error: resultError('An exact Windows VuvoDesk launcher owner is required.')
|
|
181
181
|
};
|
|
182
182
|
}
|
|
183
183
|
if (!Number.isFinite(nowMs)
|
|
@@ -210,7 +210,7 @@ function readExactWindowsOwnedProcessManifest({
|
|
|
210
210
|
ok: false,
|
|
211
211
|
records: [],
|
|
212
212
|
updatedAt,
|
|
213
|
-
error: resultError('Unsupported Windows
|
|
213
|
+
error: resultError('Unsupported Windows VuvoDesk process-manifest version.')
|
|
214
214
|
};
|
|
215
215
|
}
|
|
216
216
|
if (!ownersMatch(parsed?.owner, exactOwner)) {
|
|
@@ -218,7 +218,7 @@ function readExactWindowsOwnedProcessManifest({
|
|
|
218
218
|
ok: false,
|
|
219
219
|
records: [],
|
|
220
220
|
updatedAt,
|
|
221
|
-
error: resultError('Windows
|
|
221
|
+
error: resultError('Windows VuvoDesk process manifest belongs to another launcher generation.')
|
|
222
222
|
};
|
|
223
223
|
}
|
|
224
224
|
if (!Number.isFinite(updatedAtMs)
|
|
@@ -228,7 +228,7 @@ function readExactWindowsOwnedProcessManifest({
|
|
|
228
228
|
ok: false,
|
|
229
229
|
records: [],
|
|
230
230
|
updatedAt,
|
|
231
|
-
error: resultError('Windows
|
|
231
|
+
error: resultError('Windows VuvoDesk process manifest is stale or has an invalid timestamp.')
|
|
232
232
|
};
|
|
233
233
|
}
|
|
234
234
|
const exactRecords = normalizeRecords(parsed?.records);
|
|
@@ -237,7 +237,7 @@ function readExactWindowsOwnedProcessManifest({
|
|
|
237
237
|
ok: false,
|
|
238
238
|
records: [],
|
|
239
239
|
updatedAt,
|
|
240
|
-
error: resultError('Windows
|
|
240
|
+
error: resultError('Windows VuvoDesk process manifest contains an invalid process identity.')
|
|
241
241
|
};
|
|
242
242
|
}
|
|
243
243
|
return {
|
|
@@ -327,7 +327,7 @@ export function consumeDeadWindowsOwnedProcessManifest({
|
|
|
327
327
|
ok: false,
|
|
328
328
|
path,
|
|
329
329
|
consumed: false,
|
|
330
|
-
error: resultError('Windows
|
|
330
|
+
error: resultError('Windows VuvoDesk process manifest changed after its exact process set was drained.')
|
|
331
331
|
};
|
|
332
332
|
}
|
|
333
333
|
|
|
@@ -374,7 +374,7 @@ export function consumeDeadWindowsOwnedProcessManifest({
|
|
|
374
374
|
quarantinePath: restored ? undefined : quarantinePath,
|
|
375
375
|
consumed: false,
|
|
376
376
|
error: resultError(
|
|
377
|
-
'Windows
|
|
377
|
+
'Windows VuvoDesk process manifest changed during consumption; '
|
|
378
378
|
+ `${restored ? 'the moved generation was restored.' : 'the moved generation was preserved.'}`
|
|
379
379
|
+ `${restoreError?.message ? ` ${restoreError.message}` : ''}`
|
|
380
380
|
)
|
|
@@ -15,13 +15,13 @@ test('prints the public launcher version in the user-facing banner', () => {
|
|
|
15
15
|
LIVEDESK_NPM_LAUNCHER_NAME: 'livedesk',
|
|
16
16
|
LIVEDESK_NPM_LAUNCHER_VERSION: '9.8.7'
|
|
17
17
|
}),
|
|
18
|
-
'[
|
|
18
|
+
'[VuvoDesk Client] livedesk@9.8.7'
|
|
19
19
|
);
|
|
20
20
|
});
|
|
21
21
|
|
|
22
22
|
test('uses the product name for direct client launches', () => {
|
|
23
23
|
assert.equal(
|
|
24
24
|
formatClientVersionBanner({}),
|
|
25
|
-
`[
|
|
25
|
+
`[VuvoDesk Client] livedesk@${packageInfo.version}`
|
|
26
26
|
);
|
|
27
27
|
});
|