@livedesk/client 0.1.173 → 0.1.174
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/livedesk-client.js
CHANGED
|
@@ -14,6 +14,7 @@ import { createClientRuntimeServer } from '../src/runtime/client-runtime-server.
|
|
|
14
14
|
import { installAgentTerminationHandlers } from '../src/runtime/agent-process-lifecycle.js';
|
|
15
15
|
import { createHubWakeListener } from '../src/runtime/hub-wake-listener.js';
|
|
16
16
|
import { normalizeRuntimeAuthSession } from '../../runtime-core/src/auth-session.js';
|
|
17
|
+
import { startRoleTransitionSupervisor } from '../../runtime-core/src/role-transition-supervisor.js';
|
|
17
18
|
|
|
18
19
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
19
20
|
const require = createRequire(import.meta.url);
|
|
@@ -21,6 +22,7 @@ const packageRoot = resolve(__dirname, '..');
|
|
|
21
22
|
const nodeAgentPath = join(__dirname, 'livedesk-client-node.js');
|
|
22
23
|
const FAST_PREFLIGHT_TIMEOUT_MS = 5000;
|
|
23
24
|
const DEFAULT_MANAGER = '127.0.0.1:5197';
|
|
25
|
+
const DEFAULT_HUB_CLIENT_PORT = 5197;
|
|
24
26
|
const DEFAULT_AUTH_CALLBACK_HOST = '127.0.0.1';
|
|
25
27
|
const DEFAULT_AUTH_CALLBACK_PORT = 5179;
|
|
26
28
|
const ENDPOINT_PROBE_TIMEOUT_MS = 900;
|
|
@@ -112,30 +114,53 @@ function writeLocalRoleCache(role, deviceId, roleVersion = 0, assignedHubId = ''
|
|
|
112
114
|
return true;
|
|
113
115
|
}
|
|
114
116
|
|
|
115
|
-
function
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
117
|
+
export function buildUnifiedRoleRestartEnvironment(role, baseEnv = process.env, runtimePid = process.pid) {
|
|
118
|
+
const normalizedRole = String(role || '').trim().toLowerCase();
|
|
119
|
+
const waitPid = Number(runtimePid);
|
|
120
|
+
if (normalizedRole !== 'hub' && normalizedRole !== 'client') {
|
|
121
|
+
throw new Error(`Unsupported LiveDesk role restart: ${role}`);
|
|
122
|
+
}
|
|
123
|
+
if (!Number.isInteger(waitPid) || waitPid <= 1) {
|
|
124
|
+
throw new Error(`Invalid LiveDesk runtime PID for role restart: ${runtimePid}`);
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
...baseEnv,
|
|
128
|
+
LIVEDESK_ROLE_TRANSITION: '1',
|
|
129
|
+
// The replacement must wait only for this Client runtime. Waiting for
|
|
130
|
+
// npm, npx, PowerShell, or another terminal parent can deadlock the
|
|
131
|
+
// takeover even after Supabase has selected this device as Hub.
|
|
132
|
+
LIVEDESK_RESTART_WAIT_PID: String(waitPid),
|
|
133
|
+
LIVEDESK_ROLE_TAKEOVER: normalizedRole === 'hub' ? '1' : '',
|
|
134
|
+
LIVEDESK_SKIP_BROWSER_OPEN: '1'
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function spawnUnifiedRoleRestart(role) {
|
|
139
|
+
if (isTruthy(process.env.LIVEDESK_DESKTOP_HOST)) {
|
|
140
|
+
console.log(`[LiveDesk] Desktop supervisor will restart the runtime as ${role}.`);
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
const unifiedEntry = String(process.env.LIVEDESK_UNIFIED_LAUNCHER_ENTRY || '').trim();
|
|
144
|
+
const restartEnvironment = buildUnifiedRoleRestartEnvironment(role);
|
|
145
|
+
if (!unifiedEntry || !existsSync(unifiedEntry)) {
|
|
146
|
+
throw new Error(`LiveDesk unified role launcher is unavailable: ${unifiedEntry || 'missing path'}`);
|
|
147
|
+
}
|
|
148
|
+
const handoff = await startRoleTransitionSupervisor({
|
|
149
|
+
role,
|
|
150
|
+
previousPid: process.pid,
|
|
151
|
+
launcherEntry: unifiedEntry,
|
|
152
|
+
stateDir: UNIFIED_CLIENT_STATE_DIR,
|
|
153
|
+
runtimePort: Number(process.env.LIVEDESK_CLIENT_RUNTIME_PORT || DEFAULT_AUTH_CALLBACK_PORT),
|
|
154
|
+
remotePort: resolveHubClientPort(),
|
|
155
|
+
env: restartEnvironment,
|
|
156
|
+
cwd: process.cwd()
|
|
157
|
+
});
|
|
158
|
+
console.log(
|
|
159
|
+
`[LiveDesk] Role transition supervisor claimed target=${role} `
|
|
160
|
+
+ `pid=${handoff.supervisorPid}. Diagnostics: ${handoff.logPath}`
|
|
161
|
+
);
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
139
164
|
|
|
140
165
|
function printHelp() {
|
|
141
166
|
process.stdout.write(`
|
|
@@ -349,15 +374,64 @@ function parseLauncherArgs(argv) {
|
|
|
349
374
|
};
|
|
350
375
|
}
|
|
351
376
|
|
|
352
|
-
function normalizePort(value) {
|
|
377
|
+
function normalizePort(value) {
|
|
353
378
|
const port = Number(String(value || '').trim());
|
|
354
379
|
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
355
380
|
return 0;
|
|
356
381
|
}
|
|
357
382
|
return port;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
function
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export function resolveHubClientPort(env = process.env) {
|
|
386
|
+
return normalizePort(env.REMOTE_HUB_PORT || env.LIVEDESK_HUB_REMOTE_PORT) || DEFAULT_HUB_CLIENT_PORT;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export function preflightHubClientPort(port = resolveHubClientPort()) {
|
|
390
|
+
const normalizedPort = normalizePort(port);
|
|
391
|
+
if (!normalizedPort) {
|
|
392
|
+
return Promise.resolve({ ok: false, port: Number(port) || 0, code: 'invalid-hub-client-port' });
|
|
393
|
+
}
|
|
394
|
+
return new Promise(resolvePreflight => {
|
|
395
|
+
const probe = net.createServer();
|
|
396
|
+
let completed = false;
|
|
397
|
+
const finish = result => {
|
|
398
|
+
if (completed) return;
|
|
399
|
+
completed = true;
|
|
400
|
+
resolvePreflight({ port: normalizedPort, ...result });
|
|
401
|
+
};
|
|
402
|
+
probe.unref?.();
|
|
403
|
+
probe.once('error', error => {
|
|
404
|
+
finish({
|
|
405
|
+
ok: false,
|
|
406
|
+
code: error?.code === 'EADDRINUSE' ? 'hub-client-port-in-use' : 'hub-client-port-unavailable',
|
|
407
|
+
systemCode: String(error?.code || '')
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
probe.listen({ host: '0.0.0.0', port: normalizedPort, exclusive: true }, () => {
|
|
411
|
+
probe.close(error => {
|
|
412
|
+
if (error) {
|
|
413
|
+
finish({ ok: false, code: 'hub-client-port-unavailable', systemCode: String(error?.code || '') });
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
finish({ ok: true, code: 'ok' });
|
|
417
|
+
});
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function hubClientPortPreflightError(preflight) {
|
|
423
|
+
const port = Number(preflight?.port || resolveHubClientPort());
|
|
424
|
+
return {
|
|
425
|
+
ok: false,
|
|
426
|
+
code: String(preflight?.code || 'hub-client-port-unavailable'),
|
|
427
|
+
port,
|
|
428
|
+
error: preflight?.code === 'hub-client-port-in-use'
|
|
429
|
+
? `LiveDesk 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.`
|
|
430
|
+
: `LiveDesk 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.`
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function normalizeSlotNumber(value) {
|
|
361
435
|
const number = Number(String(value || '').trim());
|
|
362
436
|
if (!Number.isInteger(number) || number < 1 || number > 999) {
|
|
363
437
|
return '';
|
|
@@ -2628,12 +2702,18 @@ async function startConnectionChoiceServer(supabase, options = {}) {
|
|
|
2628
2702
|
payload = Object.fromEntries(params.entries());
|
|
2629
2703
|
}
|
|
2630
2704
|
const nextRole = String(payload.role || '').trim().toLowerCase();
|
|
2631
|
-
if (nextRole !== 'hub') {
|
|
2705
|
+
if (nextRole !== 'hub') {
|
|
2632
2706
|
res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8', 'Access-Control-Allow-Origin': '*' });
|
|
2633
2707
|
res.end(JSON.stringify({ ok: false, error: 'client-can-only-transition-to-hub' }));
|
|
2634
|
-
return;
|
|
2635
|
-
}
|
|
2636
|
-
const
|
|
2708
|
+
return;
|
|
2709
|
+
}
|
|
2710
|
+
const portPreflight = await preflightHubClientPort();
|
|
2711
|
+
if (!portPreflight.ok) {
|
|
2712
|
+
res.writeHead(409, { 'Content-Type': 'application/json; charset=utf-8', 'Access-Control-Allow-Origin': '*' });
|
|
2713
|
+
res.end(JSON.stringify(hubClientPortPreflightError(portPreflight)));
|
|
2714
|
+
return;
|
|
2715
|
+
}
|
|
2716
|
+
const session = await refreshSessionIfNeeded(supabase);
|
|
2637
2717
|
const expectedRoleVersion = Number(dashboardState.roleVersion || 0);
|
|
2638
2718
|
const { data, error } = await supabase.rpc('set_livedesk_device_role', {
|
|
2639
2719
|
p_device_id: deviceId,
|
|
@@ -2928,6 +3008,10 @@ async function chooseClientConnection(supabase, options = {}) {
|
|
|
2928
3008
|
if (!supabase) {
|
|
2929
3009
|
return { ok: false, error: 'supabase-session-required' };
|
|
2930
3010
|
}
|
|
3011
|
+
const portPreflight = await preflightHubClientPort();
|
|
3012
|
+
if (!portPreflight.ok) {
|
|
3013
|
+
return hubClientPortPreflightError(portPreflight);
|
|
3014
|
+
}
|
|
2931
3015
|
const session = await refreshSessionIfNeeded(supabase);
|
|
2932
3016
|
if (!session?.access_token) {
|
|
2933
3017
|
return { ok: false, error: 'supabase-session-required' };
|
|
@@ -3930,12 +4014,12 @@ export async function runClientRuntime(argv = process.argv.slice(2)) {
|
|
|
3930
4014
|
while (true) {
|
|
3931
4015
|
const prepared = await prepareLoginConnection(parsed, connectionPage);
|
|
3932
4016
|
connectionPage = prepared.connectionPage || connectionPage;
|
|
3933
|
-
const roleRestartBeforeAgent = connectionPage?.getRoleRestartRequest?.() || roleRestartRequest;
|
|
3934
|
-
if (roleRestartBeforeAgent?.role) {
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
process.exit(0);
|
|
4017
|
+
const roleRestartBeforeAgent = connectionPage?.getRoleRestartRequest?.() || roleRestartRequest;
|
|
4018
|
+
if (roleRestartBeforeAgent?.role) {
|
|
4019
|
+
if (isTruthy(process.env.LIVEDESK_DESKTOP_HOST)) process.exit(43);
|
|
4020
|
+
await spawnUnifiedRoleRestart(roleRestartBeforeAgent.role);
|
|
4021
|
+
connectionPage?.close?.();
|
|
4022
|
+
process.exit(0);
|
|
3939
4023
|
}
|
|
3940
4024
|
const fastRuntime = getFastRuntime();
|
|
3941
4025
|
const useFast = shouldTryFast(prepared, fastRuntime);
|
|
@@ -4024,12 +4108,12 @@ export async function runClientRuntime(argv = process.argv.slice(2)) {
|
|
|
4024
4108
|
});
|
|
4025
4109
|
});
|
|
4026
4110
|
}
|
|
4027
|
-
const requestedRoleRestart = connectionPage?.getRoleRestartRequest?.();
|
|
4028
|
-
if (requestedRoleRestart?.role) {
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
process.exit(0);
|
|
4111
|
+
const requestedRoleRestart = connectionPage?.getRoleRestartRequest?.();
|
|
4112
|
+
if (requestedRoleRestart?.role) {
|
|
4113
|
+
if (isTruthy(process.env.LIVEDESK_DESKTOP_HOST)) process.exit(43);
|
|
4114
|
+
await spawnUnifiedRoleRestart(requestedRoleRestart.role);
|
|
4115
|
+
connectionPage?.close?.();
|
|
4116
|
+
process.exit(0);
|
|
4033
4117
|
}
|
|
4034
4118
|
if (result?.signal) {
|
|
4035
4119
|
connectionPage?.close?.();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livedesk/client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.174",
|
|
4
4
|
"description": "LiveDesk local remote client",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -39,10 +39,10 @@
|
|
|
39
39
|
"ws": "^8.18.3"
|
|
40
40
|
},
|
|
41
41
|
"optionalDependencies": {
|
|
42
|
-
"@livedesk/fast-linux-x64": "0.1.
|
|
43
|
-
"@livedesk/fast-osx-arm64": "0.1.
|
|
44
|
-
"@livedesk/fast-osx-x64": "0.1.
|
|
45
|
-
"@livedesk/fast-win-x64": "0.1.
|
|
42
|
+
"@livedesk/fast-linux-x64": "0.1.380",
|
|
43
|
+
"@livedesk/fast-osx-arm64": "0.1.380",
|
|
44
|
+
"@livedesk/fast-osx-x64": "0.1.380",
|
|
45
|
+
"@livedesk/fast-win-x64": "0.1.380"
|
|
46
46
|
},
|
|
47
47
|
"publishConfig": {
|
|
48
48
|
"access": "public"
|
|
@@ -739,6 +739,7 @@ export function createClientRuntimeServer(options = {}) {
|
|
|
739
739
|
osVersion: os.release(),
|
|
740
740
|
pid: process.pid,
|
|
741
741
|
cpuUsagePercent: Math.round(cpuUsagePercent * 10) / 10,
|
|
742
|
+
cpuLogicalCores: Math.max(1, os.cpus().length),
|
|
742
743
|
memoryTotalBytes: totalMemoryBytes,
|
|
743
744
|
memoryUsedBytes: Math.max(0, totalMemoryBytes - freeMemoryBytes),
|
|
744
745
|
uptimeSeconds: Math.max(0, Math.floor(os.uptime())),
|