@livedesk/client 0.1.196 → 0.1.197
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
|
@@ -4102,11 +4102,17 @@ function spawnAgent(command, args, env = process.env, onStart = null) {
|
|
|
4102
4102
|
agentPid: 0,
|
|
4103
4103
|
restartVerified: false
|
|
4104
4104
|
});
|
|
4105
|
+
const ownsProcessGroup = process.platform !== 'win32';
|
|
4105
4106
|
const child = spawn(command, args, {
|
|
4106
4107
|
env,
|
|
4107
4108
|
stdio: 'inherit',
|
|
4109
|
+
// A dedicated Unix process group lets Ctrl+C, replacement startup, and
|
|
4110
|
+
// forced shutdown terminate RemoteFast together with its SCK/FFmpeg
|
|
4111
|
+
// descendants without touching npm, the terminal, or unrelated jobs.
|
|
4112
|
+
detached: ownsProcessGroup,
|
|
4108
4113
|
windowsHide: true
|
|
4109
4114
|
});
|
|
4115
|
+
child.livedeskOwnsProcessGroup = ownsProcessGroup;
|
|
4110
4116
|
activeAgentProcess = child;
|
|
4111
4117
|
if (typeof onStart === 'function') {
|
|
4112
4118
|
onStart(child);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livedesk/client",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.197",
|
|
4
4
|
"description": "LiveDesk local remote client",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -41,10 +41,10 @@
|
|
|
41
41
|
"ws": "^8.18.3"
|
|
42
42
|
},
|
|
43
43
|
"optionalDependencies": {
|
|
44
|
-
"@livedesk/fast-linux-x64": "0.1.
|
|
45
|
-
"@livedesk/fast-osx-arm64": "0.1.
|
|
46
|
-
"@livedesk/fast-osx-x64": "0.1.
|
|
47
|
-
"@livedesk/fast-win-x64": "0.1.
|
|
44
|
+
"@livedesk/fast-linux-x64": "0.1.404",
|
|
45
|
+
"@livedesk/fast-osx-arm64": "0.1.404",
|
|
46
|
+
"@livedesk/fast-osx-x64": "0.1.404",
|
|
47
|
+
"@livedesk/fast-win-x64": "0.1.404"
|
|
48
48
|
},
|
|
49
49
|
"publishConfig": {
|
|
50
50
|
"access": "public"
|
|
@@ -1,9 +1,61 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
|
|
1
3
|
const SIGNAL_EXIT_CODES = Object.freeze({ SIGINT: 130, SIGTERM: 143 });
|
|
2
4
|
|
|
5
|
+
function terminateWindowsProcessTree(processId) {
|
|
6
|
+
return new Promise(resolve => {
|
|
7
|
+
execFile(
|
|
8
|
+
'taskkill.exe',
|
|
9
|
+
['/PID', String(processId), '/T', '/F'],
|
|
10
|
+
{
|
|
11
|
+
windowsHide: true,
|
|
12
|
+
timeout: 10000,
|
|
13
|
+
killSignal: 'SIGKILL'
|
|
14
|
+
},
|
|
15
|
+
error => resolve({ ok: !error, error: error || null })
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function isOwnedUnixProcessGroupAlive(child, platform, signalProcess) {
|
|
21
|
+
const processId = Number(child?.pid || 0);
|
|
22
|
+
if (platform === 'win32'
|
|
23
|
+
|| child?.livedeskOwnsProcessGroup !== true
|
|
24
|
+
|| !Number.isInteger(processId)
|
|
25
|
+
|| processId <= 1) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
signalProcess(-processId, 0);
|
|
30
|
+
return true;
|
|
31
|
+
} catch (error) {
|
|
32
|
+
return error?.code === 'EPERM';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function signalAgentTree(child, signal, platform, signalProcess) {
|
|
37
|
+
const processId = Number(child?.pid || 0);
|
|
38
|
+
if (platform !== 'win32'
|
|
39
|
+
&& child?.livedeskOwnsProcessGroup === true
|
|
40
|
+
&& Number.isInteger(processId)
|
|
41
|
+
&& processId > 1) {
|
|
42
|
+
try {
|
|
43
|
+
signalProcess(-processId, signal);
|
|
44
|
+
return;
|
|
45
|
+
} catch (error) {
|
|
46
|
+
if (error?.code !== 'ESRCH') throw error;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
child.kill(signal);
|
|
50
|
+
}
|
|
51
|
+
|
|
3
52
|
export function installAgentTerminationHandlers({
|
|
4
53
|
hostProcess = process,
|
|
5
54
|
getAgentProcess,
|
|
6
|
-
shutdownTimeoutMs =
|
|
55
|
+
shutdownTimeoutMs = 8000,
|
|
56
|
+
platform = process.platform,
|
|
57
|
+
signalProcess = (pid, signal) => process.kill(pid, signal),
|
|
58
|
+
terminateWindowsTree = terminateWindowsProcessTree,
|
|
7
59
|
exitProcess = code => hostProcess.exit(code)
|
|
8
60
|
} = {}) {
|
|
9
61
|
if (typeof getAgentProcess !== 'function') {
|
|
@@ -18,32 +70,72 @@ export function installAgentTerminationHandlers({
|
|
|
18
70
|
terminating = true;
|
|
19
71
|
const exitCode = SIGNAL_EXIT_CODES[signal] || 1;
|
|
20
72
|
const child = getAgentProcess();
|
|
21
|
-
if (!child || child.exitCode !== null
|
|
73
|
+
if (!child || child.exitCode !== null) {
|
|
22
74
|
exitProcess(exitCode);
|
|
23
75
|
return;
|
|
24
76
|
}
|
|
25
77
|
|
|
26
78
|
let finished = false;
|
|
79
|
+
let poll = null;
|
|
80
|
+
let timeout = null;
|
|
81
|
+
let hardStop = null;
|
|
27
82
|
const finish = () => {
|
|
28
83
|
if (finished) return;
|
|
29
84
|
finished = true;
|
|
85
|
+
if (poll) clearInterval(poll);
|
|
86
|
+
if (timeout) clearTimeout(timeout);
|
|
87
|
+
if (hardStop) clearTimeout(hardStop);
|
|
30
88
|
exitProcess(exitCode);
|
|
31
89
|
};
|
|
32
|
-
child
|
|
90
|
+
const processId = Number(child?.pid || 0);
|
|
91
|
+
if (platform === 'win32') {
|
|
92
|
+
if (!Number.isInteger(processId) || processId <= 1) {
|
|
93
|
+
finish();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// On Windows child.kill() only gives us the Agent leader's
|
|
97
|
+
// lifecycle. It does not provide an authoritative contract
|
|
98
|
+
// that FFmpeg descendants have stopped. Keep the launcher
|
|
99
|
+
// alive until taskkill has completed against this exact,
|
|
100
|
+
// launcher-owned process tree. Never search for or terminate
|
|
101
|
+
// unrelated ffmpeg.exe processes globally.
|
|
102
|
+
let treeTermination;
|
|
103
|
+
try {
|
|
104
|
+
treeTermination = terminateWindowsTree(processId);
|
|
105
|
+
} catch {
|
|
106
|
+
finish();
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
Promise.resolve(treeTermination)
|
|
110
|
+
.catch(() => null)
|
|
111
|
+
.finally(finish);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const finishWhenTreeStops = () => {
|
|
115
|
+
const ownsUnixGroup = child?.livedeskOwnsProcessGroup === true
|
|
116
|
+
&& Number(child?.pid || 0) > 1;
|
|
117
|
+
const childAlive = child.exitCode === null && child.signalCode == null;
|
|
118
|
+
const groupAlive = ownsUnixGroup
|
|
119
|
+
&& isOwnedUnixProcessGroupAlive(child, platform, signalProcess);
|
|
120
|
+
if (!childAlive && !groupAlive) {
|
|
121
|
+
finish();
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
child.once('exit', finishWhenTreeStops);
|
|
33
125
|
try {
|
|
34
|
-
child
|
|
126
|
+
signalAgentTree(child, signal, platform, signalProcess);
|
|
35
127
|
} catch {
|
|
36
128
|
finish();
|
|
37
129
|
return;
|
|
38
130
|
}
|
|
39
|
-
|
|
131
|
+
poll = setInterval(finishWhenTreeStops, 50);
|
|
132
|
+
timeout = setTimeout(() => {
|
|
40
133
|
try {
|
|
41
|
-
|
|
134
|
+
signalAgentTree(child, 'SIGKILL', platform, signalProcess);
|
|
42
135
|
} catch {
|
|
43
136
|
}
|
|
44
|
-
finish
|
|
137
|
+
hardStop = setTimeout(finish, 250);
|
|
45
138
|
}, Math.max(100, Number(shutdownTimeoutMs) || 3000));
|
|
46
|
-
timeout.unref?.();
|
|
47
139
|
};
|
|
48
140
|
handlers.set(signal, handler);
|
|
49
141
|
hostProcess.once(signal, handler);
|