@livedesk/client 0.1.169 → 0.1.170
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-node.js +285 -79
- package/bin/livedesk-client.js +102 -12
- package/package.json +5 -5
|
@@ -9,14 +9,15 @@ import { spawn } from 'child_process';
|
|
|
9
9
|
import { createRequire } from 'node:module';
|
|
10
10
|
|
|
11
11
|
const require = createRequire(import.meta.url);
|
|
12
|
-
const AGENT_VERSION = (() => {
|
|
12
|
+
const AGENT_VERSION = (() => {
|
|
13
13
|
try {
|
|
14
14
|
return String(require('../package.json').version || '0.0.0');
|
|
15
15
|
} catch {
|
|
16
16
|
return '0.0.0';
|
|
17
|
-
}
|
|
18
|
-
})();
|
|
19
|
-
const
|
|
17
|
+
}
|
|
18
|
+
})();
|
|
19
|
+
const PRODUCT_VERSION = String(process.env.LIVEDESK_NPM_LAUNCHER_VERSION || AGENT_VERSION).trim() || AGENT_VERSION;
|
|
20
|
+
const DEFAULT_MANAGER = '127.0.0.1:5197';
|
|
20
21
|
const DEFAULT_HEARTBEAT_MS = 5000;
|
|
21
22
|
const DEFAULT_RECONNECT_MS = 5000;
|
|
22
23
|
const EXIT_INVALID_PAIR_TOKEN = 23;
|
|
@@ -29,7 +30,118 @@ const MAX_AI_OUTPUT_CHARS = 6000;
|
|
|
29
30
|
const MAX_FILE_TRANSFER_FILES = 24;
|
|
30
31
|
const MAX_FILE_TRANSFER_BYTES = 24 * 1024 * 1024;
|
|
31
32
|
const MAX_AGENT_OUTPUT_CHARS = 32000;
|
|
32
|
-
const launchedProcessRegistry = new Map();
|
|
33
|
+
const launchedProcessRegistry = new Map();
|
|
34
|
+
const CLIENT_UPDATE_VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
35
|
+
|
|
36
|
+
function normalizeClientUpdateTargetVersion(value) {
|
|
37
|
+
const version = String(value || '').trim().replace(/^v/i, '');
|
|
38
|
+
if (!version) return '';
|
|
39
|
+
if (!CLIENT_UPDATE_VERSION_PATTERN.test(version)) {
|
|
40
|
+
throw new Error(`Invalid LiveDesk product update version: ${version}`);
|
|
41
|
+
}
|
|
42
|
+
return version;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getClientUpdateStatePath() {
|
|
46
|
+
return String(process.env.LIVEDESK_CLIENT_UPDATE_STATE_PATH || path.join(os.homedir(), '.livedesk', 'client-update.json'));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function writeClientUpdateState(stage, details = {}) {
|
|
50
|
+
const statePath = getClientUpdateStatePath();
|
|
51
|
+
const operationId = String(details.operationId || process.env.LIVEDESK_CLIENT_UPDATE_OPERATION_ID || '').trim();
|
|
52
|
+
if (!operationId) return;
|
|
53
|
+
let previous = null;
|
|
54
|
+
try {
|
|
55
|
+
previous = JSON.parse(await fs.readFile(statePath, 'utf8'));
|
|
56
|
+
} catch {
|
|
57
|
+
// The first update stage creates the file.
|
|
58
|
+
}
|
|
59
|
+
const replacesTerminalOperation = previous?.operationId
|
|
60
|
+
&& previous.operationId !== operationId
|
|
61
|
+
&& stage === 'scheduled'
|
|
62
|
+
&& ['connected', 'failed', 'restored'].includes(String(previous.stage || ''));
|
|
63
|
+
if (previous?.operationId && previous.operationId !== operationId && !replacesTerminalOperation) return;
|
|
64
|
+
const targetProductVersion = String(
|
|
65
|
+
details.targetProductVersion
|
|
66
|
+
|| process.env.LIVEDESK_CLIENT_UPDATE_TARGET_PRODUCT_VERSION
|
|
67
|
+
|| (!replacesTerminalOperation ? previous?.targetProductVersion : '')
|
|
68
|
+
|| ''
|
|
69
|
+
).trim();
|
|
70
|
+
const targetAgentVersion = String(
|
|
71
|
+
details.targetAgentVersion
|
|
72
|
+
|| details.targetVersion
|
|
73
|
+
|| process.env.LIVEDESK_CLIENT_UPDATE_TARGET_AGENT_VERSION
|
|
74
|
+
|| process.env.LIVEDESK_CLIENT_UPDATE_TARGET_VERSION
|
|
75
|
+
|| (!replacesTerminalOperation ? previous?.targetAgentVersion : '')
|
|
76
|
+
|| (!replacesTerminalOperation ? previous?.targetVersion : '')
|
|
77
|
+
|| ''
|
|
78
|
+
).trim();
|
|
79
|
+
const state = {
|
|
80
|
+
...(!replacesTerminalOperation && previous && typeof previous === 'object' ? previous : {}),
|
|
81
|
+
operationId,
|
|
82
|
+
stage,
|
|
83
|
+
targetVersion: targetAgentVersion,
|
|
84
|
+
targetProductVersion,
|
|
85
|
+
targetAgentVersion,
|
|
86
|
+
productVersion: PRODUCT_VERSION,
|
|
87
|
+
agentVersion: AGENT_VERSION,
|
|
88
|
+
agentPid: process.pid,
|
|
89
|
+
launcherPid: Number(process.env.LIVEDESK_CLIENT_PARENT_PID || 0) || 0,
|
|
90
|
+
attemptId: String(details.attemptId || process.env.LIVEDESK_CLIENT_UPDATE_ATTEMPT_ID || '').trim(),
|
|
91
|
+
updatedAt: new Date().toISOString(),
|
|
92
|
+
...details
|
|
93
|
+
};
|
|
94
|
+
await fs.mkdir(path.dirname(statePath), { recursive: true });
|
|
95
|
+
const temporaryPath = `${statePath}.${process.pid}.${crypto.randomBytes(6).toString('hex')}.tmp`;
|
|
96
|
+
try {
|
|
97
|
+
await fs.writeFile(temporaryPath, JSON.stringify(state, null, 2), { encoding: 'utf8', mode: 0o600 });
|
|
98
|
+
await fs.rename(temporaryPath, statePath);
|
|
99
|
+
} finally {
|
|
100
|
+
await fs.rm(temporaryPath, { force: true }).catch(() => undefined);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function waitForClientUpdateShutdownSignal(operationId, timeoutMs = 150_000) {
|
|
105
|
+
const statePath = getClientUpdateStatePath();
|
|
106
|
+
const deadline = Date.now() + timeoutMs;
|
|
107
|
+
while (Date.now() < deadline) {
|
|
108
|
+
try {
|
|
109
|
+
const state = JSON.parse(await fs.readFile(statePath, 'utf8'));
|
|
110
|
+
if (state?.operationId !== operationId) {
|
|
111
|
+
await wait(200);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (state.stage === 'waiting-for-shutdown') return true;
|
|
115
|
+
if (state.stage === 'failed') {
|
|
116
|
+
console.error(`LiveDesk client update preparation failed: ${state.error || 'unknown error'}`);
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
} catch {
|
|
120
|
+
// The detached supervisor may still be writing its first stage.
|
|
121
|
+
}
|
|
122
|
+
await wait(200);
|
|
123
|
+
}
|
|
124
|
+
await writeClientUpdateState('failed', {
|
|
125
|
+
operationId,
|
|
126
|
+
error: 'Timed out waiting for the LiveDesk update supervisor to prepare the exact package.'
|
|
127
|
+
}).catch(() => undefined);
|
|
128
|
+
console.error('LiveDesk client update preparation timed out; the current Agent will remain running.');
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function markClientUpdateConnected() {
|
|
133
|
+
const operationId = String(process.env.LIVEDESK_CLIENT_UPDATE_OPERATION_ID || '').trim();
|
|
134
|
+
if (!operationId) return;
|
|
135
|
+
void writeClientUpdateState('connected', {
|
|
136
|
+
operationId,
|
|
137
|
+
attemptId: String(process.env.LIVEDESK_CLIENT_UPDATE_ATTEMPT_ID || '').trim(),
|
|
138
|
+
connectedAt: new Date().toISOString(),
|
|
139
|
+
restartVerified: true,
|
|
140
|
+
error: ''
|
|
141
|
+
}).catch(error => {
|
|
142
|
+
console.error(`LiveDesk client update diagnostics could not be persisted: ${error?.message || error}`);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
33
145
|
|
|
34
146
|
function printHelp() {
|
|
35
147
|
console.log(`
|
|
@@ -1318,76 +1430,165 @@ function remotePolicyAllows(options, command) {
|
|
|
1318
1430
|
return denied ? { ok: false, error: `${denied}-blocked-by-settings` } : { ok: true };
|
|
1319
1431
|
}
|
|
1320
1432
|
|
|
1321
|
-
function buildClientUpdateBootstrapScript() {
|
|
1322
|
-
return [
|
|
1323
|
-
'const { spawn } = require("node:child_process");',
|
|
1433
|
+
function buildClientUpdateBootstrapScript() {
|
|
1434
|
+
return [
|
|
1435
|
+
'const { spawn, spawnSync } = require("node:child_process");',
|
|
1324
1436
|
'const fs = require("node:fs");',
|
|
1325
1437
|
'const path = require("node:path");',
|
|
1438
|
+
'const crypto = require("node:crypto");',
|
|
1326
1439
|
'const waitPid = Number(process.env.LIVEDESK_UPDATE_WAIT_PID || 0);',
|
|
1327
1440
|
'const agentPid = Number(process.env.LIVEDESK_UPDATE_AGENT_PID || 0);',
|
|
1328
|
-
'const
|
|
1329
|
-
'const
|
|
1330
|
-
'const
|
|
1331
|
-
'const
|
|
1441
|
+
'const operationId = String(process.env.LIVEDESK_CLIENT_UPDATE_OPERATION_ID || "").trim();',
|
|
1442
|
+
'const statePath = String(process.env.LIVEDESK_CLIENT_UPDATE_STATE_PATH || path.join(require("node:os").homedir(), ".livedesk", "client-update.json"));',
|
|
1443
|
+
'const logPath = statePath.replace(/\\.json$/i, "") + ".log";',
|
|
1444
|
+
'const cleanVersion = value => String(value || "").trim().replace(/^v/i, "");',
|
|
1445
|
+
'const versionPattern = /^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?(?:\\+[0-9A-Za-z.-]+)?$/;',
|
|
1446
|
+
'const targetProductVersion = cleanVersion(process.env.LIVEDESK_CLIENT_UPDATE_TARGET_PRODUCT_VERSION || process.env.LIVEDESK_CLIENT_UPDATE_TARGET_VERSION);',
|
|
1447
|
+
'const targetAgentVersion = cleanVersion(process.env.LIVEDESK_CLIENT_UPDATE_TARGET_AGENT_VERSION || process.env.LIVEDESK_CLIENT_UPDATE_TARGET_VERSION || targetProductVersion);',
|
|
1448
|
+
'const currentProductVersion = cleanVersion(process.env.LIVEDESK_CLIENT_UPDATE_CURRENT_PRODUCT_VERSION || process.env.LIVEDESK_NPM_LAUNCHER_VERSION);',
|
|
1449
|
+
'const currentAgentVersion = cleanVersion(process.env.LIVEDESK_CLIENT_UPDATE_CURRENT_AGENT_VERSION || process.env.LIVEDESK_CLIENT_PACKAGE_VERSION || currentProductVersion);',
|
|
1450
|
+
'for (const [label, version] of [["target product", targetProductVersion], ["target Agent", targetAgentVersion], ["current product", currentProductVersion], ["current Agent", currentAgentVersion]]) { if (!versionPattern.test(version)) throw new Error("Invalid LiveDesk " + label + " update version: " + version); }',
|
|
1451
|
+
'const packageSpec = "livedesk@" + targetProductVersion;',
|
|
1452
|
+
'const localLauncher = String(process.env.LIVEDESK_CLIENT_UPDATE_LOCAL_LAUNCHER || process.env.LIVEDESK_UNIFIED_LAUNCHER_ENTRY || "").trim();',
|
|
1453
|
+
'const preservedArgs = (() => { try { const decoded = JSON.parse(Buffer.from(String(process.env.LIVEDESK_CLIENT_UPDATE_ARGS_BASE64 || ""), "base64").toString("utf8")); return Array.isArray(decoded) ? decoded.map(value => String(value)) : []; } catch { return []; } })();',
|
|
1454
|
+
'const proofTimeoutMs = Math.max(500, Number(process.env.LIVEDESK_CLIENT_UPDATE_PROOF_TIMEOUT_MS || 30000));',
|
|
1455
|
+
'const proofPollMs = Math.max(25, Number(process.env.LIVEDESK_CLIENT_UPDATE_PROOF_POLL_MS || 200));',
|
|
1456
|
+
'const killGraceMs = Math.max(50, Number(process.env.LIVEDESK_CLIENT_UPDATE_KILL_GRACE_MS || 1500));',
|
|
1457
|
+
'const targetLaunchLimit = 3;',
|
|
1458
|
+
'const restoreLaunchLimit = 2;',
|
|
1459
|
+
'const readState = () => { try { return JSON.parse(fs.readFileSync(statePath, "utf8")); } catch { return {}; } };',
|
|
1460
|
+
'const writeState = (stage, details = {}) => { fs.mkdirSync(path.dirname(statePath), { recursive: true }); const previous = readState(); const replacesTerminalOperation = previous.operationId && previous.operationId !== operationId && stage === "preparing-package" && ["connected", "failed", "restored"].includes(String(previous.stage || "")); if (previous.operationId && previous.operationId !== operationId && !replacesTerminalOperation) return false; const base = replacesTerminalOperation ? {} : previous; const next = { ...base, operationId, stage, targetVersion: targetAgentVersion, targetProductVersion, targetAgentVersion, currentProductVersion, currentAgentVersion, supervisorPid: process.pid, updatedAt: new Date().toISOString(), ...details }; const temporary = statePath + "." + process.pid + "." + Math.random().toString(16).slice(2) + ".tmp"; fs.writeFileSync(temporary, JSON.stringify(next, null, 2), { encoding: "utf8", mode: 0o600 }); fs.renameSync(temporary, statePath); return true; };',
|
|
1461
|
+
'const log = message => { try { fs.mkdirSync(path.dirname(logPath), { recursive: true }); fs.appendFileSync(logPath, new Date().toISOString() + " " + message + "\\n", "utf8"); } catch {} };',
|
|
1462
|
+
'const isAlive = pid => { try { process.kill(pid, 0); return true; } catch (error) { return error?.code !== "ESRCH"; } };',
|
|
1463
|
+
'const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));',
|
|
1464
|
+
'const waitForExit = async (pid, timeoutMs) => { const deadline = Date.now() + timeoutMs; while (pid > 1 && Date.now() < deadline) { if (!isAlive(pid)) return true; await sleep(250); } return pid <= 1 || !isAlive(pid); };',
|
|
1465
|
+
'const killProcessTree = async pid => { if (!Number.isInteger(pid) || pid <= 1 || pid === process.pid) return; if (process.platform === "win32") { spawnSync("taskkill.exe", ["/PID", String(pid), "/T", "/F"], { windowsHide: true, stdio: "ignore" }); } else { try { process.kill(-pid, "SIGTERM"); } catch { try { process.kill(pid, "SIGTERM"); } catch {} } await sleep(killGraceMs); try { process.kill(-pid, "SIGKILL"); } catch { try { process.kill(pid, "SIGKILL"); } catch {} } } await waitForExit(pid, killGraceMs); };',
|
|
1466
|
+
'const ensureExit = async (pid, label) => { if (pid <= 1 || await waitForExit(pid, 20000)) return; log(label + " pid=" + pid + " did not exit gracefully; terminating its process tree."); await killProcessTree(pid); if (isAlive(pid)) throw new Error(label + " process " + pid + " did not exit before restart."); };',
|
|
1467
|
+
'const nodeDir = path.dirname(process.execPath);',
|
|
1332
1468
|
'const npxCandidates = process.platform === "win32" ? [process.env.LIVEDESK_NPX_EXECUTABLE, path.join(nodeDir, "npx.cmd"), path.join(nodeDir, "npx.exe"), "npx.cmd"] : [process.env.LIVEDESK_NPX_EXECUTABLE, path.join(nodeDir, "npx"), "npx"];',
|
|
1333
1469
|
'const npx = npxCandidates.find(candidate => candidate && (!path.isAbsolute(candidate) || fs.existsSync(candidate)));',
|
|
1334
|
-
'if (!npx) throw new Error("LiveDesk npx executable was not found.");',
|
|
1470
|
+
'if (!npx) { writeState("failed", { error: "LiveDesk npx executable was not found." }); throw new Error("LiveDesk npx executable was not found."); }',
|
|
1335
1471
|
'const npmExecPath = String(process.env.npm_execpath || process.env.NPM_EXECPATH || "").trim();',
|
|
1336
1472
|
'const npxCliCandidates = [process.env.LIVEDESK_NPX_CLI_PATH, npmExecPath ? path.join(path.dirname(npmExecPath), "npx-cli.js") : "", path.join(nodeDir, "node_modules", "npm", "bin", "npx-cli.js")].filter(Boolean);',
|
|
1337
1473
|
'const npxCli = npxCliCandidates.find(candidate => fs.existsSync(candidate));',
|
|
1338
|
-
'const
|
|
1339
|
-
'const
|
|
1340
|
-
'const
|
|
1474
|
+
'const quoteCmd = value => "\\"" + String(value).replaceAll("%", "%%").replaceAll("\\"", "\\"\\"") + "\\"";',
|
|
1475
|
+
'const invocation = npxArgs => npxCli ? { command: process.execPath, args: [npxCli, ...npxArgs] } : process.platform === "win32" ? { command: process.env.ComSpec || "cmd.exe", args: ["/d", "/s", "/c", "call " + quoteCmd(npx) + " " + npxArgs.map(quoteCmd).join(" ")] } : { command: npx, args: npxArgs };',
|
|
1476
|
+
'const runToExit = (npxArgs, timeoutMs) => new Promise((resolve, reject) => { const call = invocation(npxArgs); const child = spawn(call.command, call.args, { env: process.env, windowsHide: true, stdio: ["ignore", "pipe", "pipe"] }); let stdout = ""; let stderr = ""; let settled = false; child.stdout?.on("data", chunk => { stdout = (stdout + chunk.toString()).slice(-65536); }); child.stderr?.on("data", chunk => { stderr = (stderr + chunk.toString()).slice(-65536); }); const timer = setTimeout(() => { if (settled) return; settled = true; void killProcessTree(child.pid || 0).finally(() => reject(new Error("LiveDesk package preparation timed out."))); }, timeoutMs); child.once("error", error => { if (settled) return; settled = true; clearTimeout(timer); reject(error); }); child.once("exit", (code, signal) => { if (settled) return; settled = true; clearTimeout(timer); resolve({ code: code ?? -1, signal, stdout, stderr }); }); });',
|
|
1477
|
+
'const attempts = [];',
|
|
1478
|
+
'const attemptIdFor = (kind, number) => operationId + "-" + kind + "-" + number + "-" + crypto.randomBytes(4).toString("hex");',
|
|
1479
|
+
'const updateAttempt = (attempt, patch) => { Object.assign(attempt, patch, { updatedAt: new Date().toISOString() }); return attempts.map(item => ({ ...item })); };',
|
|
1480
|
+
'const killAttempt = async (restartPid, attemptId) => { const state = readState(); const pids = new Set([Number(restartPid || 0)]); if (state.operationId === operationId && state.attemptId === attemptId) { pids.add(Number(state.launcherPid || 0)); pids.add(Number(state.agentPid || 0)); } for (const pid of pids) await killProcessTree(pid); };',
|
|
1481
|
+
'const launchAndVerify = async ({ kind, number, expectedProductVersion, expectedAgentVersion, call }) => { const attemptId = attemptIdFor(kind, number); const attempt = { attemptId, kind, number, status: "launching", expectedProductVersion, expectedAgentVersion, startedAt: new Date().toISOString() }; attempts.push(attempt); const attemptEnv = { ...process.env, LIVEDESK_CLIENT_UPDATE_ATTEMPT_ID: attemptId, LIVEDESK_CLIENT_UPDATE_ATTEMPT_KIND: kind, LIVEDESK_CLIENT_UPDATE_ATTEMPT_NUMBER: String(number), LIVEDESK_CLIENT_UPDATE_OPERATION_ID: operationId, LIVEDESK_CLIENT_UPDATE_TARGET_PRODUCT_VERSION: targetProductVersion, LIVEDESK_CLIENT_UPDATE_TARGET_AGENT_VERSION: targetAgentVersion, LIVEDESK_CLIENT_UPDATE_TARGET_VERSION: targetAgentVersion }; const logFd = fs.openSync(logPath, "a"); let child; try { child = spawn(call.command, call.args, { cwd: process.env.LIVEDESK_CLIENT_UPDATE_CWD || process.cwd(), env: attemptEnv, detached: true, windowsHide: true, stdio: ["ignore", logFd, logFd] }); await new Promise((resolve, reject) => { child.once("error", reject); child.once("spawn", resolve); }); } finally { fs.closeSync(logFd); } const restartPid = Number(child?.pid || 0); updateAttempt(attempt, { status: "waiting-for-connected-proof", restartPid }); writeState(kind + "-launched", { attemptId, attemptKind: kind, attemptNumber: number, restartPid, attempts: attempts.map(item => ({ ...item })), restartVerified: false, error: "" }); let exitResult = null; child.once("exit", (code, signal) => { exitResult = { code, signal }; }); const deadline = Date.now() + proofTimeoutMs; let proof = null; let failure = ""; while (Date.now() < deadline) { const state = readState(); if (state.operationId === operationId && state.attemptId === attemptId && state.stage === "connected") { const observedProduct = cleanVersion(state.productVersion); const observedAgent = cleanVersion(state.agentVersion); if (observedProduct === expectedProductVersion && observedAgent === expectedAgentVersion) { proof = state; break; } failure = "Connected version mismatch expected product " + expectedProductVersion + " / Agent " + expectedAgentVersion + ", observed product " + (observedProduct || "unknown") + " / Agent " + (observedAgent || "unknown") + "."; break; } if (exitResult) { failure = "Launcher exited before exact connected proof (code=" + (exitResult.code ?? "none") + ", signal=" + (exitResult.signal || "none") + ")."; break; } await sleep(proofPollMs); } if (proof) { updateAttempt(attempt, { status: "connected", connectedAt: proof.connectedAt || new Date().toISOString(), productVersion: cleanVersion(proof.productVersion), agentVersion: cleanVersion(proof.agentVersion) }); if (child && !exitResult) child.unref(); writeState(kind === "target" ? "connected" : "restored", { attemptId, attemptKind: kind, attemptNumber: number, restartPid, launcherPid: Number(proof.launcherPid || 0), agentPid: Number(proof.agentPid || 0), productVersion: cleanVersion(proof.productVersion), agentVersion: cleanVersion(proof.agentVersion), attempts: attempts.map(item => ({ ...item })), restartVerified: kind === "target", restored: kind === "restore", connectedAt: proof.connectedAt || new Date().toISOString(), error: kind === "restore" ? "Target update failed; previous local Client was restored." : "" }); return true; } failure = failure || "Timed out waiting for exact connected proof for " + attemptId + "."; await killAttempt(restartPid, attemptId); updateAttempt(attempt, { status: "failed", failedAt: new Date().toISOString(), killed: true, error: failure }); writeState(kind + "-attempt-failed", { attemptId, attemptKind: kind, attemptNumber: number, restartPid, attempts: attempts.map(item => ({ ...item })), restartVerified: false, error: failure }); log(attemptId + " failed: " + failure); return false; };',
|
|
1341
1482
|
'process.env.LIVEDESK_SKIP_BROWSER_OPEN = "1";',
|
|
1342
|
-
'(async () => { await
|
|
1343
|
-
''
|
|
1344
|
-
].join('\n');
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
function scheduleClientUpdate(options, payload = {}) {
|
|
1348
|
-
const
|
|
1349
|
-
const
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1483
|
+
'(async () => { writeState("preparing-package", { packageSpec, attempts: [], error: "" }); log("preparing " + packageSpec); const prepared = await runToExit(["-y", "--prefer-online", packageSpec, "--version"], 120000); if (prepared.code !== 0) throw new Error("LiveDesk package preparation failed (exit=" + prepared.code + "): " + (prepared.stderr || prepared.stdout).trim().slice(-2000)); const stdoutVersions = prepared.stdout.split(/\\r?\\n/).map(line => line.trim()).filter(line => versionPattern.test(line)); if (!stdoutVersions.includes(targetProductVersion)) throw new Error("Prepared LiveDesk stdout did not report requested product version " + targetProductVersion + ". stdout=" + prepared.stdout.trim().slice(-1000)); writeState("waiting-for-shutdown", { observedVersion: targetProductVersion, observedProductVersion: targetProductVersion, preparedAt: new Date().toISOString(), attempts: [], error: "" }); log("package ready product=" + targetProductVersion + "; waiting for current runtime shutdown"); await ensureExit(agentPid, "Agent"); await ensureExit(waitPid, "Launcher"); const roleArgs = ["--force-role", "client", "--no-login", "--no-open", ...preservedArgs]; const targetCall = invocation(["-y", "--prefer-online", packageSpec, ...roleArgs]); for (let number = 1; number <= targetLaunchLimit; number += 1) { if (await launchAndVerify({ kind: "target", number, expectedProductVersion: targetProductVersion, expectedAgentVersion: targetAgentVersion, call: targetCall })) { log("target connected with exact product " + targetProductVersion + " / Agent " + targetAgentVersion); return; } } writeState("restoring", { attempts: attempts.map(item => ({ ...item })), error: "All target launch attempts failed; restoring previous local Client." }); const restoreCall = localLauncher && fs.existsSync(localLauncher) ? { command: process.execPath, args: [localLauncher, ...roleArgs] } : invocation(["-y", "--prefer-offline", "livedesk@" + currentProductVersion, ...roleArgs]); for (let number = 1; number <= restoreLaunchLimit; number += 1) { if (await launchAndVerify({ kind: "restore", number, expectedProductVersion: currentProductVersion, expectedAgentVersion: currentAgentVersion, call: restoreCall })) { log("previous local Client restored at product " + currentProductVersion + " / Agent " + currentAgentVersion); process.exitCode = 1; return; } } throw new Error("Target update failed after 3 attempts and previous local Client restoration failed after 2 attempts."); })().catch(error => { const message = error?.stack || error?.message || String(error); log("failed: " + message); try { writeState("failed", { attempts: attempts.map(item => ({ ...item })), error: String(error?.message || error).slice(0, 4000), failedAt: new Date().toISOString(), restartVerified: false }); } catch {} process.exitCode = 1; });',
|
|
1484
|
+
''
|
|
1485
|
+
].join('\n');
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
async function scheduleClientUpdate(options, payload = {}) {
|
|
1489
|
+
const operationId = String(payload.operationId || crypto.randomUUID()).trim();
|
|
1490
|
+
const statePath = getClientUpdateStatePath();
|
|
1491
|
+
let targetProductVersion = '';
|
|
1492
|
+
let targetAgentVersion = '';
|
|
1493
|
+
try {
|
|
1494
|
+
targetProductVersion = normalizeClientUpdateTargetVersion(
|
|
1495
|
+
payload.targetProductVersion
|
|
1496
|
+
|| payload.productVersion
|
|
1497
|
+
|| payload.managerVersion
|
|
1498
|
+
|| payload.targetVersion
|
|
1499
|
+
|| process.env.LIVEDESK_CLIENT_UPDATE_TARGET_PRODUCT_VERSION
|
|
1500
|
+
|| process.env.LIVEDESK_CLIENT_UPDATE_TARGET_VERSION
|
|
1501
|
+
|| ''
|
|
1502
|
+
);
|
|
1503
|
+
targetAgentVersion = normalizeClientUpdateTargetVersion(
|
|
1504
|
+
payload.targetAgentVersion
|
|
1505
|
+
|| payload.clientVersion
|
|
1506
|
+
|| payload.targetVersion
|
|
1507
|
+
|| process.env.LIVEDESK_CLIENT_UPDATE_TARGET_AGENT_VERSION
|
|
1508
|
+
|| process.env.LIVEDESK_CLIENT_UPDATE_TARGET_VERSION
|
|
1509
|
+
|| targetProductVersion
|
|
1510
|
+
|| ''
|
|
1511
|
+
);
|
|
1512
|
+
await writeClientUpdateState('scheduled', {
|
|
1513
|
+
operationId,
|
|
1514
|
+
targetVersion: targetAgentVersion,
|
|
1515
|
+
targetProductVersion,
|
|
1516
|
+
targetAgentVersion,
|
|
1517
|
+
requestedAt: new Date().toISOString(),
|
|
1518
|
+
restartVerified: false,
|
|
1519
|
+
error: ''
|
|
1520
|
+
});
|
|
1521
|
+
const configuredParentPid = Number(process.env.LIVEDESK_CLIENT_PARENT_PID || process.ppid);
|
|
1522
|
+
const parentPid = Number.isInteger(configuredParentPid) && configuredParentPid > 1
|
|
1523
|
+
? configuredParentPid
|
|
1524
|
+
: process.pid;
|
|
1525
|
+
if (!Number.isInteger(parentPid) || parentPid <= 1) {
|
|
1526
|
+
throw new Error('LiveDesk client launcher process id is unavailable.');
|
|
1527
|
+
}
|
|
1528
|
+
const nodeBinDir = path.dirname(process.execPath);
|
|
1529
|
+
const npxCandidates = process.platform === 'win32'
|
|
1530
|
+
? [process.env.LIVEDESK_NPX_EXECUTABLE, path.join(nodeBinDir, 'npx.cmd'), path.join(nodeBinDir, 'npx.exe'), 'npx.cmd']
|
|
1531
|
+
: [process.env.LIVEDESK_NPX_EXECUTABLE, path.join(nodeBinDir, 'npx'), 'npx'];
|
|
1532
|
+
const command = npxCandidates.find(candidate => candidate && (!path.isAbsolute(candidate) || existsSync(candidate)));
|
|
1533
|
+
if (!command) {
|
|
1534
|
+
throw new Error('LiveDesk npx executable was not found.');
|
|
1535
|
+
}
|
|
1536
|
+
return await new Promise((resolve, reject) => {
|
|
1537
|
+
const child = spawn(process.execPath, ['-e', buildClientUpdateBootstrapScript()], {
|
|
1538
|
+
env: {
|
|
1539
|
+
...process.env,
|
|
1540
|
+
LIVEDESK_NODE_EXECUTABLE: process.execPath,
|
|
1541
|
+
LIVEDESK_NPX_EXECUTABLE: command,
|
|
1542
|
+
LIVEDESK_UPDATE_WAIT_PID: String(parentPid),
|
|
1543
|
+
LIVEDESK_UPDATE_AGENT_PID: String(process.pid),
|
|
1544
|
+
LIVEDESK_SKIP_BROWSER_OPEN: '1',
|
|
1545
|
+
LIVEDESK_CLIENT_UPDATE_TARGET_VERSION: targetAgentVersion,
|
|
1546
|
+
LIVEDESK_CLIENT_UPDATE_TARGET_PRODUCT_VERSION: targetProductVersion,
|
|
1547
|
+
LIVEDESK_CLIENT_UPDATE_TARGET_AGENT_VERSION: targetAgentVersion,
|
|
1548
|
+
LIVEDESK_CLIENT_UPDATE_CURRENT_PRODUCT_VERSION: PRODUCT_VERSION,
|
|
1549
|
+
LIVEDESK_CLIENT_UPDATE_CURRENT_AGENT_VERSION: AGENT_VERSION,
|
|
1550
|
+
LIVEDESK_CLIENT_UPDATE_LOCAL_LAUNCHER: String(
|
|
1551
|
+
process.env.LIVEDESK_UNIFIED_LAUNCHER_ENTRY
|
|
1552
|
+
|| process.env.LIVEDESK_CLIENT_UPDATE_LOCAL_LAUNCHER
|
|
1553
|
+
|| ''
|
|
1554
|
+
),
|
|
1555
|
+
LIVEDESK_CLIENT_UPDATE_OPERATION_ID: operationId,
|
|
1556
|
+
LIVEDESK_CLIENT_UPDATE_STATE_PATH: statePath,
|
|
1557
|
+
LIVEDESK_CLIENT_UPDATE_CWD: process.cwd()
|
|
1558
|
+
},
|
|
1559
|
+
detached: true,
|
|
1560
|
+
stdio: 'ignore',
|
|
1561
|
+
windowsHide: true
|
|
1562
|
+
});
|
|
1563
|
+
child.once('error', reject);
|
|
1564
|
+
child.once('spawn', () => {
|
|
1565
|
+
child.unref();
|
|
1566
|
+
resolve({
|
|
1567
|
+
ok: true,
|
|
1568
|
+
status: 'update-scheduled',
|
|
1569
|
+
targetVersion: targetAgentVersion,
|
|
1570
|
+
targetProductVersion,
|
|
1571
|
+
targetAgentVersion,
|
|
1572
|
+
operationId,
|
|
1573
|
+
statePath,
|
|
1574
|
+
parentPid,
|
|
1575
|
+
restartAfterMs: 0
|
|
1576
|
+
});
|
|
1577
|
+
});
|
|
1578
|
+
});
|
|
1579
|
+
} catch (error) {
|
|
1580
|
+
await writeClientUpdateState('failed', {
|
|
1581
|
+
operationId,
|
|
1582
|
+
targetVersion: targetAgentVersion,
|
|
1583
|
+
targetProductVersion,
|
|
1584
|
+
targetAgentVersion,
|
|
1585
|
+
error: error?.message || String(error),
|
|
1586
|
+
failedAt: new Date().toISOString(),
|
|
1587
|
+
restartVerified: false
|
|
1588
|
+
}).catch(() => undefined);
|
|
1589
|
+
throw error;
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1391
1592
|
|
|
1392
1593
|
async function handleRemoteCommand(socket, options, message, nextFrameSeq, activeStreams) {
|
|
1393
1594
|
const command = String(message.command || '');
|
|
@@ -1414,16 +1615,18 @@ async function handleRemoteCommand(socket, options, message, nextFrameSeq, activ
|
|
|
1414
1615
|
return;
|
|
1415
1616
|
}
|
|
1416
1617
|
|
|
1417
|
-
if (command === 'livedesk.client-update') {
|
|
1418
|
-
try {
|
|
1419
|
-
const result = await scheduleClientUpdate(options, message.payload || {});
|
|
1618
|
+
if (command === 'livedesk.client-update') {
|
|
1619
|
+
try {
|
|
1620
|
+
const result = await scheduleClientUpdate(options, message.payload || {});
|
|
1420
1621
|
writeJsonLine(socket, {
|
|
1421
1622
|
type: 'command.result',
|
|
1422
|
-
commandId: message.commandId,
|
|
1423
|
-
result
|
|
1424
|
-
});
|
|
1425
|
-
|
|
1426
|
-
|
|
1623
|
+
commandId: message.commandId,
|
|
1624
|
+
result
|
|
1625
|
+
});
|
|
1626
|
+
if (await waitForClientUpdateShutdownSignal(result.operationId)) {
|
|
1627
|
+
process.exit(EXIT_CLIENT_UPDATE);
|
|
1628
|
+
}
|
|
1629
|
+
} catch (error) {
|
|
1427
1630
|
writeJsonLine(socket, {
|
|
1428
1631
|
type: 'command.result',
|
|
1429
1632
|
commandId: message.commandId,
|
|
@@ -1751,9 +1954,10 @@ function connectOnce(options, deviceId) {
|
|
|
1751
1954
|
hostname: os.hostname(),
|
|
1752
1955
|
platform: os.platform(),
|
|
1753
1956
|
arch: os.arch(),
|
|
1754
|
-
pid: process.pid,
|
|
1755
|
-
agentVersion: AGENT_VERSION,
|
|
1756
|
-
|
|
1957
|
+
pid: process.pid,
|
|
1958
|
+
agentVersion: AGENT_VERSION,
|
|
1959
|
+
productVersion: PRODUCT_VERSION,
|
|
1960
|
+
capabilities: {
|
|
1757
1961
|
status: true,
|
|
1758
1962
|
thumbnail: options.thumbnailEnabled,
|
|
1759
1963
|
liveStream: options.liveEnabled,
|
|
@@ -1768,7 +1972,8 @@ function connectOnce(options, deviceId) {
|
|
|
1768
1972
|
fileTransferMaxBytes: MAX_FILE_TRANSFER_BYTES,
|
|
1769
1973
|
computerAgent: options.taskEnabled,
|
|
1770
1974
|
taskDispatch: options.taskEnabled,
|
|
1771
|
-
clientUpdate: true,
|
|
1975
|
+
clientUpdate: true,
|
|
1976
|
+
productVersion: PRODUCT_VERSION,
|
|
1772
1977
|
agentApproval: options.taskEnabled,
|
|
1773
1978
|
agentAudit: options.taskEnabled,
|
|
1774
1979
|
agentTools: [...NODE_AGENT_OPERATIONS],
|
|
@@ -1793,11 +1998,12 @@ function connectOnce(options, deviceId) {
|
|
|
1793
1998
|
}
|
|
1794
1999
|
|
|
1795
2000
|
const message = JSON.parse(line);
|
|
1796
|
-
if (message.type === 'welcome') {
|
|
2001
|
+
if (message.type === 'welcome') {
|
|
1797
2002
|
options.effectivePolicy = message.effectivePolicy && typeof message.effectivePolicy === 'object'
|
|
1798
2003
|
? message.effectivePolicy
|
|
1799
2004
|
: null;
|
|
1800
|
-
console.log(`Connected to LiveDesk Hub as ${options.name} (${deviceId})`);
|
|
2005
|
+
console.log(`Connected to LiveDesk Hub as ${options.name} (${deviceId})`);
|
|
2006
|
+
markClientUpdateConnected();
|
|
1801
2007
|
writeJsonLine(socket, {
|
|
1802
2008
|
type: 'status',
|
|
1803
2009
|
status: getStatus(options)
|
package/bin/livedesk-client.js
CHANGED
|
@@ -46,6 +46,8 @@ const CLIENT_AUTH_STORAGE_KEY = 'livedesk.client.supabase.auth';
|
|
|
46
46
|
const CLIENT_HUB_TARGET_CACHE_STORAGE_KEY = 'livedesk.client.last-hub-target';
|
|
47
47
|
const DEVICE_ROLE_CACHE_PATH = join(UNIFIED_CLIENT_STATE_DIR, 'device-role.json');
|
|
48
48
|
const FAST_PREFLIGHT_CACHE_PATH = join(UNIFIED_CLIENT_STATE_DIR, 'fast-preflight.json');
|
|
49
|
+
const CLIENT_UPDATE_STATE_PATH = process.env.LIVEDESK_CLIENT_UPDATE_STATE_PATH
|
|
50
|
+
|| join(UNIFIED_CLIENT_STATE_DIR, 'client-update.json');
|
|
49
51
|
let activeAgentProcess = null;
|
|
50
52
|
let roleRestartRequest = null;
|
|
51
53
|
let discoveryWakeController = new AbortController();
|
|
@@ -377,7 +379,7 @@ function upsertForwardedOption(args, flag, value) {
|
|
|
377
379
|
return next;
|
|
378
380
|
}
|
|
379
381
|
|
|
380
|
-
function appendForwardedFlag(args, flag) {
|
|
382
|
+
function appendForwardedFlag(args, flag) {
|
|
381
383
|
if (args.includes(flag)) {
|
|
382
384
|
return args;
|
|
383
385
|
}
|
|
@@ -550,6 +552,83 @@ function createFileStorage(filePath) {
|
|
|
550
552
|
};
|
|
551
553
|
}
|
|
552
554
|
|
|
555
|
+
const CLIENT_UPDATE_SINGLE_FLAGS = new Set([
|
|
556
|
+
'--help',
|
|
557
|
+
'-h',
|
|
558
|
+
'--version',
|
|
559
|
+
'--login',
|
|
560
|
+
'--logout',
|
|
561
|
+
'--no-login',
|
|
562
|
+
'--startup-run',
|
|
563
|
+
'--no-open',
|
|
564
|
+
'--exit-on-disconnect',
|
|
565
|
+
'--exit-on-invalid-pair',
|
|
566
|
+
'--once'
|
|
567
|
+
]);
|
|
568
|
+
const CLIENT_UPDATE_VALUE_FLAGS = new Set([
|
|
569
|
+
'--update-wait-pid',
|
|
570
|
+
// Pairing credentials stay in the inherited private environment and must
|
|
571
|
+
// never be copied onto a replacement process command line.
|
|
572
|
+
'--pair'
|
|
573
|
+
]);
|
|
574
|
+
|
|
575
|
+
export function buildClientUpdateRestartArgs(prepared = {}) {
|
|
576
|
+
const source = Array.isArray(prepared.forwarded) ? prepared.forwarded : [];
|
|
577
|
+
const args = [];
|
|
578
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
579
|
+
const arg = String(source[index] || '');
|
|
580
|
+
if (!arg || arg === 'connect' || CLIENT_UPDATE_SINGLE_FLAGS.has(arg)) {
|
|
581
|
+
continue;
|
|
582
|
+
}
|
|
583
|
+
if (arg.startsWith('--pair=')) {
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
if (CLIENT_UPDATE_VALUE_FLAGS.has(arg)) {
|
|
587
|
+
index += 1;
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
590
|
+
args.push(arg);
|
|
591
|
+
}
|
|
592
|
+
return args;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
function readClientUpdateState() {
|
|
596
|
+
try {
|
|
597
|
+
return JSON.parse(readFileSync(CLIENT_UPDATE_STATE_PATH, 'utf8'));
|
|
598
|
+
} catch {
|
|
599
|
+
return null;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
function writeClientUpdateRuntimeStage(stage, details = {}) {
|
|
604
|
+
const operationId = String(process.env.LIVEDESK_CLIENT_UPDATE_OPERATION_ID || '').trim();
|
|
605
|
+
if (!operationId) return;
|
|
606
|
+
const previous = readClientUpdateState();
|
|
607
|
+
if (previous?.operationId && previous.operationId !== operationId) return;
|
|
608
|
+
const targetVersion = String(process.env.LIVEDESK_CLIENT_UPDATE_TARGET_VERSION || '').trim().replace(/^v/i, '');
|
|
609
|
+
const productVersion = String(process.env.LIVEDESK_NPM_LAUNCHER_VERSION || readPackageVersion()).trim();
|
|
610
|
+
const state = {
|
|
611
|
+
...(previous && typeof previous === 'object' ? previous : {}),
|
|
612
|
+
operationId,
|
|
613
|
+
stage,
|
|
614
|
+
targetVersion,
|
|
615
|
+
productVersion,
|
|
616
|
+
agentVersion: readPackageVersion(),
|
|
617
|
+
launcherPid: process.pid,
|
|
618
|
+
updatedAt: new Date().toISOString(),
|
|
619
|
+
...details
|
|
620
|
+
};
|
|
621
|
+
mkdirSync(dirname(CLIENT_UPDATE_STATE_PATH), { recursive: true });
|
|
622
|
+
const temporaryPath = `${CLIENT_UPDATE_STATE_PATH}.${process.pid}.${randomBytes(6).toString('hex')}.tmp`;
|
|
623
|
+
try {
|
|
624
|
+
writeFileSync(temporaryPath, JSON.stringify(state, null, 2), 'utf8');
|
|
625
|
+
try { chmodSync(temporaryPath, 0o600); } catch { /* Windows profile ACLs remain authoritative. */ }
|
|
626
|
+
renameSync(temporaryPath, CLIENT_UPDATE_STATE_PATH);
|
|
627
|
+
} catch {
|
|
628
|
+
rmSync(temporaryPath, { force: true });
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
553
632
|
function hubCacheOwnerForSession(session) {
|
|
554
633
|
const userId = String(session?.user?.id || '').trim();
|
|
555
634
|
return userId ? `user:${userId}` : '';
|
|
@@ -3487,7 +3566,7 @@ function resolveBundledFfmpegPaths() {
|
|
|
3487
3566
|
return paths;
|
|
3488
3567
|
}
|
|
3489
3568
|
|
|
3490
|
-
function buildClientAgentEnvironment(prepared) {
|
|
3569
|
+
function buildClientAgentEnvironment(prepared) {
|
|
3491
3570
|
const env = { ...process.env };
|
|
3492
3571
|
env.LIVEDESK_CLIENT_PARENT_PID = String(process.pid);
|
|
3493
3572
|
// The client can be started by Explorer, a startup shortcut, or a GUI shell
|
|
@@ -3515,10 +3594,14 @@ function buildClientAgentEnvironment(prepared) {
|
|
|
3515
3594
|
env.LIVEDESK_CLIENT_MANAGER = String(prepared?.manager || env.LIVEDESK_CLIENT_MANAGER || '');
|
|
3516
3595
|
env.LIVEDESK_CLIENT_PAIR_TOKEN = String(prepared?.pair || env.LIVEDESK_CLIENT_PAIR_TOKEN || '');
|
|
3517
3596
|
env.LIVEDESK_CLIENT_NAME = String(prepared?.name || env.LIVEDESK_CLIENT_NAME || '');
|
|
3518
|
-
env.LIVEDESK_CLIENT_SLOT = String(prepared?.slot || env.LIVEDESK_CLIENT_SLOT || '');
|
|
3519
|
-
env.LIVEDESK_CLIENT_ENGINE = String(prepared?.engine || env.LIVEDESK_CLIENT_ENGINE || 'auto');
|
|
3520
|
-
|
|
3521
|
-
|
|
3597
|
+
env.LIVEDESK_CLIENT_SLOT = String(prepared?.slot || env.LIVEDESK_CLIENT_SLOT || '');
|
|
3598
|
+
env.LIVEDESK_CLIENT_ENGINE = String(prepared?.engine || env.LIVEDESK_CLIENT_ENGINE || 'auto');
|
|
3599
|
+
env.LIVEDESK_CLIENT_UPDATE_STATE_PATH = CLIENT_UPDATE_STATE_PATH;
|
|
3600
|
+
env.LIVEDESK_CLIENT_UPDATE_ARGS_BASE64 = Buffer
|
|
3601
|
+
.from(JSON.stringify(buildClientUpdateRestartArgs(prepared)), 'utf8')
|
|
3602
|
+
.toString('base64');
|
|
3603
|
+
return env;
|
|
3604
|
+
}
|
|
3522
3605
|
|
|
3523
3606
|
function buildFastEnvironment(prepared) {
|
|
3524
3607
|
const env = buildClientAgentEnvironment(prepared);
|
|
@@ -3721,13 +3804,17 @@ function resolveFastLaunch(runtime) {
|
|
|
3721
3804
|
};
|
|
3722
3805
|
}
|
|
3723
3806
|
|
|
3724
|
-
function spawnAgent(command, args, env = process.env, onStart = null) {
|
|
3807
|
+
function spawnAgent(command, args, env = process.env, onStart = null) {
|
|
3725
3808
|
const child = spawn(command, args, {
|
|
3726
3809
|
env,
|
|
3727
3810
|
stdio: 'inherit',
|
|
3728
3811
|
windowsHide: true
|
|
3729
3812
|
});
|
|
3730
|
-
activeAgentProcess = child;
|
|
3813
|
+
activeAgentProcess = child;
|
|
3814
|
+
writeClientUpdateRuntimeStage('agent-started', {
|
|
3815
|
+
agentPid: Number(child.pid || 0),
|
|
3816
|
+
restartVerified: false
|
|
3817
|
+
});
|
|
3731
3818
|
if (typeof onStart === 'function') {
|
|
3732
3819
|
onStart(child);
|
|
3733
3820
|
}
|
|
@@ -3771,10 +3858,13 @@ export async function runClientRuntime(argv = process.argv.slice(2)) {
|
|
|
3771
3858
|
if (!isTruthy(process.env.LIVEDESK_UNIFIED_RUNTIME)) {
|
|
3772
3859
|
console.warn('[LiveDesk] @livedesk/client is deprecated. Use the unified livedesk package or LiveDesk Desktop.');
|
|
3773
3860
|
}
|
|
3774
|
-
const parsed = parseLauncherArgs(argv);
|
|
3775
|
-
if (parsed.updateWaitPid) {
|
|
3776
|
-
await waitForProcessExit(parsed.updateWaitPid);
|
|
3777
|
-
}
|
|
3861
|
+
const parsed = parseLauncherArgs(argv);
|
|
3862
|
+
if (parsed.updateWaitPid) {
|
|
3863
|
+
await waitForProcessExit(parsed.updateWaitPid);
|
|
3864
|
+
}
|
|
3865
|
+
writeClientUpdateRuntimeStage('launcher-started', {
|
|
3866
|
+
restartVerified: false
|
|
3867
|
+
});
|
|
3778
3868
|
if (parsed.help) {
|
|
3779
3869
|
printHelp();
|
|
3780
3870
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livedesk/client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.170",
|
|
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.374",
|
|
43
|
+
"@livedesk/fast-osx-arm64": "0.1.376",
|
|
44
|
+
"@livedesk/fast-osx-x64": "0.1.376",
|
|
45
|
+
"@livedesk/fast-win-x64": "0.1.374"
|
|
46
46
|
},
|
|
47
47
|
"publishConfig": {
|
|
48
48
|
"access": "public"
|