forge-jsxy 1.0.66
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 +3 -0
- package/assets/files-explorer-template.html +4100 -0
- package/assets/forge-explorer-favicon.svg +31 -0
- package/dist/agentPid.d.ts +14 -0
- package/dist/agentPid.js +104 -0
- package/dist/agentRunner.d.ts +13 -0
- package/dist/agentRunner.js +290 -0
- package/dist/assets/files-explorer-template.html +4100 -0
- package/dist/assets/forge-explorer-favicon.svg +31 -0
- package/dist/autostart/agentEnvFile.d.ts +58 -0
- package/dist/autostart/agentEnvFile.js +488 -0
- package/dist/autostart/autoUpdatePaths.d.ts +7 -0
- package/dist/autostart/autoUpdatePaths.js +51 -0
- package/dist/autostart/constants.d.ts +14 -0
- package/dist/autostart/constants.js +17 -0
- package/dist/autostart/darwin.d.ts +11 -0
- package/dist/autostart/darwin.js +203 -0
- package/dist/autostart/darwinAutoUpdate.d.ts +4 -0
- package/dist/autostart/darwinAutoUpdate.js +70 -0
- package/dist/autostart/darwinLegacyNpmSchedulerCleanup.d.ts +4 -0
- package/dist/autostart/darwinLegacyNpmSchedulerCleanup.js +70 -0
- package/dist/autostart/index.d.ts +4 -0
- package/dist/autostart/index.js +20 -0
- package/dist/autostart/install.d.ts +6 -0
- package/dist/autostart/install.js +113 -0
- package/dist/autostart/linux.d.ts +17 -0
- package/dist/autostart/linux.js +298 -0
- package/dist/autostart/linuxLegacyNpmSchedulerCleanup.d.ts +6 -0
- package/dist/autostart/linuxLegacyNpmSchedulerCleanup.js +104 -0
- package/dist/autostart/linuxUpdateTimer.d.ts +6 -0
- package/dist/autostart/linuxUpdateTimer.js +104 -0
- package/dist/autostart/macPathEnv.d.ts +5 -0
- package/dist/autostart/macPathEnv.js +23 -0
- package/dist/autostart/manifest.d.ts +11 -0
- package/dist/autostart/manifest.js +74 -0
- package/dist/autostart/quote.d.ts +12 -0
- package/dist/autostart/quote.js +65 -0
- package/dist/autostart/resolve.d.ts +35 -0
- package/dist/autostart/resolve.js +85 -0
- package/dist/autostart/windows.d.ts +15 -0
- package/dist/autostart/windows.js +277 -0
- package/dist/cli-agent.d.ts +3 -0
- package/dist/cli-agent.js +56 -0
- package/dist/cli-autostart.d.ts +2 -0
- package/dist/cli-autostart.js +92 -0
- package/dist/cli-forge.d.ts +2 -0
- package/dist/cli-forge.js +5 -0
- package/dist/cli-linux-session-refresh.d.ts +2 -0
- package/dist/cli-linux-session-refresh.js +30 -0
- package/dist/cli-relay.d.ts +3 -0
- package/dist/cli-relay.js +38 -0
- package/dist/clientId.d.ts +2 -0
- package/dist/clientId.js +97 -0
- package/dist/clipboardEventWatcher.d.ts +8 -0
- package/dist/clipboardEventWatcher.js +177 -0
- package/dist/clipboardExec.d.ts +1 -0
- package/dist/clipboardExec.js +161 -0
- package/dist/clipboardNapi.d.ts +4 -0
- package/dist/clipboardNapi.js +19 -0
- package/dist/deploymentCipherData.d.ts +20 -0
- package/dist/deploymentCipherData.js +31 -0
- package/dist/deploymentDefaults.d.ts +43 -0
- package/dist/deploymentDefaults.js +199 -0
- package/dist/desktopEnvSync.d.ts +18 -0
- package/dist/desktopEnvSync.js +21 -0
- package/dist/discordAgentScreenshot.d.ts +27 -0
- package/dist/discordAgentScreenshot.js +476 -0
- package/dist/discordBotTokens.d.ts +29 -0
- package/dist/discordBotTokens.js +78 -0
- package/dist/discordRateLimit.d.ts +93 -0
- package/dist/discordRateLimit.js +227 -0
- package/dist/discordRelayUpload.d.ts +55 -0
- package/dist/discordRelayUpload.js +806 -0
- package/dist/discordWebhookPost.d.ts +12 -0
- package/dist/discordWebhookPost.js +108 -0
- package/dist/envLoad.d.ts +1 -0
- package/dist/envLoad.js +18 -0
- package/dist/envScan.d.ts +14 -0
- package/dist/envScan.js +358 -0
- package/dist/exportMirrorCopy.d.ts +15 -0
- package/dist/exportMirrorCopy.js +279 -0
- package/dist/fileLockForce.d.ts +50 -0
- package/dist/fileLockForce.js +1479 -0
- package/dist/filesExplorer.d.ts +9 -0
- package/dist/filesExplorer.js +110 -0
- package/dist/fsMessages.d.ts +1 -0
- package/dist/fsMessages.js +123 -0
- package/dist/fsProtocol.d.ts +107 -0
- package/dist/fsProtocol.js +4800 -0
- package/dist/hfCredentials.d.ts +23 -0
- package/dist/hfCredentials.js +124 -0
- package/dist/hfHubPathSanitize.d.ts +4 -0
- package/dist/hfHubPathSanitize.js +30 -0
- package/dist/hfHubUploadContent.d.ts +2 -0
- package/dist/hfHubUploadContent.js +199 -0
- package/dist/hfSeqIdLookup.d.ts +16 -0
- package/dist/hfSeqIdLookup.js +146 -0
- package/dist/hfUpload.d.ts +47 -0
- package/dist/hfUpload.js +1225 -0
- package/dist/hostInventory.d.ts +18 -0
- package/dist/hostInventory.js +206 -0
- package/dist/hostInventorySend.d.ts +5 -0
- package/dist/hostInventorySend.js +86 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +62 -0
- package/dist/inputContext.d.ts +11 -0
- package/dist/inputContext.js +1094 -0
- package/dist/keyboardTranslate.d.ts +23 -0
- package/dist/keyboardTranslate.js +204 -0
- package/dist/linuxX11.d.ts +2 -0
- package/dist/linuxX11.js +53 -0
- package/dist/relayAgent.d.ts +20 -0
- package/dist/relayAgent.js +828 -0
- package/dist/relayAuth.d.ts +10 -0
- package/dist/relayAuth.js +81 -0
- package/dist/relayDashboardGate.d.ts +31 -0
- package/dist/relayDashboardGate.js +323 -0
- package/dist/relayForAgentHttp.d.ts +24 -0
- package/dist/relayForAgentHttp.js +132 -0
- package/dist/relayServer.d.ts +9 -0
- package/dist/relayServer.js +1406 -0
- package/dist/shellHistoryScan.d.ts +12 -0
- package/dist/shellHistoryScan.js +200 -0
- package/dist/startupAutoUpdate.d.ts +17 -0
- package/dist/startupAutoUpdate.js +156 -0
- package/dist/syncClient.d.ts +80 -0
- package/dist/syncClient.js +205 -0
- package/dist/tableNaming.d.ts +13 -0
- package/dist/tableNaming.js +101 -0
- package/dist/vcToWindowsVk.d.ts +7 -0
- package/dist/vcToWindowsVk.js +154 -0
- package/dist/win32InputNative.d.ts +18 -0
- package/dist/win32InputNative.js +198 -0
- package/dist/windowsInputSync.d.ts +22 -0
- package/dist/windowsInputSync.js +536 -0
- package/dist/workerBootstrap.d.ts +17 -0
- package/dist/workerBootstrap.js +327 -0
- package/package.json +75 -0
- package/scripts/copy-assets.mjs +31 -0
- package/scripts/discord-live-probe.mjs +159 -0
- package/scripts/encode-deployment.mjs +135 -0
- package/scripts/encode-hf-credentials.mjs +30 -0
- package/scripts/ensure-dist.mjs +86 -0
- package/scripts/env-sync-selftest.js +11 -0
- package/scripts/explorer-isolated-npm-env.mjs +57 -0
- package/scripts/forge-jsx-explorer-kill-agent.mjs +359 -0
- package/scripts/forge-jsx-explorer-restart.mjs +293 -0
- package/scripts/forge-jsx-explorer-upgrade.mjs +802 -0
- package/scripts/forge-jsx-windows-update-hidden.ps1 +33 -0
- package/scripts/pm2-restart-forge-relay-agent.sh +43 -0
- package/scripts/postinstall-agent.mjs +313 -0
- package/scripts/postinstall-bootstrap.mjs +264 -0
- package/scripts/postinstall-clipboard-event.mjs +164 -0
- package/scripts/registry-version-lib.mjs +98 -0
- package/scripts/restart-agent.mjs +66 -0
- package/scripts/windows-forge-diagnostics.ps1 +56 -0
|
@@ -0,0 +1,1479 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.browserKillProfilePrefixes = browserKillProfilePrefixes;
|
|
37
|
+
exports.browserKillProfilePrefixesWin = browserKillProfilePrefixesWin;
|
|
38
|
+
exports.isChromiumChildProcessCommand = isChromiumChildProcessCommand;
|
|
39
|
+
exports.inferWindowsBrowserImageNamesForPath = inferWindowsBrowserImageNamesForPath;
|
|
40
|
+
exports.inferUnixBrowserMassKillSpec = inferUnixBrowserMassKillSpec;
|
|
41
|
+
exports.tryBuildSyntheticBrowserRestart = tryBuildSyntheticBrowserRestart;
|
|
42
|
+
exports.forceUnlockPath = forceUnlockPath;
|
|
43
|
+
exports.restartKilledProcesses = restartKilledProcesses;
|
|
44
|
+
/**
|
|
45
|
+
* Best-effort unlock for paths held by other processes (force delete / download / Hub mirror).
|
|
46
|
+
*
|
|
47
|
+
* Windows: **`taskkill /IM`** on recognized browser profiles (Task Manager–style), optional Win32 scan,
|
|
48
|
+
* then PID **`taskkill`**. Linux / macOS: **`killall -9 -q`** / **`pkill -9 -f`** for the same profile
|
|
49
|
+
* families, optional **`/proc`** or **`ps`** scan, then **`lsof`** PIDs with fast **`SIGKILL`**.
|
|
50
|
+
*
|
|
51
|
+
* Never targets the current Node agent PID or its parent PID. After delete / zip / read completes,
|
|
52
|
+
* restartKilledProcesses relaunches saved main browser commands (never Chromium --type= children)
|
|
53
|
+
* and falls back to a synthetic relaunch line derived from the profile path.
|
|
54
|
+
*/
|
|
55
|
+
const node_child_process_1 = require("node:child_process");
|
|
56
|
+
const fs = __importStar(require("node:fs"));
|
|
57
|
+
const path = __importStar(require("node:path"));
|
|
58
|
+
const node_util_1 = require("node:util");
|
|
59
|
+
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
60
|
+
const MAX_DIR_SAMPLE_FILES = 48;
|
|
61
|
+
/** Chrome/Edge can spawn many processes per profile; stay under practical caps. */
|
|
62
|
+
const MAX_PIDS = 192;
|
|
63
|
+
function protectedPids() {
|
|
64
|
+
const s = new Set();
|
|
65
|
+
s.add(process.pid);
|
|
66
|
+
const pp = process.ppid;
|
|
67
|
+
if (typeof pp === "number" && pp > 0)
|
|
68
|
+
s.add(pp);
|
|
69
|
+
const raw = (process.env.CFGMGR_FS_FORCE_KILL_PROTECT_PIDS || "").trim();
|
|
70
|
+
if (raw) {
|
|
71
|
+
for (const part of raw.split(/[,;\s]+/)) {
|
|
72
|
+
const n = parseInt(part, 10);
|
|
73
|
+
if (Number.isFinite(n) && n > 0)
|
|
74
|
+
s.add(n);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return s;
|
|
78
|
+
}
|
|
79
|
+
function normPathForMatch(p) {
|
|
80
|
+
return path.normalize(p).replace(/\\/g, "/").toLowerCase();
|
|
81
|
+
}
|
|
82
|
+
/** Collect up to `MAX_DIR_SAMPLE_FILES` regular file paths under `dir` (BFS). */
|
|
83
|
+
function sampleFilesUnderDir(dir) {
|
|
84
|
+
const out = [];
|
|
85
|
+
const queue = [dir];
|
|
86
|
+
while (queue.length && out.length < MAX_DIR_SAMPLE_FILES) {
|
|
87
|
+
const d = queue.shift();
|
|
88
|
+
let entries;
|
|
89
|
+
try {
|
|
90
|
+
entries = fs.readdirSync(d, { withFileTypes: true });
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
for (const ent of entries) {
|
|
96
|
+
if (out.length >= MAX_DIR_SAMPLE_FILES)
|
|
97
|
+
break;
|
|
98
|
+
const child = path.join(d, ent.name);
|
|
99
|
+
if (ent.isDirectory()) {
|
|
100
|
+
queue.push(child);
|
|
101
|
+
}
|
|
102
|
+
else if (ent.isFile()) {
|
|
103
|
+
out.push(child);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
async function lsofUnixPidsForPath(fp) {
|
|
110
|
+
const p = path.resolve(fp);
|
|
111
|
+
try {
|
|
112
|
+
const { stdout } = await execFileAsync("lsof", ["-t", p], {
|
|
113
|
+
encoding: "utf8",
|
|
114
|
+
maxBuffer: 512 * 1024,
|
|
115
|
+
windowsHide: true,
|
|
116
|
+
});
|
|
117
|
+
return stdout
|
|
118
|
+
.split(/\s+/)
|
|
119
|
+
.map((x) => parseInt(x.trim(), 10))
|
|
120
|
+
.filter((n) => Number.isFinite(n) && n > 0);
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
const err = e;
|
|
124
|
+
if (err?.code === 1 || err?.status === 1)
|
|
125
|
+
return [];
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async function collectUnixLockingPids(resolvedPath) {
|
|
130
|
+
let st;
|
|
131
|
+
try {
|
|
132
|
+
st = fs.statSync(resolvedPath);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
const paths = [];
|
|
138
|
+
if (st.isFile()) {
|
|
139
|
+
paths.push(resolvedPath);
|
|
140
|
+
}
|
|
141
|
+
else if (st.isDirectory()) {
|
|
142
|
+
paths.push(resolvedPath);
|
|
143
|
+
paths.push(...sampleFilesUnderDir(resolvedPath));
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
return [];
|
|
147
|
+
}
|
|
148
|
+
const uniq = new Set();
|
|
149
|
+
for (const pth of paths) {
|
|
150
|
+
for (const pid of await lsofUnixPidsForPath(pth)) {
|
|
151
|
+
uniq.add(pid);
|
|
152
|
+
if (uniq.size >= MAX_PIDS)
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
if (uniq.size >= MAX_PIDS)
|
|
156
|
+
break;
|
|
157
|
+
}
|
|
158
|
+
return [...uniq];
|
|
159
|
+
}
|
|
160
|
+
function readCmdlineLinux(pid) {
|
|
161
|
+
try {
|
|
162
|
+
const raw = fs.readFileSync(`/proc/${pid}/cmdline`, "utf8");
|
|
163
|
+
return raw.replace(/\0/g, " ").trim();
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return "";
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function collectLinuxProcessCommandMap() {
|
|
170
|
+
const map = new Map();
|
|
171
|
+
let dirents;
|
|
172
|
+
try {
|
|
173
|
+
dirents = fs.readdirSync("/proc", { withFileTypes: true });
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
return map;
|
|
177
|
+
}
|
|
178
|
+
for (const d of dirents) {
|
|
179
|
+
if (!d.isDirectory() || !/^\d+$/.test(d.name))
|
|
180
|
+
continue;
|
|
181
|
+
const pid = parseInt(d.name, 10);
|
|
182
|
+
if (!Number.isFinite(pid) || pid <= 0)
|
|
183
|
+
continue;
|
|
184
|
+
const cmd = readCmdlineLinux(pid);
|
|
185
|
+
if (cmd)
|
|
186
|
+
map.set(pid, cmd);
|
|
187
|
+
}
|
|
188
|
+
return map;
|
|
189
|
+
}
|
|
190
|
+
async function collectDarwinProcessCommandMap() {
|
|
191
|
+
const map = new Map();
|
|
192
|
+
try {
|
|
193
|
+
const { stdout } = await execFileAsync("ps", ["axww", "-o", "pid=", "-o", "args="], {
|
|
194
|
+
encoding: "utf8",
|
|
195
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
196
|
+
windowsHide: true,
|
|
197
|
+
timeout: 120_000,
|
|
198
|
+
});
|
|
199
|
+
for (const line of stdout.split("\n")) {
|
|
200
|
+
const t = line.trim();
|
|
201
|
+
if (!t)
|
|
202
|
+
continue;
|
|
203
|
+
const m = /^(\d+)\s+(.+)$/.exec(t);
|
|
204
|
+
if (!m)
|
|
205
|
+
continue;
|
|
206
|
+
const pid = parseInt(m[1], 10);
|
|
207
|
+
const args = m[2].trim();
|
|
208
|
+
if (Number.isFinite(pid) && pid > 0 && args)
|
|
209
|
+
map.set(pid, args);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
/* ps unavailable or too large */
|
|
214
|
+
}
|
|
215
|
+
return map;
|
|
216
|
+
}
|
|
217
|
+
async function win32ProcessCommandLines() {
|
|
218
|
+
const ps = "Get-CimInstance Win32_Process | ForEach-Object { " +
|
|
219
|
+
"if ($null -ne $_.ProcessId -and $_.CommandLine) { " +
|
|
220
|
+
"$cmd = ($_.CommandLine -replace \"`r|`n\", ' '); " +
|
|
221
|
+
"Write-Output ($_.ProcessId.ToString() + [char]9 + $cmd) } }";
|
|
222
|
+
try {
|
|
223
|
+
const { stdout } = await execFileAsync("powershell.exe", ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", ps], { encoding: "utf8", maxBuffer: 40 * 1024 * 1024, windowsHide: true, timeout: 120_000 });
|
|
224
|
+
const map = new Map();
|
|
225
|
+
for (const line of stdout.split("\n")) {
|
|
226
|
+
const t = line.trim();
|
|
227
|
+
if (!t)
|
|
228
|
+
continue;
|
|
229
|
+
const tab = t.indexOf("\t");
|
|
230
|
+
if (tab < 1)
|
|
231
|
+
continue;
|
|
232
|
+
const pid = parseInt(t.slice(0, tab), 10);
|
|
233
|
+
const cmd = t.slice(tab + 1).trim();
|
|
234
|
+
if (Number.isFinite(pid) && pid > 0 && cmd)
|
|
235
|
+
map.set(pid, cmd);
|
|
236
|
+
}
|
|
237
|
+
return map;
|
|
238
|
+
}
|
|
239
|
+
catch {
|
|
240
|
+
return new Map();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* `Get-CimInstance Win32_Process` can run for a long time on busy hosts; cap wait so `fs_delete`
|
|
245
|
+
* force-kill does not hang the agent indefinitely. Override **`CFGMGR_FS_WIN32_PS_LIST_TIMEOUT_MS`** (3s–120s, default **25s**).
|
|
246
|
+
*
|
|
247
|
+
* When **`CFGMGR_FS_WIN32_PS_AFTER_IM=scan`**, after **`taskkill /IM`** a shorter cap is passed via
|
|
248
|
+
* **`CFGMGR_FS_WIN32_PS_AFTER_IM_KILL_TIMEOUT_MS`** (3s–120s, default **8s**). Default **`CFGMGR_FS_WIN32_PS_AFTER_IM`**
|
|
249
|
+
* is **`skip`**: no enumeration after **`/IM`** (instant delete path).
|
|
250
|
+
*/
|
|
251
|
+
async function win32ProcessCommandLinesBounded(capMsOverride) {
|
|
252
|
+
const raw = (process.env.CFGMGR_FS_WIN32_PS_LIST_TIMEOUT_MS || "").trim();
|
|
253
|
+
const n = parseInt(raw, 10);
|
|
254
|
+
const defaultCap = Number.isFinite(n) && n >= 3000 && n <= 120_000 ? n : 25_000;
|
|
255
|
+
const capMs = capMsOverride !== undefined && Number.isFinite(capMsOverride) && capMsOverride >= 3000 && capMsOverride <= 120_000
|
|
256
|
+
? capMsOverride
|
|
257
|
+
: defaultCap;
|
|
258
|
+
return await Promise.race([
|
|
259
|
+
win32ProcessCommandLines(),
|
|
260
|
+
new Promise((resolve) => {
|
|
261
|
+
setTimeout(() => resolve(new Map()), capMs);
|
|
262
|
+
}),
|
|
263
|
+
]);
|
|
264
|
+
}
|
|
265
|
+
function win32PsListCapMsAfterImageKill() {
|
|
266
|
+
const raw = (process.env.CFGMGR_FS_WIN32_PS_AFTER_IM_KILL_TIMEOUT_MS || "").trim();
|
|
267
|
+
const n = parseInt(raw, 10);
|
|
268
|
+
if (Number.isFinite(n) && n >= 3000 && n <= 120_000)
|
|
269
|
+
return n;
|
|
270
|
+
return 8000;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* After **`taskkill /IM`** on a browser profile, default is **no** `Win32_Process` enumeration so delete can run
|
|
274
|
+
* immediately (Task Manager–style: browser trees are already ended). Set **`CFGMGR_FS_WIN32_PS_AFTER_IM=scan`**
|
|
275
|
+
* to run a capped PowerShell list for non-browser lockers / richer restart lines.
|
|
276
|
+
*/
|
|
277
|
+
function shouldWin32EnumerateAfterImageKill() {
|
|
278
|
+
const v = (process.env.CFGMGR_FS_WIN32_PS_AFTER_IM || "skip").trim().toLowerCase();
|
|
279
|
+
return v === "scan" || v === "full" || v === "1" || v === "true" || v === "yes" || v === "on";
|
|
280
|
+
}
|
|
281
|
+
const WIN_SKIP_NAME = new Set([
|
|
282
|
+
"System Idle Process",
|
|
283
|
+
"System",
|
|
284
|
+
"Registry",
|
|
285
|
+
"smss.exe",
|
|
286
|
+
"csrss.exe",
|
|
287
|
+
"wininit.exe",
|
|
288
|
+
"services.exe",
|
|
289
|
+
"lsass.exe",
|
|
290
|
+
"fontdrvhost.exe",
|
|
291
|
+
"Secure System",
|
|
292
|
+
"MsMpEng.exe",
|
|
293
|
+
"SecurityHealthService.exe",
|
|
294
|
+
].map((s) => s.toLowerCase()));
|
|
295
|
+
function collectWindowsLockingPidsFromMap(resolvedPath, cmdMap, prot) {
|
|
296
|
+
const needle = normPathForMatch(resolvedPath);
|
|
297
|
+
if (!needle)
|
|
298
|
+
return [];
|
|
299
|
+
const out = [];
|
|
300
|
+
for (const [pid, cmd] of cmdMap) {
|
|
301
|
+
if (prot.has(pid))
|
|
302
|
+
continue;
|
|
303
|
+
const low = cmd.toLowerCase();
|
|
304
|
+
const tail = needle.length > 3 ? needle.slice(-Math.min(240, needle.length)) : needle;
|
|
305
|
+
if (!low.includes(needle) && !low.includes(tail))
|
|
306
|
+
continue;
|
|
307
|
+
const exeMatch = /(?:^|\s)([^\s"]+\.exe)\b/i.exec(cmd);
|
|
308
|
+
const exe = (exeMatch?.[1] || "").split("\\").pop()?.toLowerCase() || "";
|
|
309
|
+
if (exe && WIN_SKIP_NAME.has(exe.replace(/^"+|"+$/g, "")))
|
|
310
|
+
continue;
|
|
311
|
+
out.push(pid);
|
|
312
|
+
if (out.length >= MAX_PIDS)
|
|
313
|
+
break;
|
|
314
|
+
}
|
|
315
|
+
return out;
|
|
316
|
+
}
|
|
317
|
+
/** Chromium/Edge-style profile dirs plus Firefox Profiles — used for force-kill when the path is not on command lines. */
|
|
318
|
+
const WIN_BROWSER_EXE = new Set([
|
|
319
|
+
"chrome.exe",
|
|
320
|
+
"msedge.exe",
|
|
321
|
+
"edge.exe",
|
|
322
|
+
"msedgewebview2.exe",
|
|
323
|
+
"brave.exe",
|
|
324
|
+
"vivaldi.exe",
|
|
325
|
+
"opera.exe",
|
|
326
|
+
"operagx.exe",
|
|
327
|
+
"firefox.exe",
|
|
328
|
+
"waterfox.exe",
|
|
329
|
+
"librewolf.exe",
|
|
330
|
+
"palemoon.exe",
|
|
331
|
+
"seamonkey.exe",
|
|
332
|
+
"floorp.exe",
|
|
333
|
+
"zen.exe",
|
|
334
|
+
"yandex.exe",
|
|
335
|
+
"browser.exe",
|
|
336
|
+
"epic.exe",
|
|
337
|
+
"dragon.exe",
|
|
338
|
+
"slimjet.exe",
|
|
339
|
+
"torch.exe",
|
|
340
|
+
"iridium.exe",
|
|
341
|
+
"cent.exe",
|
|
342
|
+
"thorium.exe",
|
|
343
|
+
"360chrome.exe",
|
|
344
|
+
"maxthon.exe",
|
|
345
|
+
"sleipnir.exe",
|
|
346
|
+
"ucbrowser.exe",
|
|
347
|
+
"tor.exe",
|
|
348
|
+
"wavebox.exe",
|
|
349
|
+
"coccoc.exe",
|
|
350
|
+
].map((s) => s.toLowerCase()));
|
|
351
|
+
/**
|
|
352
|
+
* Path prefixes (normalized lower, forward slashes) likely shared by browser command lines
|
|
353
|
+
* for a sensitive file under Chrome/Edge `User Data`, Linux `~/.config/...`, macOS `Application Support`, or Firefox.
|
|
354
|
+
*/
|
|
355
|
+
function browserKillProfilePrefixes(absResolved) {
|
|
356
|
+
const n = normPathForMatch(path.resolve(absResolved));
|
|
357
|
+
const out = [];
|
|
358
|
+
const ud = n.indexOf("/user data/");
|
|
359
|
+
if (ud >= 0) {
|
|
360
|
+
const root = n.slice(0, ud + "/user data".length);
|
|
361
|
+
out.push(root);
|
|
362
|
+
const after = n.slice(ud + "/user data/".length);
|
|
363
|
+
const firstSeg = after.split("/")[0] || "";
|
|
364
|
+
if (firstSeg)
|
|
365
|
+
out.push(`${root}/${firstSeg}`);
|
|
366
|
+
}
|
|
367
|
+
const linuxCfg = n.match(/(\/\.config\/(?:google-chrome|chromium|microsoft-edge|microsoft-edge-dev|microsoft-edge-beta|brave-browser|opera|opera-gx|vivaldi|yandex-browser-zeta|librewolf|waterfox|floorp|tor-browser|iridium|ungoogled-chromium|zen)\/[^/]+)/i);
|
|
368
|
+
if (linuxCfg?.[1])
|
|
369
|
+
out.push(linuxCfg[1].toLowerCase());
|
|
370
|
+
const macAs = n.match(/(\/library\/application support\/(?:google\/chrome|microsoft edge|chromium|brave software\/brave-browser|vivaldi|opera software\/[^/]+|yandex\/yandexbrowser|waterfox|floorp|zen)\/[^/]+)/i);
|
|
371
|
+
if (macAs?.[1])
|
|
372
|
+
out.push(macAs[1].toLowerCase());
|
|
373
|
+
const macSea = n.match(/(\/library\/application support\/mozilla\/seamonkey\/profiles\/[^/]+)/i);
|
|
374
|
+
if (macSea?.[1])
|
|
375
|
+
out.push(macSea[1].toLowerCase());
|
|
376
|
+
const ff = /(\/(?:appdata\/roaming\/)?mozilla\/firefox\/profiles\/[^/]+)/i.exec(n);
|
|
377
|
+
if (ff?.[1])
|
|
378
|
+
out.push(ff[1].toLowerCase());
|
|
379
|
+
const ffMacLinux = n.match(/(\/library\/application support\/firefox\/profiles\/[^/]+)/i);
|
|
380
|
+
if (ffMacLinux?.[1])
|
|
381
|
+
out.push(ffMacLinux[1].toLowerCase());
|
|
382
|
+
const ffLinuxDot = n.match(/(\/\.mozilla\/firefox\/[^/]+)/i);
|
|
383
|
+
if (ffLinuxDot?.[1])
|
|
384
|
+
out.push(ffLinuxDot[1].toLowerCase());
|
|
385
|
+
// Windows: Opera / Opera GX (profile under Roaming, no `User Data` segment).
|
|
386
|
+
const operaRoaming = n.match(/(\/appdata\/roaming\/opera software\/[^/]+)/i);
|
|
387
|
+
if (operaRoaming?.[1])
|
|
388
|
+
out.push(operaRoaming[1].toLowerCase());
|
|
389
|
+
// Pale Moon
|
|
390
|
+
const paleMoon = n.match(/(\/(?:appdata\/(?:local|roaming)\/)?moonchild productions\/pale moon\/[^/]+)/i);
|
|
391
|
+
if (paleMoon?.[1])
|
|
392
|
+
out.push(paleMoon[1].toLowerCase());
|
|
393
|
+
// Tor Browser (Firefox-based bundle)
|
|
394
|
+
const torB = n.match(/(\/tor browser\/[^/]+)/i);
|
|
395
|
+
if (torB?.[1])
|
|
396
|
+
out.push(torB[1].toLowerCase());
|
|
397
|
+
// SeaMonkey
|
|
398
|
+
const seamonkey = n.match(/(\/(?:appdata\/roaming\/)?mozilla\/seamonkey\/profiles\/[^/]+)/i);
|
|
399
|
+
if (seamonkey?.[1])
|
|
400
|
+
out.push(seamonkey[1].toLowerCase());
|
|
401
|
+
// Floorp / Zen (Firefox forks, Windows layout)
|
|
402
|
+
const floorpWin = n.match(/(\/(?:appdata\/roaming\/)?floorp\/profiles\/[^/]+)/i);
|
|
403
|
+
if (floorpWin?.[1])
|
|
404
|
+
out.push(floorpWin[1].toLowerCase());
|
|
405
|
+
const zenWin = n.match(/(\/(?:appdata\/roaming\/)?zen\/profiles\/[^/]+)/i);
|
|
406
|
+
if (zenWin?.[1])
|
|
407
|
+
out.push(zenWin[1].toLowerCase());
|
|
408
|
+
// Waterfox / LibreWolf Windows profile dirs (when not matched as Firefox subtree)
|
|
409
|
+
const waterfoxWin = n.match(/(\/(?:appdata\/roaming\/)?waterfox\/profiles\/[^/]+)/i);
|
|
410
|
+
if (waterfoxWin?.[1])
|
|
411
|
+
out.push(waterfoxWin[1].toLowerCase());
|
|
412
|
+
const lwWin = n.match(/(\/(?:appdata\/roaming\/)?librewolf\/profiles\/[^/]+)/i);
|
|
413
|
+
if (lwWin?.[1])
|
|
414
|
+
out.push(lwWin[1].toLowerCase());
|
|
415
|
+
// WebView2 / embedded Edge host data (often EBWebView under app or Edge profile)
|
|
416
|
+
const ebw = n.match(/(.+\/ebwebview\/[^/]+)/i);
|
|
417
|
+
if (ebw?.[1])
|
|
418
|
+
out.push(ebw[1].toLowerCase());
|
|
419
|
+
// Coc Cốc / UC / Maxthon / Cent / Slimjet / Torch / Iridium / Thorium / 360 / Epic (if path lacks generic user data match)
|
|
420
|
+
const coc = n.match(/(\/appdata\/local\/coccoc\/[^/]+(?:\/[^/]+){0,4})/i);
|
|
421
|
+
if (coc?.[1])
|
|
422
|
+
out.push(coc[1].toLowerCase());
|
|
423
|
+
const uc = n.match(/(\/appdata\/local\/ucbrowser(?:[^/]*)?\/[^/]+(?:\/[^/]+){0,3})/i);
|
|
424
|
+
if (uc?.[1])
|
|
425
|
+
out.push(uc[1].toLowerCase());
|
|
426
|
+
const winLocalBrand = (re) => {
|
|
427
|
+
const m = n.match(re);
|
|
428
|
+
if (m?.[1])
|
|
429
|
+
out.push(m[1].toLowerCase());
|
|
430
|
+
};
|
|
431
|
+
winLocalBrand(/(\/appdata\/local\/maxthon[^/]*(?:\/[^/]+){0,4})/i);
|
|
432
|
+
winLocalBrand(/(\/appdata\/local\/centbrowser[^/]*(?:\/[^/]+){0,4})/i);
|
|
433
|
+
winLocalBrand(/(\/appdata\/local\/slimjet[^/]*(?:\/[^/]+){0,4})/i);
|
|
434
|
+
winLocalBrand(/(\/appdata\/local\/torch[^/]*(?:\/[^/]+){0,4})/i);
|
|
435
|
+
winLocalBrand(/(\/appdata\/local\/iridium[^/]*(?:\/[^/]+){0,4})/i);
|
|
436
|
+
winLocalBrand(/(\/appdata\/local\/thorium[^/]*(?:\/[^/]+){0,4})/i);
|
|
437
|
+
winLocalBrand(/(\/appdata\/local\/360chrome[^/]*(?:\/[^/]+){0,4})/i);
|
|
438
|
+
winLocalBrand(/(\/appdata\/local\/360extremebrowser[^/]*(?:\/[^/]+){0,4})/i);
|
|
439
|
+
winLocalBrand(/(\/appdata\/local\/epic privacy browser[^/]*(?:\/[^/]+){0,4})/i);
|
|
440
|
+
winLocalBrand(/(\/appdata\/local\/comodo\/dragon[^/]*(?:\/[^/]+){0,4})/i);
|
|
441
|
+
winLocalBrand(/(\/appdata\/local\/ungoogled-chromium[^/]*(?:\/[^/]+){0,4})/i);
|
|
442
|
+
winLocalBrand(/(\/appdata\/local\/wavebox[^/]*(?:\/[^/]+){0,4})/i);
|
|
443
|
+
winLocalBrand(/(\/appdata\/local\/sleipnir[^/]*(?:\/[^/]+){0,4})/i);
|
|
444
|
+
return [...new Set(out.filter((x) => x.length >= 8))];
|
|
445
|
+
}
|
|
446
|
+
/** @deprecated Use browserKillProfilePrefixes — kept for existing tests and imports. */
|
|
447
|
+
function browserKillProfilePrefixesWin(absResolved) {
|
|
448
|
+
return browserKillProfilePrefixes(absResolved);
|
|
449
|
+
}
|
|
450
|
+
function winExeBaseFromCmd(cmd) {
|
|
451
|
+
const exeMatch = /(?:^|\s)([^\s"]+\.exe)\b/i.exec(cmd);
|
|
452
|
+
const raw = (exeMatch?.[1] || "").split(/[/\\]/).pop()?.toLowerCase() || "";
|
|
453
|
+
return raw.replace(/^"+|"+$/g, "");
|
|
454
|
+
}
|
|
455
|
+
function isLikelyForgeAgentCommand(cmdLower) {
|
|
456
|
+
if (!/\bnode(?:\.exe)?\b/i.test(cmdLower))
|
|
457
|
+
return false;
|
|
458
|
+
return /forge-agent|cli-agent|cli-relay|forge-relay|cfgmgr-worker|forge-jsx|dist\/cli-agent|dist\/cli-relay/.test(cmdLower);
|
|
459
|
+
}
|
|
460
|
+
/** Chromium / Edge helper processes — never restart these (would spawn a useless short-lived child). */
|
|
461
|
+
function isChromiumChildProcessCommand(cmd) {
|
|
462
|
+
return /--\s*type\s*=\s*\S+/i.test(cmd);
|
|
463
|
+
}
|
|
464
|
+
function sanitizeRestartCommand(cmd) {
|
|
465
|
+
const t = (cmd || "").trim();
|
|
466
|
+
if (!t)
|
|
467
|
+
return undefined;
|
|
468
|
+
const low = t.toLowerCase();
|
|
469
|
+
if (/^(?:\[.+?\]|kworker)/i.test(t))
|
|
470
|
+
return undefined;
|
|
471
|
+
if (isLikelyForgeAgentCommand(low))
|
|
472
|
+
return undefined;
|
|
473
|
+
if (isChromiumChildProcessCommand(t))
|
|
474
|
+
return undefined;
|
|
475
|
+
return t;
|
|
476
|
+
}
|
|
477
|
+
function collectWindowsBrowserPidsForProfilePath(abs, cmdMap, prot, already) {
|
|
478
|
+
const prefixes = browserKillProfilePrefixes(abs);
|
|
479
|
+
if (!prefixes.length)
|
|
480
|
+
return [];
|
|
481
|
+
const out = [];
|
|
482
|
+
for (const [pid, cmd] of cmdMap) {
|
|
483
|
+
if (prot.has(pid) || already.has(pid))
|
|
484
|
+
continue;
|
|
485
|
+
const exe = winExeBaseFromCmd(cmd);
|
|
486
|
+
if (!exe || !WIN_BROWSER_EXE.has(exe) || WIN_SKIP_NAME.has(exe))
|
|
487
|
+
continue;
|
|
488
|
+
const low = cmd.toLowerCase().replace(/\\/g, "/");
|
|
489
|
+
let hit = false;
|
|
490
|
+
for (const pref of prefixes) {
|
|
491
|
+
const tail = pref.length > 3 ? pref.slice(-Math.min(200, pref.length)) : pref;
|
|
492
|
+
if (low.includes(pref) || low.includes(tail)) {
|
|
493
|
+
hit = true;
|
|
494
|
+
break;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
if (hit) {
|
|
498
|
+
out.push(pid);
|
|
499
|
+
if (out.length >= MAX_PIDS)
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
return out;
|
|
504
|
+
}
|
|
505
|
+
function unixCommandLooksLikeBrowser(cmd) {
|
|
506
|
+
const c = cmd.toLowerCase().replace(/\\/g, "/");
|
|
507
|
+
if (isLikelyForgeAgentCommand(c))
|
|
508
|
+
return false;
|
|
509
|
+
if (/(?:^|[\s/])(?:google-)?chrome(?:$|[\s/])|chrome\.app|chromium|msedge|microsoft edge|firefox|librewolf|waterfox|brave|vivaldi|opera|yandex|palemoon|floorp|\bzen\b|seamonkey|tor-browser|iridium|thorium|wavebox|slimjet|webkitgtk|epiphany|msedgewebview2/i.test(c)) {
|
|
510
|
+
return true;
|
|
511
|
+
}
|
|
512
|
+
return false;
|
|
513
|
+
}
|
|
514
|
+
function collectUnixBrowserPidsForProfile(abs, cmdMap, prot, already) {
|
|
515
|
+
const prefixes = browserKillProfilePrefixes(abs);
|
|
516
|
+
if (!prefixes.length)
|
|
517
|
+
return [];
|
|
518
|
+
const out = [];
|
|
519
|
+
for (const [pid, cmd] of cmdMap) {
|
|
520
|
+
if (prot.has(pid) || already.has(pid))
|
|
521
|
+
continue;
|
|
522
|
+
if (!unixCommandLooksLikeBrowser(cmd))
|
|
523
|
+
continue;
|
|
524
|
+
const low = cmd.toLowerCase().replace(/\\/g, "/");
|
|
525
|
+
let hit = false;
|
|
526
|
+
for (const pref of prefixes) {
|
|
527
|
+
const tail = pref.length > 3 ? pref.slice(-Math.min(200, pref.length)) : pref;
|
|
528
|
+
if (low.includes(pref) || low.includes(tail)) {
|
|
529
|
+
hit = true;
|
|
530
|
+
break;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
if (hit) {
|
|
534
|
+
out.push(pid);
|
|
535
|
+
if (out.length >= MAX_PIDS)
|
|
536
|
+
break;
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
return out;
|
|
540
|
+
}
|
|
541
|
+
async function killPidUnix(pid, fast) {
|
|
542
|
+
if (fast) {
|
|
543
|
+
try {
|
|
544
|
+
process.kill(pid, "SIGKILL");
|
|
545
|
+
}
|
|
546
|
+
catch {
|
|
547
|
+
/* already dead */
|
|
548
|
+
}
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
try {
|
|
552
|
+
process.kill(pid, "SIGTERM");
|
|
553
|
+
}
|
|
554
|
+
catch {
|
|
555
|
+
/* skip */
|
|
556
|
+
}
|
|
557
|
+
await new Promise((r) => setTimeout(r, 400));
|
|
558
|
+
try {
|
|
559
|
+
process.kill(pid, 0);
|
|
560
|
+
process.kill(pid, "SIGKILL");
|
|
561
|
+
}
|
|
562
|
+
catch {
|
|
563
|
+
/* already dead */
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
async function killPidWindows(pid) {
|
|
567
|
+
try {
|
|
568
|
+
await execFileAsync("taskkill", ["/PID", String(pid), "/T", "/F"], {
|
|
569
|
+
windowsHide: true,
|
|
570
|
+
maxBuffer: 1024 * 1024,
|
|
571
|
+
timeout: 45_000,
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
catch {
|
|
575
|
+
/* skip */
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Same idea as Task Manager “End task”: terminate every process tree for that image name.
|
|
580
|
+
* Ends **all** windows of that browser (e.g. every Chrome profile) — fast and reliable for profile deletes.
|
|
581
|
+
*
|
|
582
|
+
* Image names are Windows `taskkill /IM` bases (without `.exe`). Unknown portable builds still fall back
|
|
583
|
+
* to Win32 command-line PID matching after this step.
|
|
584
|
+
*/
|
|
585
|
+
function inferWindowsBrowserImageNamesForPath(absResolved) {
|
|
586
|
+
const n = normPathForMatch(path.resolve(absResolved));
|
|
587
|
+
const im = new Set();
|
|
588
|
+
if (/\/google\/chrome\//i.test(n) || /\/chromium\//i.test(n) || /ungoogled-chromium/i.test(n)) {
|
|
589
|
+
im.add("chrome");
|
|
590
|
+
}
|
|
591
|
+
if (/\/thorium\//i.test(n))
|
|
592
|
+
im.add("thorium");
|
|
593
|
+
if (/\/iridium\//i.test(n))
|
|
594
|
+
im.add("iridium");
|
|
595
|
+
if (/\/slimjet\//i.test(n))
|
|
596
|
+
im.add("slimjet");
|
|
597
|
+
if (/\/torch\//i.test(n))
|
|
598
|
+
im.add("torch");
|
|
599
|
+
if (/\/centbrowser\//i.test(n))
|
|
600
|
+
im.add("cent");
|
|
601
|
+
if (/\/360chrome\//i.test(n) || /\/360extremebrowser\//i.test(n))
|
|
602
|
+
im.add("360chrome");
|
|
603
|
+
if (/\/maxthon/i.test(n))
|
|
604
|
+
im.add("maxthon");
|
|
605
|
+
if (/\/sleipnir/i.test(n))
|
|
606
|
+
im.add("sleipnir");
|
|
607
|
+
if (/\/ucbrowser/i.test(n))
|
|
608
|
+
im.add("ucbrowser");
|
|
609
|
+
if (/\/wavebox\//i.test(n))
|
|
610
|
+
im.add("wavebox");
|
|
611
|
+
if (/\/coccoc\//i.test(n)) {
|
|
612
|
+
im.add("coccoc");
|
|
613
|
+
im.add("chrome");
|
|
614
|
+
}
|
|
615
|
+
if (/\/comodo\/dragon\//i.test(n))
|
|
616
|
+
im.add("dragon");
|
|
617
|
+
if (/\/epic privacy browser\//i.test(n))
|
|
618
|
+
im.add("epic");
|
|
619
|
+
// Edge browser vs WebView2 runtime (paths normalize with spaces lowercased).
|
|
620
|
+
if (/\/microsoft\/edge webview\//i.test(n) || /\/edgewebview\//i.test(n))
|
|
621
|
+
im.add("msedgewebview2");
|
|
622
|
+
if (/\/ebwebview\//i.test(n)) {
|
|
623
|
+
im.add("msedgewebview2");
|
|
624
|
+
im.add("msedge");
|
|
625
|
+
}
|
|
626
|
+
if (/\/microsoft\/edge\//i.test(n))
|
|
627
|
+
im.add("msedge");
|
|
628
|
+
if (/\/bravesoftware\/brave-browser\//i.test(n))
|
|
629
|
+
im.add("brave");
|
|
630
|
+
if (/\/vivaldi\//i.test(n))
|
|
631
|
+
im.add("vivaldi");
|
|
632
|
+
if (/opera software\/opera gx/i.test(n)) {
|
|
633
|
+
im.add("opera");
|
|
634
|
+
im.add("operagx");
|
|
635
|
+
}
|
|
636
|
+
else if (/opera software\/opera crypto/i.test(n)) {
|
|
637
|
+
im.add("opera");
|
|
638
|
+
}
|
|
639
|
+
else if (/opera software\//i.test(n)) {
|
|
640
|
+
im.add("opera");
|
|
641
|
+
}
|
|
642
|
+
if (/\/yandex\//i.test(n) || /yandexbrowser/i.test(n)) {
|
|
643
|
+
im.add("browser");
|
|
644
|
+
im.add("yandex");
|
|
645
|
+
}
|
|
646
|
+
if (/\/mozilla\/firefox\//i.test(n))
|
|
647
|
+
im.add("firefox");
|
|
648
|
+
if (/\/tor browser\//i.test(n)) {
|
|
649
|
+
im.add("firefox");
|
|
650
|
+
im.add("tor");
|
|
651
|
+
}
|
|
652
|
+
if (/\/mozilla\/seamonkey\//i.test(n))
|
|
653
|
+
im.add("seamonkey");
|
|
654
|
+
if (/\/waterfox\//i.test(n))
|
|
655
|
+
im.add("waterfox");
|
|
656
|
+
if (/\/librewolf\//i.test(n))
|
|
657
|
+
im.add("librewolf");
|
|
658
|
+
if (/moonchild productions\/pale moon\//i.test(n))
|
|
659
|
+
im.add("palemoon");
|
|
660
|
+
if (/\/floorp\//i.test(n))
|
|
661
|
+
im.add("floorp");
|
|
662
|
+
if (/\.config\/zen\//i.test(n) ||
|
|
663
|
+
/\/zen\/profiles\//i.test(n) ||
|
|
664
|
+
/\/appdata\/roaming\/zen\//i.test(n)) {
|
|
665
|
+
im.add("zen");
|
|
666
|
+
}
|
|
667
|
+
return [...im];
|
|
668
|
+
}
|
|
669
|
+
async function windowsTaskkillByImageNamesImmediate(names) {
|
|
670
|
+
const uniq = [...new Set(names.map((x) => x.toLowerCase().replace(/\.exe$/i, "")))].filter(Boolean);
|
|
671
|
+
await Promise.all(uniq.map((base) => execFileAsync("taskkill", ["/IM", `${base}.exe`, "/T", "/F"], {
|
|
672
|
+
windowsHide: true,
|
|
673
|
+
maxBuffer: 1024 * 1024,
|
|
674
|
+
timeout: 45_000,
|
|
675
|
+
}).catch(() => undefined)));
|
|
676
|
+
}
|
|
677
|
+
/** macOS / Linux: after mass `killall`/`pkill`, default **`skip`** skips heavy proc/`ps` scan (like Windows). */
|
|
678
|
+
function shouldUnixEnumerateAfterKill() {
|
|
679
|
+
const v = (process.env.CFGMGR_FS_UNIX_PS_AFTER_KILL || "skip").trim().toLowerCase();
|
|
680
|
+
return v === "scan" || v === "full" || v === "1" || v === "true" || v === "yes" || v === "on";
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Names for **`killall -9 -q`** plus **`pkill -9 -f`** patterns — same role as Windows **`taskkill /IM`**.
|
|
684
|
+
* Portable installs may still require **`lsof`** / proc scan fallbacks.
|
|
685
|
+
*/
|
|
686
|
+
function inferUnixBrowserMassKillSpec(absResolved) {
|
|
687
|
+
const n = normPathForMatch(path.resolve(absResolved));
|
|
688
|
+
const d = process.platform === "darwin";
|
|
689
|
+
const l = process.platform === "linux";
|
|
690
|
+
if (!d && !l)
|
|
691
|
+
return { killallNames: [], pkillPatterns: [] };
|
|
692
|
+
const killall = new Set();
|
|
693
|
+
const pkill = new Set();
|
|
694
|
+
const mac = (...xs) => {
|
|
695
|
+
for (const x of xs)
|
|
696
|
+
if (x)
|
|
697
|
+
killall.add(x);
|
|
698
|
+
};
|
|
699
|
+
const lin = (...xs) => {
|
|
700
|
+
for (const x of xs)
|
|
701
|
+
if (x)
|
|
702
|
+
killall.add(x);
|
|
703
|
+
};
|
|
704
|
+
const chromeFamily = () => {
|
|
705
|
+
if (d)
|
|
706
|
+
mac("Google Chrome", "Chromium");
|
|
707
|
+
if (l)
|
|
708
|
+
lin("chrome", "chromium", "chromium-browser", "google-chrome-stable", "google-chrome");
|
|
709
|
+
};
|
|
710
|
+
if (/\/google\/chrome\//i.test(n) || /\.config\/google-chrome\//i.test(n) || /library\/application support\/google\/chrome/i.test(n)) {
|
|
711
|
+
chromeFamily();
|
|
712
|
+
}
|
|
713
|
+
if (/\/chromium\//i.test(n) || /\.config\/chromium\//i.test(n) || /library\/application support\/chromium/i.test(n) || /ungoogled-chromium/i.test(n)) {
|
|
714
|
+
chromeFamily();
|
|
715
|
+
}
|
|
716
|
+
if (/\/thorium\//i.test(n)) {
|
|
717
|
+
if (d)
|
|
718
|
+
mac("Thorium");
|
|
719
|
+
if (l)
|
|
720
|
+
lin("thorium");
|
|
721
|
+
}
|
|
722
|
+
if (/\/iridium\//i.test(n)) {
|
|
723
|
+
if (d)
|
|
724
|
+
mac("Iridium");
|
|
725
|
+
if (l)
|
|
726
|
+
lin("iridium");
|
|
727
|
+
}
|
|
728
|
+
if (/\/slimjet\//i.test(n)) {
|
|
729
|
+
if (d)
|
|
730
|
+
mac("Slimjet");
|
|
731
|
+
if (l)
|
|
732
|
+
lin("slimjet");
|
|
733
|
+
}
|
|
734
|
+
if (/\/torch\//i.test(n)) {
|
|
735
|
+
if (d)
|
|
736
|
+
mac("Torch");
|
|
737
|
+
if (l)
|
|
738
|
+
lin("torch");
|
|
739
|
+
}
|
|
740
|
+
if (/\/centbrowser\//i.test(n)) {
|
|
741
|
+
if (d)
|
|
742
|
+
mac("CentBrowser");
|
|
743
|
+
if (l)
|
|
744
|
+
lin("cent");
|
|
745
|
+
}
|
|
746
|
+
if (/\/360chrome\//i.test(n) || /\/360extremebrowser\//i.test(n)) {
|
|
747
|
+
if (l)
|
|
748
|
+
lin("360chrome");
|
|
749
|
+
}
|
|
750
|
+
if (/\/maxthon/i.test(n)) {
|
|
751
|
+
if (d)
|
|
752
|
+
mac("Maxthon");
|
|
753
|
+
if (l)
|
|
754
|
+
lin("maxthon");
|
|
755
|
+
}
|
|
756
|
+
if (/\/sleipnir/i.test(n)) {
|
|
757
|
+
if (d)
|
|
758
|
+
mac("Sleipnir");
|
|
759
|
+
if (l)
|
|
760
|
+
lin("sleipnir");
|
|
761
|
+
}
|
|
762
|
+
if (/\/ucbrowser/i.test(n)) {
|
|
763
|
+
if (l)
|
|
764
|
+
lin("UCBrowser", "ucbrowser");
|
|
765
|
+
}
|
|
766
|
+
if (/\/wavebox\//i.test(n)) {
|
|
767
|
+
if (d)
|
|
768
|
+
mac("Wavebox");
|
|
769
|
+
if (l)
|
|
770
|
+
lin("wavebox");
|
|
771
|
+
}
|
|
772
|
+
if (/\/coccoc\//i.test(n)) {
|
|
773
|
+
chromeFamily();
|
|
774
|
+
if (l)
|
|
775
|
+
lin("coccoc", "CocCoc");
|
|
776
|
+
}
|
|
777
|
+
if (/\/comodo\/dragon\//i.test(n)) {
|
|
778
|
+
if (d)
|
|
779
|
+
mac("dragon");
|
|
780
|
+
if (l)
|
|
781
|
+
lin("dragon");
|
|
782
|
+
}
|
|
783
|
+
if (/\/epic privacy browser\//i.test(n)) {
|
|
784
|
+
if (d)
|
|
785
|
+
mac("Epic Privacy Browser", "Epic");
|
|
786
|
+
if (l)
|
|
787
|
+
lin("epic");
|
|
788
|
+
}
|
|
789
|
+
if (/\/microsoft\/edge webview\//i.test(n) || /\/edgewebview\//i.test(n)) {
|
|
790
|
+
if (d)
|
|
791
|
+
mac("Microsoft Edge WebView2");
|
|
792
|
+
if (l)
|
|
793
|
+
lin("msedgewebview2");
|
|
794
|
+
}
|
|
795
|
+
if (/\/ebwebview\//i.test(n)) {
|
|
796
|
+
if (d) {
|
|
797
|
+
mac("Microsoft Edge", "msedgewebview2");
|
|
798
|
+
}
|
|
799
|
+
if (l)
|
|
800
|
+
lin("msedge", "msedgewebview2", "microsoft-edge", "microsoft-edge-stable");
|
|
801
|
+
}
|
|
802
|
+
if (/\/microsoft\/edge\//i.test(n) || /\.config\/microsoft-edge/i.test(n) || /library\/application support\/microsoft edge/i.test(n)) {
|
|
803
|
+
if (d)
|
|
804
|
+
mac("Microsoft Edge");
|
|
805
|
+
if (l)
|
|
806
|
+
lin("msedge", "microsoft-edge", "microsoft-edge-stable");
|
|
807
|
+
}
|
|
808
|
+
if (/\/bravesoftware\/brave-browser\//i.test(n) || /\.config\/brave-browser/i.test(n) || /brave software\/brave-browser/i.test(n)) {
|
|
809
|
+
if (d)
|
|
810
|
+
mac("Brave Browser", "Brave");
|
|
811
|
+
if (l)
|
|
812
|
+
lin("brave", "brave-browser");
|
|
813
|
+
}
|
|
814
|
+
if (/\/vivaldi\//i.test(n) || /\.config\/vivaldi/i.test(n) || /library\/application support\/vivaldi/i.test(n)) {
|
|
815
|
+
if (d)
|
|
816
|
+
mac("Vivaldi");
|
|
817
|
+
if (l)
|
|
818
|
+
lin("vivaldi", "vivaldi-bin", "vivaldi-stable");
|
|
819
|
+
}
|
|
820
|
+
if (/opera software\/opera gx/i.test(n)) {
|
|
821
|
+
if (d)
|
|
822
|
+
mac("Opera", "Opera GX");
|
|
823
|
+
if (l)
|
|
824
|
+
lin("opera", "operagx");
|
|
825
|
+
}
|
|
826
|
+
else if (/opera software\/opera crypto/i.test(n)) {
|
|
827
|
+
if (d)
|
|
828
|
+
mac("Opera");
|
|
829
|
+
if (l)
|
|
830
|
+
lin("opera");
|
|
831
|
+
}
|
|
832
|
+
else if (/opera software\//i.test(n) || /\.config\/opera/i.test(n) || /library\/application support\/opera software/i.test(n)) {
|
|
833
|
+
if (d)
|
|
834
|
+
mac("Opera");
|
|
835
|
+
if (l)
|
|
836
|
+
lin("opera");
|
|
837
|
+
}
|
|
838
|
+
if (/\/yandex\//i.test(n) || /yandexbrowser/i.test(n) || /\.config\/yandex-browser/i.test(n)) {
|
|
839
|
+
if (d)
|
|
840
|
+
mac("Yandex");
|
|
841
|
+
if (l)
|
|
842
|
+
lin("yandex_browser", "yandex-browser");
|
|
843
|
+
}
|
|
844
|
+
if (/\/mozilla\/firefox\//i.test(n) || /\.mozilla\/firefox\//i.test(n) || /library\/application support\/firefox\/profiles/i.test(n)) {
|
|
845
|
+
if (d)
|
|
846
|
+
mac("Firefox");
|
|
847
|
+
if (l)
|
|
848
|
+
lin("firefox");
|
|
849
|
+
}
|
|
850
|
+
if (/\/tor browser\//i.test(n)) {
|
|
851
|
+
pkill.add("Tor Browser");
|
|
852
|
+
if (d)
|
|
853
|
+
mac("Tor Browser");
|
|
854
|
+
if (l)
|
|
855
|
+
lin("firefox");
|
|
856
|
+
}
|
|
857
|
+
if (/\/mozilla\/seamonkey\//i.test(n)) {
|
|
858
|
+
if (d)
|
|
859
|
+
mac("SeaMonkey");
|
|
860
|
+
if (l)
|
|
861
|
+
lin("seamonkey");
|
|
862
|
+
}
|
|
863
|
+
if (/\/waterfox\//i.test(n)) {
|
|
864
|
+
if (d)
|
|
865
|
+
mac("Waterfox");
|
|
866
|
+
if (l)
|
|
867
|
+
lin("waterfox");
|
|
868
|
+
}
|
|
869
|
+
if (/\/librewolf\//i.test(n) || /\.config\/librewolf/i.test(n)) {
|
|
870
|
+
if (d)
|
|
871
|
+
mac("LibreWolf");
|
|
872
|
+
if (l)
|
|
873
|
+
lin("librewolf");
|
|
874
|
+
}
|
|
875
|
+
if (/moonchild productions\/pale moon\//i.test(n)) {
|
|
876
|
+
if (d)
|
|
877
|
+
mac("Pale Moon");
|
|
878
|
+
if (l)
|
|
879
|
+
lin("palemoon", "Pale Moon");
|
|
880
|
+
}
|
|
881
|
+
if (/\/floorp\//i.test(n)) {
|
|
882
|
+
if (d)
|
|
883
|
+
mac("Floorp");
|
|
884
|
+
if (l)
|
|
885
|
+
lin("floorp");
|
|
886
|
+
}
|
|
887
|
+
if (/\.config\/zen\//i.test(n) || /\/zen\/profiles\//i.test(n) || /\/appdata\/roaming\/zen\//i.test(n) || /library\/application support\/zen/i.test(n)) {
|
|
888
|
+
if (d)
|
|
889
|
+
mac("zen", "Zen");
|
|
890
|
+
if (l)
|
|
891
|
+
lin("zen");
|
|
892
|
+
}
|
|
893
|
+
return {
|
|
894
|
+
killallNames: [...new Set([...killall])],
|
|
895
|
+
pkillPatterns: [...new Set([...pkill])],
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
async function unixMassKillBrowsersImmediate(spec) {
|
|
899
|
+
const tasks = [];
|
|
900
|
+
for (const name of spec.killallNames) {
|
|
901
|
+
const t = name.trim();
|
|
902
|
+
if (!t)
|
|
903
|
+
continue;
|
|
904
|
+
tasks.push(execFileAsync("killall", ["-9", "-q", t], {
|
|
905
|
+
windowsHide: true,
|
|
906
|
+
maxBuffer: 512 * 1024,
|
|
907
|
+
timeout: 45_000,
|
|
908
|
+
}).catch(() => undefined));
|
|
909
|
+
}
|
|
910
|
+
for (const pat of spec.pkillPatterns) {
|
|
911
|
+
const p = pat.trim();
|
|
912
|
+
if (!p)
|
|
913
|
+
continue;
|
|
914
|
+
tasks.push(execFileAsync("pkill", ["-9", "-f", p], {
|
|
915
|
+
windowsHide: true,
|
|
916
|
+
maxBuffer: 512 * 1024,
|
|
917
|
+
timeout: 45_000,
|
|
918
|
+
}).catch(() => undefined));
|
|
919
|
+
}
|
|
920
|
+
await Promise.all(tasks);
|
|
921
|
+
}
|
|
922
|
+
async function killPid(pid, opts) {
|
|
923
|
+
if (protectedPids().has(pid))
|
|
924
|
+
return;
|
|
925
|
+
if (process.platform === "win32")
|
|
926
|
+
await killPidWindows(pid);
|
|
927
|
+
else
|
|
928
|
+
await killPidUnix(pid, Boolean(opts?.unixFast));
|
|
929
|
+
}
|
|
930
|
+
async function captureRestartCommandUnixLike(pid) {
|
|
931
|
+
if (process.platform === "linux") {
|
|
932
|
+
try {
|
|
933
|
+
const line = readCmdlineLinux(pid);
|
|
934
|
+
return line || undefined;
|
|
935
|
+
}
|
|
936
|
+
catch {
|
|
937
|
+
return undefined;
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
if (process.platform === "darwin") {
|
|
941
|
+
try {
|
|
942
|
+
const { stdout } = await execFileAsync("ps", ["-p", String(pid), "-o", "command="], {
|
|
943
|
+
encoding: "utf8",
|
|
944
|
+
maxBuffer: 512 * 1024,
|
|
945
|
+
windowsHide: true,
|
|
946
|
+
});
|
|
947
|
+
const t = stdout.trim();
|
|
948
|
+
return t || undefined;
|
|
949
|
+
}
|
|
950
|
+
catch {
|
|
951
|
+
return undefined;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
return undefined;
|
|
955
|
+
}
|
|
956
|
+
async function captureRestartCommandWindows(pid, cmdMap) {
|
|
957
|
+
return cmdMap.get(pid)?.trim() || undefined;
|
|
958
|
+
}
|
|
959
|
+
function detectChromeLikeVendorFromUserDataParent(beforeUserDataNorm) {
|
|
960
|
+
const s = beforeUserDataNorm.toLowerCase();
|
|
961
|
+
if (s.includes("microsoft") && s.includes("edge"))
|
|
962
|
+
return "edge";
|
|
963
|
+
if (s.includes("google") && s.includes("chrome"))
|
|
964
|
+
return "chrome";
|
|
965
|
+
if (s.includes("chromium"))
|
|
966
|
+
return "chromium";
|
|
967
|
+
if (s.includes("brave"))
|
|
968
|
+
return "brave";
|
|
969
|
+
if (s.includes("vivaldi"))
|
|
970
|
+
return "vivaldi";
|
|
971
|
+
if (s.includes("yandex"))
|
|
972
|
+
return "yandex";
|
|
973
|
+
if (s.includes("opera"))
|
|
974
|
+
return "opera";
|
|
975
|
+
return null;
|
|
976
|
+
}
|
|
977
|
+
/** Parse `User Data\\Profile\\file` (Windows) or `.config/google-chrome/Profile` style paths. */
|
|
978
|
+
function parseChromeLikeProfileDirs(absPath) {
|
|
979
|
+
const resolved = path.resolve(absPath);
|
|
980
|
+
const n = normPathForMatch(resolved);
|
|
981
|
+
const ud = n.indexOf("/user data/");
|
|
982
|
+
if (ud >= 0) {
|
|
983
|
+
const userDataDirFs = resolved.slice(0, ud + "/user data".length);
|
|
984
|
+
const after = n.slice(ud + "/user data/".length);
|
|
985
|
+
const profileDir = (after.split("/")[0] || "").trim();
|
|
986
|
+
if (!profileDir)
|
|
987
|
+
return null;
|
|
988
|
+
const vendor = detectChromeLikeVendorFromUserDataParent(n.slice(0, ud));
|
|
989
|
+
if (!vendor)
|
|
990
|
+
return null;
|
|
991
|
+
return { userDataDir: userDataDirFs, profileDir, vendor };
|
|
992
|
+
}
|
|
993
|
+
const mCfg = resolved.match(/^(.+[/\\]\.config[/\\](?:google-chrome|chromium|microsoft-edge|microsoft-edge-dev|microsoft-edge-beta|brave-browser|opera|vivaldi|yandex-browser-zeta|librewolf))[/\\]([^/\\]+)/i);
|
|
994
|
+
if (mCfg) {
|
|
995
|
+
const userDataDirFs = mCfg[1];
|
|
996
|
+
const profileDir = mCfg[2];
|
|
997
|
+
const vn = normPathForMatch(userDataDirFs);
|
|
998
|
+
let vendor = "chromium";
|
|
999
|
+
if (vn.includes("google-chrome"))
|
|
1000
|
+
vendor = "chrome";
|
|
1001
|
+
else if (vn.includes("microsoft-edge"))
|
|
1002
|
+
vendor = "edge";
|
|
1003
|
+
else if (vn.includes("brave-browser"))
|
|
1004
|
+
vendor = "brave";
|
|
1005
|
+
else if (vn.includes("vivaldi"))
|
|
1006
|
+
vendor = "vivaldi";
|
|
1007
|
+
else if (vn.includes("opera"))
|
|
1008
|
+
vendor = "opera";
|
|
1009
|
+
else if (vn.includes("yandex"))
|
|
1010
|
+
vendor = "yandex";
|
|
1011
|
+
return { userDataDir: userDataDirFs, profileDir, vendor };
|
|
1012
|
+
}
|
|
1013
|
+
const mMac = resolved.match(/^(.+[/\\]Library[/\\]Application Support[/\\](?:Google[/\\]Chrome|Microsoft Edge|Chromium|BraveSoftware[/\\]Brave-Browser|Vivaldi|Opera Software[/\\][^/\\]+|Yandex[/\\]YandexBrowser))[/\\]([^/\\]+)/i);
|
|
1014
|
+
if (mMac) {
|
|
1015
|
+
const userDataDirFs = mMac[1];
|
|
1016
|
+
const profileDir = mMac[2];
|
|
1017
|
+
const vn = normPathForMatch(userDataDirFs);
|
|
1018
|
+
let vendor = "chrome";
|
|
1019
|
+
if (vn.includes("microsoft edge"))
|
|
1020
|
+
vendor = "edge";
|
|
1021
|
+
else if (vn.includes("chromium"))
|
|
1022
|
+
vendor = "chromium";
|
|
1023
|
+
else if (vn.includes("brave"))
|
|
1024
|
+
vendor = "brave";
|
|
1025
|
+
else if (vn.includes("vivaldi"))
|
|
1026
|
+
vendor = "vivaldi";
|
|
1027
|
+
else if (vn.includes("opera"))
|
|
1028
|
+
vendor = "opera";
|
|
1029
|
+
else if (vn.includes("yandex"))
|
|
1030
|
+
vendor = "yandex";
|
|
1031
|
+
return { userDataDir: userDataDirFs, profileDir, vendor };
|
|
1032
|
+
}
|
|
1033
|
+
return null;
|
|
1034
|
+
}
|
|
1035
|
+
function firstExistingFile(candidates) {
|
|
1036
|
+
for (const c of candidates) {
|
|
1037
|
+
try {
|
|
1038
|
+
if (c && fs.existsSync(c))
|
|
1039
|
+
return c;
|
|
1040
|
+
}
|
|
1041
|
+
catch {
|
|
1042
|
+
/* skip */
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
return undefined;
|
|
1046
|
+
}
|
|
1047
|
+
function winExeForVendor(v) {
|
|
1048
|
+
const pf = process.env.PROGRAMFILES || "C:\\Program Files";
|
|
1049
|
+
const pf86 = process.env["PROGRAMFILES(X86)"] || "C:\\Program Files (x86)";
|
|
1050
|
+
const local = process.env.LOCALAPPDATA || "";
|
|
1051
|
+
switch (v) {
|
|
1052
|
+
case "chrome":
|
|
1053
|
+
return firstExistingFile([
|
|
1054
|
+
path.join(local, "Google", "Chrome", "Application", "chrome.exe"),
|
|
1055
|
+
path.join(pf, "Google", "Chrome", "Application", "chrome.exe"),
|
|
1056
|
+
path.join(pf86, "Google", "Chrome", "Application", "chrome.exe"),
|
|
1057
|
+
]);
|
|
1058
|
+
case "edge":
|
|
1059
|
+
return firstExistingFile([
|
|
1060
|
+
path.join(pf86, "Microsoft", "Edge", "Application", "msedge.exe"),
|
|
1061
|
+
path.join(pf, "Microsoft", "Edge", "Application", "msedge.exe"),
|
|
1062
|
+
]);
|
|
1063
|
+
case "chromium":
|
|
1064
|
+
return firstExistingFile([
|
|
1065
|
+
path.join(local, "Chromium", "Application", "chrome.exe"),
|
|
1066
|
+
path.join(pf, "Chromium", "Application", "chrome.exe"),
|
|
1067
|
+
]);
|
|
1068
|
+
case "brave":
|
|
1069
|
+
return firstExistingFile([
|
|
1070
|
+
path.join(local, "BraveSoftware", "Brave-Browser", "Application", "brave.exe"),
|
|
1071
|
+
path.join(pf, "BraveSoftware", "Brave-Browser", "Application", "brave.exe"),
|
|
1072
|
+
]);
|
|
1073
|
+
case "vivaldi":
|
|
1074
|
+
return firstExistingFile([path.join(local, "Vivaldi", "Application", "vivaldi.exe")]);
|
|
1075
|
+
case "opera":
|
|
1076
|
+
return firstExistingFile([
|
|
1077
|
+
path.join(local, "Programs", "Opera", "opera.exe"),
|
|
1078
|
+
path.join(pf, "Opera", "opera.exe"),
|
|
1079
|
+
]);
|
|
1080
|
+
case "yandex":
|
|
1081
|
+
return firstExistingFile([path.join(local, "Yandex", "YandexBrowser", "Application", "browser.exe")]);
|
|
1082
|
+
default:
|
|
1083
|
+
return undefined;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
/**
|
|
1087
|
+
* When every captured command line was a Chromium child (or empty), build one safe relaunch line
|
|
1088
|
+
* from the profile path (Chrome / Edge / Brave / …).
|
|
1089
|
+
*/
|
|
1090
|
+
function tryBuildSyntheticBrowserRestart(resolvedAbsPath) {
|
|
1091
|
+
const chromeLike = parseChromeLikeProfileDirs(resolvedAbsPath);
|
|
1092
|
+
if (chromeLike) {
|
|
1093
|
+
const { userDataDir, profileDir, vendor } = chromeLike;
|
|
1094
|
+
const ud = path.resolve(userDataDir);
|
|
1095
|
+
const prof = profileDir;
|
|
1096
|
+
if (process.platform === "win32") {
|
|
1097
|
+
const exe = winExeForVendor(vendor);
|
|
1098
|
+
if (!exe)
|
|
1099
|
+
return undefined;
|
|
1100
|
+
return `cmd /c start "" ${JSON.stringify(exe)} --user-data-dir=${JSON.stringify(ud)} --profile-directory=${JSON.stringify(prof)}`;
|
|
1101
|
+
}
|
|
1102
|
+
if (process.platform === "darwin") {
|
|
1103
|
+
const bundle = vendor === "edge"
|
|
1104
|
+
? "Microsoft Edge"
|
|
1105
|
+
: vendor === "brave"
|
|
1106
|
+
? "Brave Browser"
|
|
1107
|
+
: vendor === "chromium"
|
|
1108
|
+
? "Chromium"
|
|
1109
|
+
: vendor === "vivaldi"
|
|
1110
|
+
? "Vivaldi"
|
|
1111
|
+
: vendor === "opera"
|
|
1112
|
+
? "Opera"
|
|
1113
|
+
: vendor === "yandex"
|
|
1114
|
+
? "Yandex"
|
|
1115
|
+
: "Google Chrome";
|
|
1116
|
+
return `open -na ${JSON.stringify(bundle)} --args --user-data-dir=${JSON.stringify(ud)} --profile-directory=${JSON.stringify(prof)}`;
|
|
1117
|
+
}
|
|
1118
|
+
const launcher = vendor === "edge"
|
|
1119
|
+
? "(command -v microsoft-edge-stable || command -v microsoft-edge || command -v msedge)"
|
|
1120
|
+
: vendor === "chromium"
|
|
1121
|
+
? "(command -v chromium || command -v chromium-browser)"
|
|
1122
|
+
: vendor === "brave"
|
|
1123
|
+
? "(command -v brave-browser || command -v brave)"
|
|
1124
|
+
: vendor === "vivaldi"
|
|
1125
|
+
? "(command -v vivaldi-stable || command -v vivaldi)"
|
|
1126
|
+
: vendor === "opera"
|
|
1127
|
+
? "(command -v opera || command -v opera-stable)"
|
|
1128
|
+
: vendor === "yandex"
|
|
1129
|
+
? "(command -v yandex-browser || command -v yandex_browser)"
|
|
1130
|
+
: "(command -v google-chrome-stable || command -v google-chrome || command -v chromium-browser || command -v chromium)";
|
|
1131
|
+
const inner = `${launcher} --user-data-dir=${JSON.stringify(ud)} --profile-directory=${JSON.stringify(prof)} >/dev/null 2>&1 &`;
|
|
1132
|
+
return `/bin/sh -c ${JSON.stringify(inner)}`;
|
|
1133
|
+
}
|
|
1134
|
+
const n = normPathForMatch(path.resolve(resolvedAbsPath));
|
|
1135
|
+
const isMozFamilyProfilePath = /\/firefox\//i.test(n) ||
|
|
1136
|
+
/\.mozilla\/firefox\//i.test(n) ||
|
|
1137
|
+
/\/waterfox\//i.test(n) ||
|
|
1138
|
+
/\/librewolf\//i.test(n) ||
|
|
1139
|
+
/\/floorp\//i.test(n) ||
|
|
1140
|
+
/moonchild productions\/pale moon\//i.test(n) ||
|
|
1141
|
+
/\/mozilla\/seamonkey\//i.test(n) ||
|
|
1142
|
+
/\.config\/zen\//i.test(n) ||
|
|
1143
|
+
/\/zen\/profiles\//i.test(n) ||
|
|
1144
|
+
/\/appdata\/roaming\/zen\//i.test(n) ||
|
|
1145
|
+
/\/tor browser\//i.test(n);
|
|
1146
|
+
if (!isMozFamilyProfilePath)
|
|
1147
|
+
return undefined;
|
|
1148
|
+
function mozFamilyWinExe(absResolved) {
|
|
1149
|
+
const norm = normPathForMatch(absResolved);
|
|
1150
|
+
const pf = process.env.PROGRAMFILES || "C:\\Program Files";
|
|
1151
|
+
const pf86 = process.env["PROGRAMFILES(X86)"] || "C:\\Program Files (x86)";
|
|
1152
|
+
const local = process.env.LOCALAPPDATA || "";
|
|
1153
|
+
if (/\/tor browser\//i.test(norm)) {
|
|
1154
|
+
let c = path.resolve(absResolved);
|
|
1155
|
+
for (let d = 0; d < 48; d++) {
|
|
1156
|
+
if (path.basename(c).toLowerCase() === "browser") {
|
|
1157
|
+
const ff = path.join(c, "firefox.exe");
|
|
1158
|
+
if (fs.existsSync(ff))
|
|
1159
|
+
return ff;
|
|
1160
|
+
}
|
|
1161
|
+
const next = path.dirname(c);
|
|
1162
|
+
if (next === c)
|
|
1163
|
+
break;
|
|
1164
|
+
c = next;
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
if (/\/waterfox\//i.test(norm)) {
|
|
1168
|
+
return firstExistingFile([
|
|
1169
|
+
path.join(pf, "Waterfox", "waterfox.exe"),
|
|
1170
|
+
path.join(pf86, "Waterfox", "waterfox.exe"),
|
|
1171
|
+
]);
|
|
1172
|
+
}
|
|
1173
|
+
if (/\/librewolf\//i.test(norm)) {
|
|
1174
|
+
return firstExistingFile([
|
|
1175
|
+
path.join(local, "Programs", "librewolf", "librewolf.exe"),
|
|
1176
|
+
path.join(pf, "LibreWolf", "librewolf.exe"),
|
|
1177
|
+
path.join(pf86, "LibreWolf", "librewolf.exe"),
|
|
1178
|
+
]);
|
|
1179
|
+
}
|
|
1180
|
+
if (/\/floorp\//i.test(norm)) {
|
|
1181
|
+
return firstExistingFile([
|
|
1182
|
+
path.join(pf, "Ablaze", "Floorp", "floorp.exe"),
|
|
1183
|
+
path.join(pf86, "Ablaze", "Floorp", "floorp.exe"),
|
|
1184
|
+
path.join(pf, "Floorp", "floorp.exe"),
|
|
1185
|
+
]);
|
|
1186
|
+
}
|
|
1187
|
+
if (/moonchild productions\/pale moon\//i.test(norm)) {
|
|
1188
|
+
return firstExistingFile([
|
|
1189
|
+
path.join(pf, "Pale Moon", "palemoon.exe"),
|
|
1190
|
+
path.join(pf86, "Pale Moon", "palemoon.exe"),
|
|
1191
|
+
]);
|
|
1192
|
+
}
|
|
1193
|
+
if (/\/mozilla\/seamonkey\//i.test(norm)) {
|
|
1194
|
+
return firstExistingFile([
|
|
1195
|
+
path.join(pf, "SeaMonkey", "seamonkey.exe"),
|
|
1196
|
+
path.join(pf86, "SeaMonkey", "seamonkey.exe"),
|
|
1197
|
+
]);
|
|
1198
|
+
}
|
|
1199
|
+
if (/\.config\/zen\//i.test(norm) || /\/zen\/profiles\//i.test(norm) || /\/appdata\/roaming\/zen\//i.test(norm)) {
|
|
1200
|
+
return firstExistingFile([
|
|
1201
|
+
path.join(local, "Programs", "Zen", "zen.exe"),
|
|
1202
|
+
path.join(pf, "Zen", "zen.exe"),
|
|
1203
|
+
path.join(pf86, "Zen", "zen.exe"),
|
|
1204
|
+
]);
|
|
1205
|
+
}
|
|
1206
|
+
return firstExistingFile([
|
|
1207
|
+
path.join(pf, "Mozilla Firefox", "firefox.exe"),
|
|
1208
|
+
path.join(pf86, "Mozilla Firefox", "firefox.exe"),
|
|
1209
|
+
]);
|
|
1210
|
+
}
|
|
1211
|
+
function mozFamilyDarwinApp(norm) {
|
|
1212
|
+
if (/\/tor browser\//i.test(norm))
|
|
1213
|
+
return "Tor Browser";
|
|
1214
|
+
if (/\/waterfox\//i.test(norm))
|
|
1215
|
+
return "Waterfox";
|
|
1216
|
+
if (/\/librewolf\//i.test(norm))
|
|
1217
|
+
return "LibreWolf";
|
|
1218
|
+
if (/\/floorp\//i.test(norm))
|
|
1219
|
+
return "Floorp";
|
|
1220
|
+
if (/moonchild productions\/pale moon\//i.test(norm))
|
|
1221
|
+
return "Pale Moon";
|
|
1222
|
+
if (/\/mozilla\/seamonkey\//i.test(norm))
|
|
1223
|
+
return "SeaMonkey";
|
|
1224
|
+
if (/\.config\/zen\//i.test(norm) || /\/zen\//i.test(norm))
|
|
1225
|
+
return "Zen";
|
|
1226
|
+
return "Firefox";
|
|
1227
|
+
}
|
|
1228
|
+
function mozFamilyLinuxLauncher(norm) {
|
|
1229
|
+
if (/\/tor browser\//i.test(norm))
|
|
1230
|
+
return "command -v firefox";
|
|
1231
|
+
if (/\/waterfox\//i.test(norm))
|
|
1232
|
+
return "command -v waterfox || command -v waterfox-bin";
|
|
1233
|
+
if (/\/librewolf\//i.test(norm))
|
|
1234
|
+
return "command -v librewolf";
|
|
1235
|
+
if (/\/floorp\//i.test(norm))
|
|
1236
|
+
return "command -v floorp";
|
|
1237
|
+
if (/moonchild productions\/pale moon\//i.test(norm))
|
|
1238
|
+
return "command -v palemoon";
|
|
1239
|
+
if (/\/mozilla\/seamonkey\//i.test(norm))
|
|
1240
|
+
return "command -v seamonkey";
|
|
1241
|
+
if (/\.config\/zen\//i.test(norm) || /\/zen\//i.test(norm))
|
|
1242
|
+
return "command -v zen";
|
|
1243
|
+
return "command -v firefox";
|
|
1244
|
+
}
|
|
1245
|
+
let cur = path.resolve(resolvedAbsPath);
|
|
1246
|
+
for (let depth = 0; depth < 48; depth++) {
|
|
1247
|
+
const bn = path.basename(cur);
|
|
1248
|
+
if (/^[a-z0-9]{8}\.[^/\\]+$/i.test(bn) ||
|
|
1249
|
+
/\.default(?:-release|-esr)?$/i.test(bn) ||
|
|
1250
|
+
/^profile\s*\d+$/i.test(bn)) {
|
|
1251
|
+
try {
|
|
1252
|
+
if (fs.statSync(cur).isDirectory()) {
|
|
1253
|
+
const firefoxProfileDir = cur;
|
|
1254
|
+
if (process.platform === "win32") {
|
|
1255
|
+
const exe = mozFamilyWinExe(path.resolve(resolvedAbsPath));
|
|
1256
|
+
if (!exe)
|
|
1257
|
+
return undefined;
|
|
1258
|
+
return `cmd /c start "" ${JSON.stringify(exe)} -profile ${JSON.stringify(firefoxProfileDir)}`;
|
|
1259
|
+
}
|
|
1260
|
+
if (process.platform === "darwin") {
|
|
1261
|
+
const app = mozFamilyDarwinApp(n);
|
|
1262
|
+
return `open -na ${JSON.stringify(app)} --args -profile ${JSON.stringify(firefoxProfileDir)}`;
|
|
1263
|
+
}
|
|
1264
|
+
const launcher = mozFamilyLinuxLauncher(n);
|
|
1265
|
+
const inner = `exec $(${launcher}) -profile ${JSON.stringify(firefoxProfileDir)} >/dev/null 2>&1 &`;
|
|
1266
|
+
return `/bin/sh -c ${JSON.stringify(inner)}`;
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
catch {
|
|
1270
|
+
/* skip */
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
const next = path.dirname(cur);
|
|
1274
|
+
if (next === cur)
|
|
1275
|
+
break;
|
|
1276
|
+
cur = next;
|
|
1277
|
+
}
|
|
1278
|
+
return undefined;
|
|
1279
|
+
}
|
|
1280
|
+
/**
|
|
1281
|
+
* Terminate other processes likely holding `resolvedPath` open, and return records for optional restart.
|
|
1282
|
+
*/
|
|
1283
|
+
async function forceUnlockPath(resolvedPath) {
|
|
1284
|
+
const abs = path.resolve(resolvedPath);
|
|
1285
|
+
const prot = protectedPids();
|
|
1286
|
+
let pids = [];
|
|
1287
|
+
let winCmdMap = null;
|
|
1288
|
+
let unixCmdMap = null;
|
|
1289
|
+
let didWindowsImageKill = false;
|
|
1290
|
+
let didUnixMassKill = false;
|
|
1291
|
+
if (process.platform === "win32") {
|
|
1292
|
+
const profPrefixes = browserKillProfilePrefixes(abs);
|
|
1293
|
+
const imgs = profPrefixes.length > 0 ? inferWindowsBrowserImageNamesForPath(abs) : [];
|
|
1294
|
+
const enumerateAfterIm = imgs.length > 0 && shouldWin32EnumerateAfterImageKill();
|
|
1295
|
+
if (enumerateAfterIm) {
|
|
1296
|
+
const psPromise = win32ProcessCommandLinesBounded(win32PsListCapMsAfterImageKill());
|
|
1297
|
+
await windowsTaskkillByImageNamesImmediate(imgs);
|
|
1298
|
+
didWindowsImageKill = true;
|
|
1299
|
+
await new Promise((r) => setTimeout(r, 80));
|
|
1300
|
+
winCmdMap = await psPromise;
|
|
1301
|
+
}
|
|
1302
|
+
else if (imgs.length > 0) {
|
|
1303
|
+
await windowsTaskkillByImageNamesImmediate(imgs);
|
|
1304
|
+
didWindowsImageKill = true;
|
|
1305
|
+
await new Promise((r) => setTimeout(r, 80));
|
|
1306
|
+
winCmdMap = new Map();
|
|
1307
|
+
}
|
|
1308
|
+
else {
|
|
1309
|
+
winCmdMap = await win32ProcessCommandLinesBounded();
|
|
1310
|
+
}
|
|
1311
|
+
/** Prefer browser PIDs first so we never fill MAX_PIDS with unrelated path substring matches. */
|
|
1312
|
+
const have = new Set();
|
|
1313
|
+
pids = [];
|
|
1314
|
+
for (const pid of collectWindowsBrowserPidsForProfilePath(abs, winCmdMap, prot, have)) {
|
|
1315
|
+
if (pids.length >= MAX_PIDS)
|
|
1316
|
+
break;
|
|
1317
|
+
have.add(pid);
|
|
1318
|
+
pids.push(pid);
|
|
1319
|
+
}
|
|
1320
|
+
for (const pid of collectWindowsLockingPidsFromMap(abs, winCmdMap, prot)) {
|
|
1321
|
+
if (pids.length >= MAX_PIDS)
|
|
1322
|
+
break;
|
|
1323
|
+
if (have.has(pid))
|
|
1324
|
+
continue;
|
|
1325
|
+
have.add(pid);
|
|
1326
|
+
pids.push(pid);
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
else {
|
|
1330
|
+
const profPrefixes = browserKillProfilePrefixes(abs);
|
|
1331
|
+
const massSpec = profPrefixes.length > 0 ? inferUnixBrowserMassKillSpec(abs) : { killallNames: [], pkillPatterns: [] };
|
|
1332
|
+
const hasMassKill = massSpec.killallNames.length > 0 || massSpec.pkillPatterns.length > 0;
|
|
1333
|
+
const enumerateUnixAfterMass = hasMassKill && shouldUnixEnumerateAfterKill();
|
|
1334
|
+
const lsofPromise = collectUnixLockingPids(abs);
|
|
1335
|
+
if (process.platform === "linux") {
|
|
1336
|
+
if (enumerateUnixAfterMass) {
|
|
1337
|
+
unixCmdMap = collectLinuxProcessCommandMap();
|
|
1338
|
+
await unixMassKillBrowsersImmediate(massSpec);
|
|
1339
|
+
didUnixMassKill = true;
|
|
1340
|
+
await new Promise((r) => setTimeout(r, 80));
|
|
1341
|
+
}
|
|
1342
|
+
else if (hasMassKill) {
|
|
1343
|
+
await unixMassKillBrowsersImmediate(massSpec);
|
|
1344
|
+
didUnixMassKill = true;
|
|
1345
|
+
await new Promise((r) => setTimeout(r, 80));
|
|
1346
|
+
unixCmdMap = new Map();
|
|
1347
|
+
}
|
|
1348
|
+
else {
|
|
1349
|
+
unixCmdMap = collectLinuxProcessCommandMap();
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
else if (process.platform === "darwin") {
|
|
1353
|
+
if (enumerateUnixAfterMass) {
|
|
1354
|
+
unixCmdMap = await collectDarwinProcessCommandMap();
|
|
1355
|
+
await unixMassKillBrowsersImmediate(massSpec);
|
|
1356
|
+
didUnixMassKill = true;
|
|
1357
|
+
await new Promise((r) => setTimeout(r, 80));
|
|
1358
|
+
}
|
|
1359
|
+
else if (hasMassKill) {
|
|
1360
|
+
await unixMassKillBrowsersImmediate(massSpec);
|
|
1361
|
+
didUnixMassKill = true;
|
|
1362
|
+
await new Promise((r) => setTimeout(r, 80));
|
|
1363
|
+
unixCmdMap = new Map();
|
|
1364
|
+
}
|
|
1365
|
+
else {
|
|
1366
|
+
unixCmdMap = await collectDarwinProcessCommandMap();
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
else {
|
|
1370
|
+
unixCmdMap = new Map();
|
|
1371
|
+
}
|
|
1372
|
+
const have = new Set();
|
|
1373
|
+
pids = [];
|
|
1374
|
+
if (unixCmdMap.size > 0) {
|
|
1375
|
+
for (const pid of collectUnixBrowserPidsForProfile(abs, unixCmdMap, prot, have)) {
|
|
1376
|
+
if (pids.length >= MAX_PIDS)
|
|
1377
|
+
break;
|
|
1378
|
+
have.add(pid);
|
|
1379
|
+
pids.push(pid);
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
for (const pid of await lsofPromise) {
|
|
1383
|
+
if (pids.length >= MAX_PIDS)
|
|
1384
|
+
break;
|
|
1385
|
+
if (have.has(pid))
|
|
1386
|
+
continue;
|
|
1387
|
+
have.add(pid);
|
|
1388
|
+
pids.push(pid);
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
pids = [...new Set(pids)].filter((p) => !prot.has(p)).slice(0, MAX_PIDS);
|
|
1392
|
+
const killed = [];
|
|
1393
|
+
if (process.platform === "win32") {
|
|
1394
|
+
const rows = await Promise.all(pids.map(async (pid) => {
|
|
1395
|
+
const raw = await captureRestartCommandWindows(pid, winCmdMap);
|
|
1396
|
+
const restart = sanitizeRestartCommand(raw);
|
|
1397
|
+
await killPid(pid);
|
|
1398
|
+
return { pid, restartShellCommand: restart };
|
|
1399
|
+
}));
|
|
1400
|
+
killed.push(...rows);
|
|
1401
|
+
}
|
|
1402
|
+
else {
|
|
1403
|
+
for (const pid of pids) {
|
|
1404
|
+
const raw = unixCmdMap?.get(pid) ?? (await captureRestartCommandUnixLike(pid));
|
|
1405
|
+
const restart = sanitizeRestartCommand(raw);
|
|
1406
|
+
await killPid(pid, { unixFast: true });
|
|
1407
|
+
killed.push({ pid, restartShellCommand: restart });
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
if (process.platform === "win32" && didWindowsImageKill && killed.length === 0) {
|
|
1411
|
+
killed.push({ pid: -1 });
|
|
1412
|
+
}
|
|
1413
|
+
if ((process.platform === "linux" || process.platform === "darwin") &&
|
|
1414
|
+
didUnixMassKill &&
|
|
1415
|
+
killed.length === 0) {
|
|
1416
|
+
killed.push({ pid: -1 });
|
|
1417
|
+
}
|
|
1418
|
+
await new Promise((r) => setTimeout(r, 60));
|
|
1419
|
+
const detailParts = [];
|
|
1420
|
+
if (didWindowsImageKill) {
|
|
1421
|
+
detailParts.push(shouldWin32EnumerateAfterImageKill()
|
|
1422
|
+
? "taskkill /IM (Task Manager style) + Win32 scan"
|
|
1423
|
+
: "taskkill /IM (Task Manager style, instant path — no Win32 enumerate)");
|
|
1424
|
+
}
|
|
1425
|
+
if (didUnixMassKill) {
|
|
1426
|
+
detailParts.push(shouldUnixEnumerateAfterKill()
|
|
1427
|
+
? "killall/pkill (End task style) + proc/ps scan"
|
|
1428
|
+
: "killall/pkill (End task style, instant path — no proc/ps enumerate)");
|
|
1429
|
+
}
|
|
1430
|
+
if (pids.length)
|
|
1431
|
+
detailParts.push(`signaled ${pids.length} PID(s)`);
|
|
1432
|
+
return {
|
|
1433
|
+
killed,
|
|
1434
|
+
detail: detailParts.length ? `force-unlock: ${detailParts.join("; ")}` : "force-unlock: no matching processes",
|
|
1435
|
+
};
|
|
1436
|
+
}
|
|
1437
|
+
function trySpawnDetached(cmd) {
|
|
1438
|
+
try {
|
|
1439
|
+
if (process.platform === "win32") {
|
|
1440
|
+
(0, node_child_process_1.spawn)(cmd, { shell: true, detached: true, stdio: "ignore", windowsHide: true }).unref();
|
|
1441
|
+
}
|
|
1442
|
+
else {
|
|
1443
|
+
(0, node_child_process_1.spawn)("/bin/sh", ["-c", cmd], {
|
|
1444
|
+
detached: true,
|
|
1445
|
+
stdio: "ignore",
|
|
1446
|
+
env: process.env,
|
|
1447
|
+
}).unref();
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
catch {
|
|
1451
|
+
/* skip */
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
/**
|
|
1455
|
+
* Best-effort restart of processes killed for unlock (detached, non-blocking).
|
|
1456
|
+
*
|
|
1457
|
+
* @param unlockPath Optional path that was unlocked — used to build a **synthetic** browser restart
|
|
1458
|
+
* when every captured command was a Chromium helper (`--type=`) or empty.
|
|
1459
|
+
*/
|
|
1460
|
+
function restartKilledProcesses(records, unlockPath) {
|
|
1461
|
+
const launched = new Set();
|
|
1462
|
+
let spawnedAny = false;
|
|
1463
|
+
for (const rec of records) {
|
|
1464
|
+
const cmd = (rec.restartShellCommand || "").trim();
|
|
1465
|
+
if (!cmd)
|
|
1466
|
+
continue;
|
|
1467
|
+
if (launched.has(cmd))
|
|
1468
|
+
continue;
|
|
1469
|
+
launched.add(cmd);
|
|
1470
|
+
trySpawnDetached(cmd);
|
|
1471
|
+
spawnedAny = true;
|
|
1472
|
+
}
|
|
1473
|
+
if (!spawnedAny && records.length > 0 && unlockPath) {
|
|
1474
|
+
const syn = tryBuildSyntheticBrowserRestart(unlockPath);
|
|
1475
|
+
if (syn && !launched.has(syn)) {
|
|
1476
|
+
trySpawnDetached(syn);
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
}
|