@weavetab/mcp 2.5.0-beta.1 → 2.5.0-beta.4
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/AGENTS.md +2 -1
- package/ARCHITECTURE.md +6 -6
- package/LICENSE +1 -1
- package/README.md +164 -802
- package/SECURITY.md +33 -22
- package/TROUBLESHOOTING.md +3 -3
- package/dist/cdp/bridge.d.ts +10 -0
- package/dist/cdp/bridge.js +285 -78
- package/dist/cdp/connector.d.ts +7 -3
- package/dist/cdp/connector.js +196 -75
- package/dist/cdp/helpers.d.ts +4 -0
- package/dist/cdp/helpers.js +181 -0
- package/dist/cdp/lock.d.ts +9 -6
- package/dist/cdp/lock.js +72 -59
- package/dist/cdp/omni_broker.d.ts +2 -1
- package/dist/cdp/omni_broker.js +13 -7
- package/dist/cdp/profiles.d.ts +10 -0
- package/dist/cdp/profiles.js +65 -0
- package/dist/cdp/walker.d.ts +25 -1
- package/dist/cdp/walker.js +354 -7
- package/dist/cli/plugin.d.ts +0 -2
- package/dist/cli/plugin.js +51 -696
- package/dist/cli/updater.d.ts +1 -1
- package/dist/cli/updater.js +27 -7
- package/dist/config/loader.d.ts +33 -6
- package/dist/config/loader.js +174 -22
- package/dist/desktop/discovery.d.ts +44 -0
- package/dist/desktop/discovery.js +364 -0
- package/dist/index.js +140 -64
- package/dist/intelligence/marks.d.ts +36 -0
- package/dist/intelligence/marks.js +30 -0
- package/dist/intelligence/patternCompiler.d.ts +118 -0
- package/dist/intelligence/patternCompiler.js +259 -0
- package/dist/intelligence/sessionMemory.d.ts +2 -0
- package/dist/intelligence/sessionMemory.js +18 -8
- package/dist/overlay/ghost.js +2 -2
- package/dist/overlay/injector.js +3 -5
- package/dist/plugin/extension_bridge.d.ts +40 -0
- package/dist/plugin/extension_bridge.js +158 -0
- package/dist/plugin/interface.d.ts +151 -15
- package/dist/plugin/loader.d.ts +19 -1
- package/dist/plugin/loader.js +331 -63
- package/dist/plugin/pipeline.d.ts +75 -0
- package/dist/plugin/pipeline.js +183 -0
- package/dist/plugin/registry.js +12 -1
- package/dist/plugin/storage.d.ts +51 -0
- package/dist/plugin/storage.js +164 -0
- package/dist/security/secrets.d.ts +1 -0
- package/dist/security/secrets.js +13 -0
- package/dist/sensors/actionability.js +1 -2
- package/dist/sensors/guard.js +4 -10
- package/dist/sensors/mutations.js +12 -0
- package/dist/sensors/operation_monitor.js +1 -0
- package/dist/server.d.ts +1 -1
- package/dist/server.js +600 -236
- package/dist/state/agent.d.ts +1 -0
- package/dist/state/agent.js +3 -0
- package/dist/state/delta.d.ts +12 -4
- package/dist/state/delta.js +67 -14
- package/dist/state/session.d.ts +8 -2
- package/dist/state/session.js +60 -13
- package/dist/tools/browser_detect.d.ts +16 -2
- package/dist/tools/browser_detect.js +54 -6
- package/dist/tools/burst.d.ts +17 -7
- package/dist/tools/burst.js +240 -83
- package/dist/tools/checkpoint.d.ts +38 -0
- package/dist/tools/checkpoint.js +271 -0
- package/dist/tools/click.d.ts +1 -0
- package/dist/tools/click.js +97 -98
- package/dist/tools/clipboard.js +31 -24
- package/dist/tools/design_extract.js +1 -1
- package/dist/tools/eval.js +1 -1
- package/dist/tools/fill.d.ts +20 -1
- package/dist/tools/fill.js +199 -40
- package/dist/tools/find.d.ts +16 -0
- package/dist/tools/find.js +67 -44
- package/dist/tools/github/index.d.ts +19 -4
- package/dist/tools/github/index.js +40 -4
- package/dist/tools/highlight.d.ts +6 -2
- package/dist/tools/highlight.js +158 -72
- package/dist/tools/inspect.js +1 -1
- package/dist/tools/key.js +65 -56
- package/dist/tools/navigate.d.ts +1 -1
- package/dist/tools/navigate.js +57 -44
- package/dist/tools/pdf.js +21 -13
- package/dist/tools/pointer.d.ts +18 -3
- package/dist/tools/pointer.js +89 -1
- package/dist/tools/read.d.ts +28 -8
- package/dist/tools/read.js +230 -56
- package/dist/tools/scrape.js +67 -13
- package/dist/tools/screenshot.js +21 -16
- package/dist/tools/scroll.js +6 -11
- package/dist/tools/select.js +8 -2
- package/dist/tools/snapshot.js +3 -34
- package/dist/tools/tabs.d.ts +10 -10
- package/dist/tools/tabs.js +55 -39
- package/dist/tools/thought.d.ts +2 -0
- package/dist/tools/thought.js +13 -14
- package/dist/tools/type.d.ts +2 -0
- package/dist/tools/type.js +109 -60
- package/dist/tools/type_secret.js +42 -76
- package/dist/tools/upload.js +1 -1
- package/dist/tools/video_compiler.d.ts +1 -1
- package/dist/tools/video_compiler.js +11 -12
- package/dist/version.d.ts +5 -0
- package/dist/version.js +5 -0
- package/docs/DOCUMENTATION.md +82 -36
- package/docs/PLUGINS.md +82 -499
- package/docs/TOOLS.md +51 -99
- package/docs/architecture/blind-injection.md +83 -0
- package/docs/architecture/dual-injection.md +32 -0
- package/docs/architecture/overview.md +49 -0
- package/docs/architecture/security.md +35 -0
- package/docs/architecture/transport.md +37 -0
- package/docs/captcha-stealth/captcha-solver.md +75 -0
- package/docs/captcha-stealth/stealth-physics.md +60 -0
- package/docs/cli/cli-commands.md +44 -0
- package/docs/cli/cli-flags.md +20 -0
- package/docs/cli/cli-introduction.md +20 -0
- package/docs/cli/cli-scripting.md +32 -0
- package/docs/cli/g-ide/ide-antigravity.md +24 -0
- package/docs/cli/g-ide/ide-claude-code.md +24 -0
- package/docs/cli/g-ide/ide-claude-desktop.md +32 -0
- package/docs/cli/g-ide/ide-cursor.md +31 -0
- package/docs/cli/g-ide/ide-generic.md +34 -0
- package/docs/cli/g-ide/ide-opencode.md +24 -0
- package/docs/cli/g-ide/ide-vscode-copilot.md +24 -0
- package/docs/cli/g-ide/ide-windsurf.md +24 -0
- package/docs/configuration/config-locations.md +24 -0
- package/docs/configuration/configuration.md +73 -0
- package/docs/configuration/env-variables.md +19 -0
- package/docs/configuration/folders.md +55 -0
- package/docs/desktop-automation/electron-apps.md +217 -0
- package/docs/get-started/installation.md +47 -0
- package/docs/get-started/introduction.md +60 -0
- package/docs/get-started/quickstart.md +54 -0
- package/docs/get-started/setup-wizard.md +26 -0
- package/docs/intelligence/site-profiles.md +151 -0
- package/docs/skills-ecosystem/skills-overview.md +35 -0
- package/docs/skills-ecosystem/skills-recipes.md +36 -0
- package/docs/tools/tools.md +631 -0
- package/docs/troubleshooting/trouble-browser.md +27 -0
- package/docs/troubleshooting/trouble-config.md +20 -0
- package/docs/troubleshooting/trouble-loop.md +31 -0
- package/ext-dist/background.js +10 -62
- package/ext-dist/content.js +333 -921
- package/ext-dist/hud/hud.js +9 -9
- package/ext-dist/manifest.json +2 -6
- package/ext-dist/styles.css +6 -1
- package/mcp.json +108 -72
- package/package.json +14 -9
- package/server.json +3 -3
- package/docs/DEV_MODE.md +0 -68
- package/docs/dev_plugin.md +0 -310
- package/ext-dist/sidepanel/sidepanel.js +0 -25
- package/ext-dist/sidepanel.html +0 -547
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weavetab — desktop/discovery.ts
|
|
3
|
+
* Auto-discovery scanner for running Electron desktop applications.
|
|
4
|
+
*
|
|
5
|
+
* Detects:
|
|
6
|
+
* 1. Active Electron instances with open CDP remote debugging ports
|
|
7
|
+
* (Slack, Discord, VS Code, Notion, Obsidian, Linear, Cursor, etc.).
|
|
8
|
+
* 2. Running Electron processes without remote debugging enabled,
|
|
9
|
+
* providing actionable launch flags.
|
|
10
|
+
*
|
|
11
|
+
* Weavetab by fy2ne
|
|
12
|
+
*/
|
|
13
|
+
import http from "node:http";
|
|
14
|
+
import fs from "node:fs";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
import { exec } from "node:child_process";
|
|
17
|
+
import { promisify } from "node:util";
|
|
18
|
+
import { logDebug } from "../audit/logger.js";
|
|
19
|
+
const execAsync = promisify(exec);
|
|
20
|
+
export const COMMON_DEBUG_PORTS = [9222, 9229, 9333, 8315, 9223, 9224];
|
|
21
|
+
const KNOWN_ELECTRON_PROCS = {
|
|
22
|
+
slack: "Slack",
|
|
23
|
+
discord: "Discord",
|
|
24
|
+
code: "VS Code",
|
|
25
|
+
"visual studio code": "VS Code",
|
|
26
|
+
notion: "Notion",
|
|
27
|
+
obsidian: "Obsidian",
|
|
28
|
+
linear: "Linear",
|
|
29
|
+
spotify: "Spotify",
|
|
30
|
+
postman: "Postman",
|
|
31
|
+
cursor: "Cursor",
|
|
32
|
+
figma: "Figma",
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Fast probe of a potential CDP debug port on 127.0.0.1.
|
|
36
|
+
* Resolves with version/target metadata if open, or null if unreachable.
|
|
37
|
+
*/
|
|
38
|
+
export async function probeDebugPort(port, timeoutMs = 300) {
|
|
39
|
+
return new Promise((resolve) => {
|
|
40
|
+
const req = http.get(`http://127.0.0.1:${port}/json/version`, { timeout: timeoutMs }, (res) => {
|
|
41
|
+
if (res.statusCode !== 200) {
|
|
42
|
+
res.resume();
|
|
43
|
+
return resolve(null);
|
|
44
|
+
}
|
|
45
|
+
let data = "";
|
|
46
|
+
res.on("data", (chunk) => (data += chunk));
|
|
47
|
+
res.on("end", async () => {
|
|
48
|
+
try {
|
|
49
|
+
const versionInfo = JSON.parse(data);
|
|
50
|
+
const targets = await fetchTargets(port, timeoutMs).catch(() => []);
|
|
51
|
+
resolve({ versionInfo, targets });
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
resolve(null);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
req.on("error", () => resolve(null));
|
|
59
|
+
req.on("timeout", () => {
|
|
60
|
+
req.destroy();
|
|
61
|
+
resolve(null);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
export async function fetchTargets(port, timeoutMs = 300) {
|
|
66
|
+
return new Promise((resolve) => {
|
|
67
|
+
const req = http.get(`http://127.0.0.1:${port}/json/list`, { timeout: timeoutMs }, (res) => {
|
|
68
|
+
if (res.statusCode !== 200) {
|
|
69
|
+
res.resume();
|
|
70
|
+
return resolve([]);
|
|
71
|
+
}
|
|
72
|
+
let data = "";
|
|
73
|
+
res.on("data", (chunk) => (data += chunk));
|
|
74
|
+
res.on("end", () => {
|
|
75
|
+
try {
|
|
76
|
+
resolve(JSON.parse(data));
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
resolve([]);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
req.on("error", () => resolve([]));
|
|
84
|
+
req.on("timeout", () => {
|
|
85
|
+
req.destroy();
|
|
86
|
+
resolve([]);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Infers an application name from CDP version and target metadata.
|
|
92
|
+
*/
|
|
93
|
+
export function identifyElectronAppName(versionInfo, targets) {
|
|
94
|
+
const allText = [
|
|
95
|
+
versionInfo?.Browser || "",
|
|
96
|
+
versionInfo?.["User-Agent"] || "",
|
|
97
|
+
...targets.map((t) => `${t.title || ""} ${t.url || ""}`),
|
|
98
|
+
].join(" ").toLowerCase();
|
|
99
|
+
for (const [key, displayName] of Object.entries(KNOWN_ELECTRON_PROCS)) {
|
|
100
|
+
if (allText.includes(key)) {
|
|
101
|
+
return displayName;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Fallback to first non-empty page title
|
|
105
|
+
const primaryTarget = targets.find((t) => t.type === "page" && t.title);
|
|
106
|
+
if (primaryTarget?.title) {
|
|
107
|
+
return primaryTarget.title.slice(0, 30);
|
|
108
|
+
}
|
|
109
|
+
return "Electron Application";
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Discovers running Electron apps on the host machine.
|
|
113
|
+
* 1. Probes common and provided debug ports for open CDP listeners.
|
|
114
|
+
* 2. Checks OS process list for running Electron apps lacking debug ports.
|
|
115
|
+
*/
|
|
116
|
+
export async function discoverElectronApps(customPorts = []) {
|
|
117
|
+
const discovered = [];
|
|
118
|
+
const portsToScan = Array.from(new Set([...COMMON_DEBUG_PORTS, ...customPorts]));
|
|
119
|
+
// 1. Probe all candidate debug ports in parallel
|
|
120
|
+
const probeResults = await Promise.all(portsToScan.map(async (port) => ({
|
|
121
|
+
port,
|
|
122
|
+
res: await probeDebugPort(port),
|
|
123
|
+
})));
|
|
124
|
+
const openAppNames = new Set();
|
|
125
|
+
for (const { port, res } of probeResults) {
|
|
126
|
+
if (res) {
|
|
127
|
+
const name = identifyElectronAppName(res.versionInfo, res.targets);
|
|
128
|
+
openAppNames.add(name.toLowerCase());
|
|
129
|
+
discovered.push({
|
|
130
|
+
name,
|
|
131
|
+
port,
|
|
132
|
+
status: "debug_port_open",
|
|
133
|
+
title: res.targets.find((t) => t.title)?.title || undefined,
|
|
134
|
+
webSocketDebuggerUrl: res.versionInfo.webSocketDebuggerUrl,
|
|
135
|
+
browserVersion: res.versionInfo.Browser,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// 2. Scan OS process list for known Electron apps without debug ports
|
|
140
|
+
try {
|
|
141
|
+
const runningProcs = await getRunningElectronProcesses();
|
|
142
|
+
for (const proc of runningProcs) {
|
|
143
|
+
if (!openAppNames.has(proc.name.toLowerCase())) {
|
|
144
|
+
discovered.push({
|
|
145
|
+
name: proc.name,
|
|
146
|
+
pid: proc.pid,
|
|
147
|
+
status: "process_running_no_debug_port",
|
|
148
|
+
recommendation: `Launch or restart ${proc.name} with: --remote-debugging-port=9222`,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
// Process list scan is best-effort across restricted OS environments
|
|
155
|
+
}
|
|
156
|
+
return discovered;
|
|
157
|
+
}
|
|
158
|
+
async function getRunningElectronProcesses() {
|
|
159
|
+
const platform = process.platform;
|
|
160
|
+
const running = [];
|
|
161
|
+
try {
|
|
162
|
+
if (platform === "win32") {
|
|
163
|
+
const { stdout } = await execAsync('tasklist /FO CSV /NH', { timeout: 1500 });
|
|
164
|
+
const lines = stdout.split("\r\n");
|
|
165
|
+
const seenNames = new Set();
|
|
166
|
+
for (const line of lines) {
|
|
167
|
+
if (!line.trim())
|
|
168
|
+
continue;
|
|
169
|
+
const parts = line.split('","').map((p) => p.replace(/"/g, ""));
|
|
170
|
+
const exeName = (parts[0] || "").toLowerCase();
|
|
171
|
+
const pid = parseInt(parts[1], 10);
|
|
172
|
+
for (const [key, displayName] of Object.entries(KNOWN_ELECTRON_PROCS)) {
|
|
173
|
+
if (exeName.includes(key) && !seenNames.has(displayName)) {
|
|
174
|
+
seenNames.add(displayName);
|
|
175
|
+
running.push({ name: displayName, pid });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
const { stdout } = await execAsync('ps -eo pid,comm', { timeout: 1500 });
|
|
182
|
+
const lines = stdout.split("\n");
|
|
183
|
+
const seenNames = new Set();
|
|
184
|
+
for (const line of lines) {
|
|
185
|
+
const trimmed = line.trim();
|
|
186
|
+
if (!trimmed)
|
|
187
|
+
continue;
|
|
188
|
+
const [pidStr, ...cmdParts] = trimmed.split(/\s+/);
|
|
189
|
+
const cmd = cmdParts.join(" ").toLowerCase();
|
|
190
|
+
const pid = parseInt(pidStr, 10);
|
|
191
|
+
for (const [key, displayName] of Object.entries(KNOWN_ELECTRON_PROCS)) {
|
|
192
|
+
if (cmd.includes(key) && !seenNames.has(displayName)) {
|
|
193
|
+
seenNames.add(displayName);
|
|
194
|
+
running.push({ name: displayName, pid });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
// ignore
|
|
202
|
+
}
|
|
203
|
+
return running;
|
|
204
|
+
}
|
|
205
|
+
export function isValidElectronExecutablePath(execPath) {
|
|
206
|
+
if (!execPath || typeof execPath !== "string")
|
|
207
|
+
return false;
|
|
208
|
+
const normalizedPath = execPath.toLowerCase().trim();
|
|
209
|
+
// Check for suspicious patterns that might indicate command injection or shell escapes
|
|
210
|
+
const suspiciousPatterns = [
|
|
211
|
+
'%', // URL encoding
|
|
212
|
+
'cmd.exe', 'powershell.exe', 'bash', 'sh', 'wscript.exe', 'cscript.exe'
|
|
213
|
+
];
|
|
214
|
+
if (suspiciousPatterns.some(pattern => normalizedPath.includes(pattern))) {
|
|
215
|
+
logDebug(`[SECURITY] Electron path validation failed: suspicious pattern in path: ${execPath}`);
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
try {
|
|
219
|
+
if (!fs.existsSync(execPath)) {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
const stat = fs.statSync(execPath);
|
|
223
|
+
if (!stat.isFile()) {
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
export function resolveElectronExecutablePath(appName, configuredPath) {
|
|
233
|
+
const normApp = appName.toLowerCase().trim();
|
|
234
|
+
// 1. If configuredPath is provided and valid, return it
|
|
235
|
+
if (configuredPath && isValidElectronExecutablePath(configuredPath)) {
|
|
236
|
+
return path.resolve(configuredPath);
|
|
237
|
+
}
|
|
238
|
+
// 2. If configuredPath was provided, but does not exist, check versioned parent folder (e.g. Squirrel app-*)
|
|
239
|
+
if (configuredPath) {
|
|
240
|
+
try {
|
|
241
|
+
const parsed = path.parse(path.resolve(configuredPath));
|
|
242
|
+
const parentDir = parsed.dir;
|
|
243
|
+
const baseExe = parsed.base || (process.platform === "win32" ? `${appName}.exe` : appName);
|
|
244
|
+
const parentBase = path.basename(parentDir);
|
|
245
|
+
const searchDirs = [];
|
|
246
|
+
if (parentBase.toLowerCase().startsWith("app-") || parentBase.toLowerCase().startsWith("app_")) {
|
|
247
|
+
searchDirs.push(path.dirname(parentDir));
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
searchDirs.push(parentDir);
|
|
251
|
+
}
|
|
252
|
+
for (const sDir of searchDirs) {
|
|
253
|
+
if (fs.existsSync(sDir)) {
|
|
254
|
+
const entries = fs.readdirSync(sDir, { withFileTypes: true });
|
|
255
|
+
const versionDirs = entries
|
|
256
|
+
.filter(e => e.isDirectory() && (e.name.startsWith("app-") || e.name.startsWith("app_")))
|
|
257
|
+
.map(e => e.name)
|
|
258
|
+
.sort((a, b) => b.localeCompare(a, undefined, { numeric: true, sensitivity: 'base' }));
|
|
259
|
+
for (const vDir of versionDirs) {
|
|
260
|
+
const candidate = path.join(sDir, vDir, baseExe);
|
|
261
|
+
if (isValidElectronExecutablePath(candidate)) {
|
|
262
|
+
return candidate;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const directCandidate = path.join(sDir, baseExe);
|
|
266
|
+
if (isValidElectronExecutablePath(directCandidate)) {
|
|
267
|
+
return directCandidate;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
catch { /* ignore fallback errors */ }
|
|
273
|
+
}
|
|
274
|
+
// 3. Platform-specific auto-discovery for known apps
|
|
275
|
+
const platform = process.platform;
|
|
276
|
+
const candidates = [];
|
|
277
|
+
if (platform === "win32") {
|
|
278
|
+
const localAppData = process.env.LOCALAPPDATA || "";
|
|
279
|
+
const progFiles = process.env.ProgramFiles || "C:\\Program Files";
|
|
280
|
+
const progFilesX86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
|
|
281
|
+
if (normApp === "discord") {
|
|
282
|
+
const discordDir = path.join(localAppData, "Discord");
|
|
283
|
+
if (fs.existsSync(discordDir)) {
|
|
284
|
+
try {
|
|
285
|
+
const entries = fs.readdirSync(discordDir, { withFileTypes: true });
|
|
286
|
+
const vDirs = entries
|
|
287
|
+
.filter(e => e.isDirectory() && e.name.startsWith("app-"))
|
|
288
|
+
.map(e => e.name)
|
|
289
|
+
.sort((a, b) => b.localeCompare(a, undefined, { numeric: true, sensitivity: 'base' }));
|
|
290
|
+
for (const v of vDirs) {
|
|
291
|
+
candidates.push(path.join(discordDir, v, "Discord.exe"));
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
catch { }
|
|
295
|
+
}
|
|
296
|
+
candidates.push(path.join(progFiles, "Discord", "Discord.exe"));
|
|
297
|
+
candidates.push(path.join(progFilesX86, "Discord", "Discord.exe"));
|
|
298
|
+
}
|
|
299
|
+
else if (normApp === "slack") {
|
|
300
|
+
const slackDir = path.join(localAppData, "slack");
|
|
301
|
+
if (fs.existsSync(slackDir)) {
|
|
302
|
+
try {
|
|
303
|
+
const entries = fs.readdirSync(slackDir, { withFileTypes: true });
|
|
304
|
+
const vDirs = entries
|
|
305
|
+
.filter(e => e.isDirectory() && e.name.startsWith("app-"))
|
|
306
|
+
.map(e => e.name)
|
|
307
|
+
.sort((a, b) => b.localeCompare(a, undefined, { numeric: true, sensitivity: 'base' }));
|
|
308
|
+
for (const v of vDirs) {
|
|
309
|
+
candidates.push(path.join(slackDir, v, "slack.exe"));
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
catch { }
|
|
313
|
+
}
|
|
314
|
+
candidates.push(path.join(progFiles, "Slack", "slack.exe"));
|
|
315
|
+
candidates.push(path.join(progFilesX86, "Slack", "slack.exe"));
|
|
316
|
+
}
|
|
317
|
+
else if (normApp === "antigravity") {
|
|
318
|
+
candidates.push(path.join(localAppData, "Programs", "antigravity", "Antigravity.exe"));
|
|
319
|
+
}
|
|
320
|
+
else if (normApp === "antigravity-ide") {
|
|
321
|
+
candidates.push(path.join(localAppData, "Programs", "Antigravity IDE", "Antigravity IDE.exe"));
|
|
322
|
+
}
|
|
323
|
+
else if (normApp === "code" || normApp === "vscode") {
|
|
324
|
+
candidates.push(path.join(localAppData, "Programs", "Microsoft VS Code", "Code.exe"));
|
|
325
|
+
candidates.push(path.join(progFiles, "Microsoft VS Code", "Code.exe"));
|
|
326
|
+
candidates.push(path.join(progFilesX86, "Microsoft VS Code", "Code.exe"));
|
|
327
|
+
}
|
|
328
|
+
else if (normApp === "linear") {
|
|
329
|
+
candidates.push(path.join(localAppData, "Programs", "Linear", "Linear.exe"));
|
|
330
|
+
}
|
|
331
|
+
else if (normApp === "notion") {
|
|
332
|
+
candidates.push(path.join(localAppData, "Programs", "Notion", "Notion.exe"));
|
|
333
|
+
}
|
|
334
|
+
else if (normApp === "obsidian") {
|
|
335
|
+
candidates.push(path.join(localAppData, "Programs", "obsidian", "Obsidian.exe"));
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
else if (platform === "darwin") {
|
|
339
|
+
if (normApp === "discord")
|
|
340
|
+
candidates.push("/Applications/Discord.app/Contents/MacOS/Discord");
|
|
341
|
+
else if (normApp === "slack")
|
|
342
|
+
candidates.push("/Applications/Slack.app/Contents/MacOS/Slack");
|
|
343
|
+
else if (normApp === "code" || normApp === "vscode")
|
|
344
|
+
candidates.push("/Applications/Visual Studio Code.app/Contents/MacOS/Electron");
|
|
345
|
+
else if (normApp === "linear")
|
|
346
|
+
candidates.push("/Applications/Linear.app/Contents/MacOS/Linear");
|
|
347
|
+
else if (normApp === "notion")
|
|
348
|
+
candidates.push("/Applications/Notion.app/Contents/MacOS/Notion");
|
|
349
|
+
else if (normApp === "obsidian")
|
|
350
|
+
candidates.push("/Applications/Obsidian.app/Contents/MacOS/Obsidian");
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
// Linux
|
|
354
|
+
candidates.push(`/usr/bin/${normApp}`);
|
|
355
|
+
candidates.push(`/usr/local/bin/${normApp}`);
|
|
356
|
+
candidates.push(`/usr/share/${normApp}/${normApp}`);
|
|
357
|
+
}
|
|
358
|
+
for (const c of candidates) {
|
|
359
|
+
if (isValidElectronExecutablePath(c)) {
|
|
360
|
+
return c;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return null;
|
|
364
|
+
}
|