@leadbay/mcp 0.23.6 → 0.23.8
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/CHANGELOG.md +14 -0
- package/README.md +34 -12
- package/dist/bin.js +173 -47
- package/dist/http-server.js +173 -47
- package/dist/installer-electron.js +259 -208
- package/dist/installer-gui.js +30 -34
- package/package.json +1 -1
|
@@ -9,8 +9,188 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
// installer/install-
|
|
12
|
+
// installer/install-shared.ts
|
|
13
13
|
import { spawn } from "child_process";
|
|
14
|
+
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
15
|
+
import { join } from "path";
|
|
16
|
+
import { homedir } from "os";
|
|
17
|
+
function printHostedMcpHelp(write = (s) => process.stderr.write(s)) {
|
|
18
|
+
write(
|
|
19
|
+
`
|
|
20
|
+
The guided installer couldn't complete (no browser opened in time).
|
|
21
|
+
You can add Leadbay's hosted MCP directly \u2014 Claude signs in in-app,
|
|
22
|
+
no localhost callback needed:
|
|
23
|
+
|
|
24
|
+
\u2022 Claude web / Cowork web:
|
|
25
|
+
Customize \u2192 Connectors \u2192 "+" \u2192 Add custom connector
|
|
26
|
+
Name: Leadbay URL: ${HOSTED_MCP_URL}
|
|
27
|
+
|
|
28
|
+
\u2022 A terminal with the Claude Code CLI:
|
|
29
|
+
claude mcp add --transport http leadbay ${HOSTED_MCP_URL}
|
|
30
|
+
|
|
31
|
+
\u2022 Or re-run the terminal install flow:
|
|
32
|
+
npx -y @leadbay/mcp@latest install --oauth
|
|
33
|
+
|
|
34
|
+
`
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
function formatInstallOsLabel(platform = process.platform, arch = process.arch) {
|
|
38
|
+
const name = platform === "darwin" ? "macOS" : platform === "win32" ? "Windows" : platform === "linux" ? "Linux" : platform;
|
|
39
|
+
return `${name} (${arch})`;
|
|
40
|
+
}
|
|
41
|
+
function detectClaudeDesktopMode(claudeSupportDir) {
|
|
42
|
+
const markers = [];
|
|
43
|
+
const legacy = existsSync(join(claudeSupportDir, "claude_desktop_config.json"));
|
|
44
|
+
if (existsSync(join(claudeSupportDir, "Claude Extensions"))) {
|
|
45
|
+
markers.push("Claude Extensions/");
|
|
46
|
+
}
|
|
47
|
+
if (existsSync(join(claudeSupportDir, "extensions-installations.json"))) {
|
|
48
|
+
markers.push("extensions-installations.json");
|
|
49
|
+
}
|
|
50
|
+
const cfgPath = join(claudeSupportDir, "config.json");
|
|
51
|
+
if (existsSync(cfgPath)) {
|
|
52
|
+
try {
|
|
53
|
+
const raw = readFileSync(cfgPath, "utf8");
|
|
54
|
+
const parsed = JSON.parse(raw);
|
|
55
|
+
if (parsed && typeof parsed === "object") {
|
|
56
|
+
const hasDxtKey = Object.keys(parsed).some((key) => key.startsWith("dxt:"));
|
|
57
|
+
if (hasDxtKey) markers.push("config.json (dxt:* keys)");
|
|
58
|
+
}
|
|
59
|
+
} catch {
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return { legacy, dxt: markers.length > 0, markers };
|
|
63
|
+
}
|
|
64
|
+
async function findOnPath(bin) {
|
|
65
|
+
return await new Promise((resolve) => {
|
|
66
|
+
const cmd = process.platform === "win32" ? "where" : "which";
|
|
67
|
+
const child = spawn(cmd, [bin], { stdio: ["ignore", "pipe", "ignore"] });
|
|
68
|
+
let buf = "";
|
|
69
|
+
child.stdout.on("data", (chunk) => buf += chunk.toString());
|
|
70
|
+
child.on("close", (code) => resolve(code === 0 ? buf.split(/\r?\n/)[0] : null));
|
|
71
|
+
child.on("error", () => resolve(null));
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
async function windowsStoreAppInstalled(packageName, appName) {
|
|
75
|
+
if (process.platform !== "win32") return false;
|
|
76
|
+
return await new Promise((resolve) => {
|
|
77
|
+
const script = [
|
|
78
|
+
`$pkg = Get-AppxPackage -Name '${packageName}' -ErrorAction SilentlyContinue`,
|
|
79
|
+
`$app = Get-StartApps | Where-Object { $_.AppID -like '${packageName}_*!${appName}' } | Select-Object -First 1`,
|
|
80
|
+
"if ($pkg -or $app) { exit 0 } else { exit 1 }"
|
|
81
|
+
].join("; ");
|
|
82
|
+
const child = spawn("powershell.exe", ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", script], {
|
|
83
|
+
stdio: "ignore",
|
|
84
|
+
windowsHide: true
|
|
85
|
+
});
|
|
86
|
+
child.on("close", (code) => resolve(code === 0));
|
|
87
|
+
child.on("error", () => resolve(false));
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
function isClaudeStorePackagePresent(localAppData) {
|
|
91
|
+
const packagesDir = join(localAppData, "Packages");
|
|
92
|
+
if (!existsSync(packagesDir)) return false;
|
|
93
|
+
try {
|
|
94
|
+
return readdirSync(packagesDir).some((name) => /^Claude_/i.test(name));
|
|
95
|
+
} catch {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function isClaudeDesktopInstalled(home) {
|
|
100
|
+
if (process.platform === "darwin") {
|
|
101
|
+
return existsSync("/Applications/Claude.app") || existsSync(home + "/Applications/Claude.app");
|
|
102
|
+
}
|
|
103
|
+
if (process.platform === "win32") {
|
|
104
|
+
const local = process.env.LOCALAPPDATA ?? home + "/AppData/Local";
|
|
105
|
+
const programFiles = process.env.ProgramFiles;
|
|
106
|
+
const programFilesX86 = process.env["ProgramFiles(x86)"];
|
|
107
|
+
const exeInstalled = [
|
|
108
|
+
local + "/Programs/Claude/Claude.exe",
|
|
109
|
+
local + "/Claude/Claude.exe",
|
|
110
|
+
programFiles ? programFiles + "/Claude/Claude.exe" : null,
|
|
111
|
+
programFilesX86 ? programFilesX86 + "/Claude/Claude.exe" : null
|
|
112
|
+
].some((candidate) => candidate !== null && existsSync(candidate));
|
|
113
|
+
if (exeInstalled) return true;
|
|
114
|
+
if (isClaudeStorePackagePresent(local)) return true;
|
|
115
|
+
return await windowsStoreAppInstalled("AnthropicPBC.Claude", "Claude");
|
|
116
|
+
}
|
|
117
|
+
const desktopBin = await findOnPath("claude-desktop");
|
|
118
|
+
if (desktopBin) return true;
|
|
119
|
+
return existsSync(home + "/.local/share/applications/claude-desktop.desktop") || existsSync("/usr/share/applications/claude-desktop.desktop") || existsSync("/opt/Claude/Claude") || existsSync("/opt/Claude/claude") || existsSync("/opt/claude/claude");
|
|
120
|
+
}
|
|
121
|
+
async function isChatGptDesktopInstalled(home) {
|
|
122
|
+
if (process.platform === "darwin") {
|
|
123
|
+
return existsSync("/Applications/ChatGPT.app") || existsSync(home + "/Applications/ChatGPT.app");
|
|
124
|
+
}
|
|
125
|
+
if (process.platform === "win32") {
|
|
126
|
+
const local = process.env.LOCALAPPDATA ?? home + "/AppData/Local";
|
|
127
|
+
const programFiles = process.env.ProgramFiles;
|
|
128
|
+
const programFilesX86 = process.env["ProgramFiles(x86)"];
|
|
129
|
+
const exeInstalled = [
|
|
130
|
+
local + "/Programs/ChatGPT/ChatGPT.exe",
|
|
131
|
+
local + "/ChatGPT/ChatGPT.exe",
|
|
132
|
+
programFiles ? programFiles + "/OpenAI/ChatGPT/ChatGPT.exe" : null,
|
|
133
|
+
programFiles ? programFiles + "/ChatGPT/ChatGPT.exe" : null,
|
|
134
|
+
programFilesX86 ? programFilesX86 + "/OpenAI/ChatGPT/ChatGPT.exe" : null,
|
|
135
|
+
programFilesX86 ? programFilesX86 + "/ChatGPT/ChatGPT.exe" : null
|
|
136
|
+
].some((candidate) => candidate !== null && existsSync(candidate));
|
|
137
|
+
return exeInstalled || await windowsStoreAppInstalled("OpenAI.ChatGPT-Desktop", "ChatGPT");
|
|
138
|
+
}
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
async function isCursorInstalled(home) {
|
|
142
|
+
const cursorBin = await findOnPath("cursor");
|
|
143
|
+
if (cursorBin) return true;
|
|
144
|
+
if (process.platform === "darwin") return existsSync("/Applications/Cursor.app");
|
|
145
|
+
if (process.platform === "win32") {
|
|
146
|
+
const local = process.env.LOCALAPPDATA ?? `${home}\\AppData\\Local`;
|
|
147
|
+
return existsSync(`${local}\\Programs\\Cursor\\Cursor.exe`);
|
|
148
|
+
}
|
|
149
|
+
return existsSync("/usr/share/applications/cursor.desktop") || existsSync("/opt/Cursor/cursor");
|
|
150
|
+
}
|
|
151
|
+
async function detectClients() {
|
|
152
|
+
const out = [];
|
|
153
|
+
const home = homedir();
|
|
154
|
+
const claudeBin = await findOnPath("claude");
|
|
155
|
+
if (claudeBin) {
|
|
156
|
+
out.push({ id: "claude-code", label: "Claude Code", detail: `${claudeBin} mcp add ...` });
|
|
157
|
+
}
|
|
158
|
+
const claudeSupportDir = process.platform === "win32" ? `${process.env.APPDATA ?? `${home}\\AppData\\Roaming`}\\Claude` : process.platform === "darwin" ? `${home}/Library/Application Support/Claude` : `${home}/.config/Claude`;
|
|
159
|
+
const claudeDesktopPath = process.platform === "win32" ? `${claudeSupportDir}\\claude_desktop_config.json` : `${claudeSupportDir}/claude_desktop_config.json`;
|
|
160
|
+
const mode = detectClaudeDesktopMode(claudeSupportDir);
|
|
161
|
+
if (await isClaudeDesktopInstalled(home)) {
|
|
162
|
+
out.push({ id: "claude-desktop", label: "Claude Desktop", detail: claudeDesktopPath, configPath: claudeDesktopPath, mode, supportDir: claudeSupportDir });
|
|
163
|
+
}
|
|
164
|
+
if (await isChatGptDesktopInstalled(home)) {
|
|
165
|
+
out.push({ id: "chatgpt-desktop", label: "ChatGPT Desktop", detail: HOSTED_MCP_URL });
|
|
166
|
+
}
|
|
167
|
+
const cursorPath = process.platform === "win32" ? `${home}\\.cursor\\mcp.json` : `${home}/.cursor/mcp.json`;
|
|
168
|
+
if (await isCursorInstalled(home)) {
|
|
169
|
+
out.push({
|
|
170
|
+
id: "cursor",
|
|
171
|
+
label: "Cursor",
|
|
172
|
+
detail: existsSync(cursorPath) ? cursorPath : `${cursorPath} (will be created)`,
|
|
173
|
+
configPath: cursorPath
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
const codexBin = await findOnPath("codex");
|
|
177
|
+
const codexDir = process.platform === "win32" ? `${process.env.USERPROFILE ?? home}\\.codex` : `${home}/.codex`;
|
|
178
|
+
if (codexBin) {
|
|
179
|
+
const codexConfigPath = process.platform === "win32" ? `${codexDir}\\config.toml` : `${codexDir}/config.toml`;
|
|
180
|
+
out.push({ id: "codex", label: "Codex", detail: codexConfigPath, configPath: codexConfigPath });
|
|
181
|
+
}
|
|
182
|
+
return out;
|
|
183
|
+
}
|
|
184
|
+
var HOSTED_MCP_URL;
|
|
185
|
+
var init_install_shared = __esm({
|
|
186
|
+
"installer/install-shared.ts"() {
|
|
187
|
+
"use strict";
|
|
188
|
+
HOSTED_MCP_URL = "https://leadbay-mcp-prod.fly.dev/mcp";
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// installer/install-claude-code.ts
|
|
193
|
+
import { spawn as spawn2 } from "child_process";
|
|
14
194
|
function buildClaudeCodeAddArgs(token, region, includeWrite, telemetryEnabled, localBinPath) {
|
|
15
195
|
const args = [
|
|
16
196
|
"mcp",
|
|
@@ -38,7 +218,7 @@ function buildClaudeCodeRemoveArgs() {
|
|
|
38
218
|
}
|
|
39
219
|
async function runClaudeMcp(args) {
|
|
40
220
|
return await new Promise((resolve) => {
|
|
41
|
-
const child =
|
|
221
|
+
const child = spawn2("claude", args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
42
222
|
let stdout = "";
|
|
43
223
|
let stderr = "";
|
|
44
224
|
child.stdout.on("data", (chunk) => stdout += chunk.toString());
|
|
@@ -397,166 +577,6 @@ var init_install_dxt = __esm({
|
|
|
397
577
|
}
|
|
398
578
|
});
|
|
399
579
|
|
|
400
|
-
// installer/install-shared.ts
|
|
401
|
-
import { spawn as spawn2 } from "child_process";
|
|
402
|
-
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
403
|
-
import { join } from "path";
|
|
404
|
-
import { homedir } from "os";
|
|
405
|
-
function formatInstallOsLabel(platform = process.platform, arch = process.arch) {
|
|
406
|
-
const name = platform === "darwin" ? "macOS" : platform === "win32" ? "Windows" : platform === "linux" ? "Linux" : platform;
|
|
407
|
-
return `${name} (${arch})`;
|
|
408
|
-
}
|
|
409
|
-
function detectClaudeDesktopMode(claudeSupportDir) {
|
|
410
|
-
const markers = [];
|
|
411
|
-
const legacy = existsSync(join(claudeSupportDir, "claude_desktop_config.json"));
|
|
412
|
-
if (existsSync(join(claudeSupportDir, "Claude Extensions"))) {
|
|
413
|
-
markers.push("Claude Extensions/");
|
|
414
|
-
}
|
|
415
|
-
if (existsSync(join(claudeSupportDir, "extensions-installations.json"))) {
|
|
416
|
-
markers.push("extensions-installations.json");
|
|
417
|
-
}
|
|
418
|
-
const cfgPath = join(claudeSupportDir, "config.json");
|
|
419
|
-
if (existsSync(cfgPath)) {
|
|
420
|
-
try {
|
|
421
|
-
const raw = readFileSync(cfgPath, "utf8");
|
|
422
|
-
const parsed = JSON.parse(raw);
|
|
423
|
-
if (parsed && typeof parsed === "object") {
|
|
424
|
-
const hasDxtKey = Object.keys(parsed).some((key) => key.startsWith("dxt:"));
|
|
425
|
-
if (hasDxtKey) markers.push("config.json (dxt:* keys)");
|
|
426
|
-
}
|
|
427
|
-
} catch {
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
return { legacy, dxt: markers.length > 0, markers };
|
|
431
|
-
}
|
|
432
|
-
async function findOnPath(bin) {
|
|
433
|
-
return await new Promise((resolve) => {
|
|
434
|
-
const cmd = process.platform === "win32" ? "where" : "which";
|
|
435
|
-
const child = spawn2(cmd, [bin], { stdio: ["ignore", "pipe", "ignore"] });
|
|
436
|
-
let buf = "";
|
|
437
|
-
child.stdout.on("data", (chunk) => buf += chunk.toString());
|
|
438
|
-
child.on("close", (code) => resolve(code === 0 ? buf.split(/\r?\n/)[0] : null));
|
|
439
|
-
child.on("error", () => resolve(null));
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
async function windowsStoreAppInstalled(packageName, appName) {
|
|
443
|
-
if (process.platform !== "win32") return false;
|
|
444
|
-
return await new Promise((resolve) => {
|
|
445
|
-
const script = [
|
|
446
|
-
`$pkg = Get-AppxPackage -Name '${packageName}' -ErrorAction SilentlyContinue`,
|
|
447
|
-
`$app = Get-StartApps | Where-Object { $_.AppID -like '${packageName}_*!${appName}' } | Select-Object -First 1`,
|
|
448
|
-
"if ($pkg -or $app) { exit 0 } else { exit 1 }"
|
|
449
|
-
].join("; ");
|
|
450
|
-
const child = spawn2("powershell.exe", ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", script], {
|
|
451
|
-
stdio: "ignore",
|
|
452
|
-
windowsHide: true
|
|
453
|
-
});
|
|
454
|
-
child.on("close", (code) => resolve(code === 0));
|
|
455
|
-
child.on("error", () => resolve(false));
|
|
456
|
-
});
|
|
457
|
-
}
|
|
458
|
-
function isClaudeStorePackagePresent(localAppData) {
|
|
459
|
-
const packagesDir = join(localAppData, "Packages");
|
|
460
|
-
if (!existsSync(packagesDir)) return false;
|
|
461
|
-
try {
|
|
462
|
-
return readdirSync(packagesDir).some((name) => /^Claude_/i.test(name));
|
|
463
|
-
} catch {
|
|
464
|
-
return false;
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
async function isClaudeDesktopInstalled(home) {
|
|
468
|
-
if (process.platform === "darwin") {
|
|
469
|
-
return existsSync("/Applications/Claude.app") || existsSync(home + "/Applications/Claude.app");
|
|
470
|
-
}
|
|
471
|
-
if (process.platform === "win32") {
|
|
472
|
-
const local = process.env.LOCALAPPDATA ?? home + "/AppData/Local";
|
|
473
|
-
const programFiles = process.env.ProgramFiles;
|
|
474
|
-
const programFilesX86 = process.env["ProgramFiles(x86)"];
|
|
475
|
-
const exeInstalled = [
|
|
476
|
-
local + "/Programs/Claude/Claude.exe",
|
|
477
|
-
local + "/Claude/Claude.exe",
|
|
478
|
-
programFiles ? programFiles + "/Claude/Claude.exe" : null,
|
|
479
|
-
programFilesX86 ? programFilesX86 + "/Claude/Claude.exe" : null
|
|
480
|
-
].some((candidate) => candidate !== null && existsSync(candidate));
|
|
481
|
-
if (exeInstalled) return true;
|
|
482
|
-
if (isClaudeStorePackagePresent(local)) return true;
|
|
483
|
-
return await windowsStoreAppInstalled("AnthropicPBC.Claude", "Claude");
|
|
484
|
-
}
|
|
485
|
-
const desktopBin = await findOnPath("claude-desktop");
|
|
486
|
-
if (desktopBin) return true;
|
|
487
|
-
return existsSync(home + "/.local/share/applications/claude-desktop.desktop") || existsSync("/usr/share/applications/claude-desktop.desktop") || existsSync("/opt/Claude/Claude") || existsSync("/opt/Claude/claude") || existsSync("/opt/claude/claude");
|
|
488
|
-
}
|
|
489
|
-
async function isChatGptDesktopInstalled(home) {
|
|
490
|
-
if (process.platform === "darwin") {
|
|
491
|
-
return existsSync("/Applications/ChatGPT.app") || existsSync(home + "/Applications/ChatGPT.app");
|
|
492
|
-
}
|
|
493
|
-
if (process.platform === "win32") {
|
|
494
|
-
const local = process.env.LOCALAPPDATA ?? home + "/AppData/Local";
|
|
495
|
-
const programFiles = process.env.ProgramFiles;
|
|
496
|
-
const programFilesX86 = process.env["ProgramFiles(x86)"];
|
|
497
|
-
const exeInstalled = [
|
|
498
|
-
local + "/Programs/ChatGPT/ChatGPT.exe",
|
|
499
|
-
local + "/ChatGPT/ChatGPT.exe",
|
|
500
|
-
programFiles ? programFiles + "/OpenAI/ChatGPT/ChatGPT.exe" : null,
|
|
501
|
-
programFiles ? programFiles + "/ChatGPT/ChatGPT.exe" : null,
|
|
502
|
-
programFilesX86 ? programFilesX86 + "/OpenAI/ChatGPT/ChatGPT.exe" : null,
|
|
503
|
-
programFilesX86 ? programFilesX86 + "/ChatGPT/ChatGPT.exe" : null
|
|
504
|
-
].some((candidate) => candidate !== null && existsSync(candidate));
|
|
505
|
-
return exeInstalled || await windowsStoreAppInstalled("OpenAI.ChatGPT-Desktop", "ChatGPT");
|
|
506
|
-
}
|
|
507
|
-
return false;
|
|
508
|
-
}
|
|
509
|
-
async function isCursorInstalled(home) {
|
|
510
|
-
const cursorBin = await findOnPath("cursor");
|
|
511
|
-
if (cursorBin) return true;
|
|
512
|
-
if (process.platform === "darwin") return existsSync("/Applications/Cursor.app");
|
|
513
|
-
if (process.platform === "win32") {
|
|
514
|
-
const local = process.env.LOCALAPPDATA ?? `${home}\\AppData\\Local`;
|
|
515
|
-
return existsSync(`${local}\\Programs\\Cursor\\Cursor.exe`);
|
|
516
|
-
}
|
|
517
|
-
return existsSync("/usr/share/applications/cursor.desktop") || existsSync("/opt/Cursor/cursor");
|
|
518
|
-
}
|
|
519
|
-
async function detectClients() {
|
|
520
|
-
const out = [];
|
|
521
|
-
const home = homedir();
|
|
522
|
-
const claudeBin = await findOnPath("claude");
|
|
523
|
-
if (claudeBin) {
|
|
524
|
-
out.push({ id: "claude-code", label: "Claude Code", detail: `${claudeBin} mcp add ...` });
|
|
525
|
-
}
|
|
526
|
-
const claudeSupportDir = process.platform === "win32" ? `${process.env.APPDATA ?? `${home}\\AppData\\Roaming`}\\Claude` : process.platform === "darwin" ? `${home}/Library/Application Support/Claude` : `${home}/.config/Claude`;
|
|
527
|
-
const claudeDesktopPath = process.platform === "win32" ? `${claudeSupportDir}\\claude_desktop_config.json` : `${claudeSupportDir}/claude_desktop_config.json`;
|
|
528
|
-
const mode = detectClaudeDesktopMode(claudeSupportDir);
|
|
529
|
-
if (await isClaudeDesktopInstalled(home)) {
|
|
530
|
-
out.push({ id: "claude-desktop", label: "Claude Desktop", detail: claudeDesktopPath, configPath: claudeDesktopPath, mode, supportDir: claudeSupportDir });
|
|
531
|
-
}
|
|
532
|
-
if (await isChatGptDesktopInstalled(home)) {
|
|
533
|
-
out.push({ id: "chatgpt-desktop", label: "ChatGPT Desktop", detail: HOSTED_MCP_URL });
|
|
534
|
-
}
|
|
535
|
-
const cursorPath = process.platform === "win32" ? `${home}\\.cursor\\mcp.json` : `${home}/.cursor/mcp.json`;
|
|
536
|
-
if (await isCursorInstalled(home)) {
|
|
537
|
-
out.push({
|
|
538
|
-
id: "cursor",
|
|
539
|
-
label: "Cursor",
|
|
540
|
-
detail: existsSync(cursorPath) ? cursorPath : `${cursorPath} (will be created)`,
|
|
541
|
-
configPath: cursorPath
|
|
542
|
-
});
|
|
543
|
-
}
|
|
544
|
-
const codexBin = await findOnPath("codex");
|
|
545
|
-
const codexDir = process.platform === "win32" ? `${process.env.USERPROFILE ?? home}\\.codex` : `${home}/.codex`;
|
|
546
|
-
if (codexBin) {
|
|
547
|
-
const codexConfigPath = process.platform === "win32" ? `${codexDir}\\config.toml` : `${codexDir}/config.toml`;
|
|
548
|
-
out.push({ id: "codex", label: "Codex", detail: codexConfigPath, configPath: codexConfigPath });
|
|
549
|
-
}
|
|
550
|
-
return out;
|
|
551
|
-
}
|
|
552
|
-
var HOSTED_MCP_URL;
|
|
553
|
-
var init_install_shared = __esm({
|
|
554
|
-
"installer/install-shared.ts"() {
|
|
555
|
-
"use strict";
|
|
556
|
-
HOSTED_MCP_URL = "https://leadbay-mcp-prod.fly.dev/mcp";
|
|
557
|
-
}
|
|
558
|
-
});
|
|
559
|
-
|
|
560
580
|
// src/oauth.ts
|
|
561
581
|
import { createHash, randomBytes } from "crypto";
|
|
562
582
|
import { createServer } from "http";
|
|
@@ -1157,7 +1177,20 @@ async function loginWithOAuth() {
|
|
|
1157
1177
|
const { accessToken } = await oauthLogin({
|
|
1158
1178
|
authServerBaseUrl: OAUTH_BASE_URLS.prod[region],
|
|
1159
1179
|
clientName: `Leadbay MCP installer @ ${hostname()}`,
|
|
1160
|
-
log: () => void 0
|
|
1180
|
+
log: () => void 0,
|
|
1181
|
+
// Wrap the opener so a launch failure surfaces the (still-reachable) URL
|
|
1182
|
+
// on stderr instead of silently dropping it, while the listener stays up.
|
|
1183
|
+
openBrowser: async (u) => {
|
|
1184
|
+
try {
|
|
1185
|
+
await openInBrowser(u);
|
|
1186
|
+
} catch {
|
|
1187
|
+
process.stderr.write(`
|
|
1188
|
+
Open this URL in your browser to sign in:
|
|
1189
|
+
${u}
|
|
1190
|
+
|
|
1191
|
+
`);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1161
1194
|
});
|
|
1162
1195
|
const sessionId = randomUUID();
|
|
1163
1196
|
const accountLabel = `Leadbay OAuth (${region.toUpperCase()})`;
|
|
@@ -1632,36 +1665,6 @@ function pageHtml(locale = "en") {
|
|
|
1632
1665
|
</body>
|
|
1633
1666
|
</html>`;
|
|
1634
1667
|
}
|
|
1635
|
-
async function openBrowser(url) {
|
|
1636
|
-
const { spawn: spawn4 } = await import("child_process");
|
|
1637
|
-
const trySpawn = (command, args) => new Promise((resolve) => {
|
|
1638
|
-
try {
|
|
1639
|
-
const child = spawn4(command, args, { stdio: "ignore", detached: true });
|
|
1640
|
-
child.unref();
|
|
1641
|
-
child.on("error", () => resolve(false));
|
|
1642
|
-
child.on("close", (code) => resolve(code === 0));
|
|
1643
|
-
} catch {
|
|
1644
|
-
resolve(false);
|
|
1645
|
-
}
|
|
1646
|
-
});
|
|
1647
|
-
if (process.platform === "darwin") {
|
|
1648
|
-
await trySpawn("open", [url]);
|
|
1649
|
-
return;
|
|
1650
|
-
}
|
|
1651
|
-
if (process.platform === "win32") {
|
|
1652
|
-
await trySpawn("cmd", ["/c", "start", "", url]);
|
|
1653
|
-
return;
|
|
1654
|
-
}
|
|
1655
|
-
const candidates = ["xdg-open", "sensible-browser", "google-chrome", "chromium-browser", "firefox"];
|
|
1656
|
-
for (const cmd of candidates) {
|
|
1657
|
-
if (await trySpawn(cmd, [url])) return;
|
|
1658
|
-
}
|
|
1659
|
-
process.stderr.write(`
|
|
1660
|
-
Open this URL in your browser to continue:
|
|
1661
|
-
${url}
|
|
1662
|
-
|
|
1663
|
-
`);
|
|
1664
|
-
}
|
|
1665
1668
|
function makeGuiServer(options, pageContent, extraRoutes, logLabel) {
|
|
1666
1669
|
let expectedHost = `127.0.0.1:${(options.port ?? PORT) || 0}`;
|
|
1667
1670
|
let resolveDone;
|
|
@@ -1671,11 +1674,16 @@ function makeGuiServer(options, pageContent, extraRoutes, logLabel) {
|
|
|
1671
1674
|
const onDone = () => setTimeout(() => {
|
|
1672
1675
|
resolveDone();
|
|
1673
1676
|
}, 1500);
|
|
1677
|
+
let resolveActivity;
|
|
1678
|
+
const activity = new Promise((r) => {
|
|
1679
|
+
resolveActivity = r;
|
|
1680
|
+
});
|
|
1674
1681
|
const server = createServer2(async (req, res) => {
|
|
1675
1682
|
if (!isAllowedOrigin(req, expectedHost)) {
|
|
1676
1683
|
sendJson(res, 403, { ok: false, error: "forbidden" });
|
|
1677
1684
|
return;
|
|
1678
1685
|
}
|
|
1686
|
+
resolveActivity();
|
|
1679
1687
|
try {
|
|
1680
1688
|
if (req.method === "GET" && req.url === "/") {
|
|
1681
1689
|
const raw = await pageContent();
|
|
@@ -1699,8 +1707,16 @@ function makeGuiServer(options, pageContent, extraRoutes, logLabel) {
|
|
|
1699
1707
|
const url = `http://127.0.0.1:${port}/`;
|
|
1700
1708
|
process.stderr.write(`Leadbay MCP ${logLabel} GUI: ${url}
|
|
1701
1709
|
`);
|
|
1702
|
-
if (options.openBrowser !== false)
|
|
1703
|
-
|
|
1710
|
+
if (options.openBrowser !== false) {
|
|
1711
|
+
openInBrowser(url).catch(() => {
|
|
1712
|
+
process.stderr.write(`
|
|
1713
|
+
Open this URL in your browser to continue:
|
|
1714
|
+
${url}
|
|
1715
|
+
|
|
1716
|
+
`);
|
|
1717
|
+
});
|
|
1718
|
+
}
|
|
1719
|
+
resolve({ url, done, activity, close: () => new Promise((res, rej) => server.close((e) => e ? rej(e) : res())) });
|
|
1704
1720
|
});
|
|
1705
1721
|
});
|
|
1706
1722
|
}
|
|
@@ -1748,7 +1764,7 @@ var init_installer_gui = __esm({
|
|
|
1748
1764
|
init_install_dxt();
|
|
1749
1765
|
init_install_shared();
|
|
1750
1766
|
init_oauth();
|
|
1751
|
-
VERSION = true ? "0.23.
|
|
1767
|
+
VERSION = true ? "0.23.8" : "0.0.0-dev";
|
|
1752
1768
|
MESSAGES = {
|
|
1753
1769
|
en: {
|
|
1754
1770
|
installer: {
|
|
@@ -1911,25 +1927,55 @@ var init_installer_gui = __esm({
|
|
|
1911
1927
|
});
|
|
1912
1928
|
|
|
1913
1929
|
// installer/installer-electron.ts
|
|
1930
|
+
init_install_shared();
|
|
1914
1931
|
import { realpathSync } from "fs";
|
|
1915
1932
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1933
|
+
var WATCHDOG_MS = 12e4;
|
|
1934
|
+
function shouldArmWatchdog(args) {
|
|
1935
|
+
return !args.includes("--uninstall") && !args.includes("--no-open");
|
|
1936
|
+
}
|
|
1937
|
+
async function runInstallerLoop(handle, watchdogMs = WATCHDOG_MS) {
|
|
1938
|
+
let timer;
|
|
1939
|
+
try {
|
|
1940
|
+
const racers = [
|
|
1941
|
+
handle.done.then(() => ({ outcome: "completed" })),
|
|
1942
|
+
new Promise((resolve) => {
|
|
1943
|
+
process.once("SIGINT", () => resolve({ outcome: "signal" }));
|
|
1944
|
+
process.once("SIGTERM", () => resolve({ outcome: "signal" }));
|
|
1945
|
+
})
|
|
1946
|
+
];
|
|
1947
|
+
if (watchdogMs !== null) {
|
|
1948
|
+
racers.push(
|
|
1949
|
+
new Promise((resolve) => {
|
|
1950
|
+
timer = setTimeout(() => resolve({ outcome: "timeout" }), watchdogMs);
|
|
1951
|
+
handle.activity.then(() => {
|
|
1952
|
+
if (timer) clearTimeout(timer);
|
|
1953
|
+
}).catch(() => void 0);
|
|
1954
|
+
})
|
|
1955
|
+
);
|
|
1956
|
+
}
|
|
1957
|
+
return await Promise.race(racers);
|
|
1958
|
+
} finally {
|
|
1959
|
+
if (timer) clearTimeout(timer);
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1916
1962
|
async function main() {
|
|
1917
1963
|
const args = process.argv.slice(2);
|
|
1918
1964
|
const { startInstallerGui: startInstallerGui2, startUninstallerGui: startUninstallerGui2 } = await Promise.resolve().then(() => (init_installer_gui(), installer_gui_exports));
|
|
1919
1965
|
const opts = { openBrowser: !args.includes("--no-open") };
|
|
1920
|
-
const
|
|
1921
|
-
|
|
1922
|
-
await
|
|
1923
|
-
handle.done.then(() => {
|
|
1924
|
-
completed = true;
|
|
1925
|
-
}),
|
|
1926
|
-
new Promise((resolve) => {
|
|
1927
|
-
process.once("SIGINT", () => resolve());
|
|
1928
|
-
process.once("SIGTERM", () => resolve());
|
|
1929
|
-
})
|
|
1930
|
-
]);
|
|
1966
|
+
const isUninstall = args.includes("--uninstall");
|
|
1967
|
+
const handle = isUninstall ? await startUninstallerGui2(opts) : await startInstallerGui2(opts);
|
|
1968
|
+
const { outcome } = await runInstallerLoop(handle, shouldArmWatchdog(args) ? WATCHDOG_MS : null);
|
|
1931
1969
|
await handle.close().catch(() => void 0);
|
|
1932
|
-
|
|
1970
|
+
if (outcome === "timeout") {
|
|
1971
|
+
process.stderr.write("\nInstaller timed out waiting for the browser flow.\n");
|
|
1972
|
+
printHostedMcpHelp();
|
|
1973
|
+
process.exit(1);
|
|
1974
|
+
}
|
|
1975
|
+
const verb = isUninstall ? "Uninstall" : "Installation";
|
|
1976
|
+
process.stderr.write(outcome === "completed" ? `
|
|
1977
|
+
${verb} complete. Exiting.
|
|
1978
|
+
` : "\nExiting.\n");
|
|
1933
1979
|
}
|
|
1934
1980
|
var isEntrypoint = (() => {
|
|
1935
1981
|
try {
|
|
@@ -1947,3 +1993,8 @@ if (isEntrypoint) {
|
|
|
1947
1993
|
process.exit(1);
|
|
1948
1994
|
});
|
|
1949
1995
|
}
|
|
1996
|
+
export {
|
|
1997
|
+
WATCHDOG_MS,
|
|
1998
|
+
runInstallerLoop,
|
|
1999
|
+
shouldArmWatchdog
|
|
2000
|
+
};
|
package/dist/installer-gui.js
CHANGED
|
@@ -1027,7 +1027,7 @@ async function oauthLogin(opts) {
|
|
|
1027
1027
|
}
|
|
1028
1028
|
|
|
1029
1029
|
// installer/installer-gui.ts
|
|
1030
|
-
var VERSION = true ? "0.23.
|
|
1030
|
+
var VERSION = true ? "0.23.8" : "0.0.0-dev";
|
|
1031
1031
|
var MESSAGES = {
|
|
1032
1032
|
en: {
|
|
1033
1033
|
installer: {
|
|
@@ -1259,7 +1259,20 @@ async function loginWithOAuth() {
|
|
|
1259
1259
|
const { accessToken } = await oauthLogin({
|
|
1260
1260
|
authServerBaseUrl: OAUTH_BASE_URLS.prod[region],
|
|
1261
1261
|
clientName: `Leadbay MCP installer @ ${hostname()}`,
|
|
1262
|
-
log: () => void 0
|
|
1262
|
+
log: () => void 0,
|
|
1263
|
+
// Wrap the opener so a launch failure surfaces the (still-reachable) URL
|
|
1264
|
+
// on stderr instead of silently dropping it, while the listener stays up.
|
|
1265
|
+
openBrowser: async (u) => {
|
|
1266
|
+
try {
|
|
1267
|
+
await openInBrowser(u);
|
|
1268
|
+
} catch {
|
|
1269
|
+
process.stderr.write(`
|
|
1270
|
+
Open this URL in your browser to sign in:
|
|
1271
|
+
${u}
|
|
1272
|
+
|
|
1273
|
+
`);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1263
1276
|
});
|
|
1264
1277
|
const sessionId = randomUUID();
|
|
1265
1278
|
const accountLabel = `Leadbay OAuth (${region.toUpperCase()})`;
|
|
@@ -1744,36 +1757,6 @@ function pageHtml(locale = "en") {
|
|
|
1744
1757
|
</body>
|
|
1745
1758
|
</html>`;
|
|
1746
1759
|
}
|
|
1747
|
-
async function openBrowser(url) {
|
|
1748
|
-
const { spawn: spawn4 } = await import("child_process");
|
|
1749
|
-
const trySpawn = (command, args) => new Promise((resolve) => {
|
|
1750
|
-
try {
|
|
1751
|
-
const child = spawn4(command, args, { stdio: "ignore", detached: true });
|
|
1752
|
-
child.unref();
|
|
1753
|
-
child.on("error", () => resolve(false));
|
|
1754
|
-
child.on("close", (code) => resolve(code === 0));
|
|
1755
|
-
} catch {
|
|
1756
|
-
resolve(false);
|
|
1757
|
-
}
|
|
1758
|
-
});
|
|
1759
|
-
if (process.platform === "darwin") {
|
|
1760
|
-
await trySpawn("open", [url]);
|
|
1761
|
-
return;
|
|
1762
|
-
}
|
|
1763
|
-
if (process.platform === "win32") {
|
|
1764
|
-
await trySpawn("cmd", ["/c", "start", "", url]);
|
|
1765
|
-
return;
|
|
1766
|
-
}
|
|
1767
|
-
const candidates = ["xdg-open", "sensible-browser", "google-chrome", "chromium-browser", "firefox"];
|
|
1768
|
-
for (const cmd of candidates) {
|
|
1769
|
-
if (await trySpawn(cmd, [url])) return;
|
|
1770
|
-
}
|
|
1771
|
-
process.stderr.write(`
|
|
1772
|
-
Open this URL in your browser to continue:
|
|
1773
|
-
${url}
|
|
1774
|
-
|
|
1775
|
-
`);
|
|
1776
|
-
}
|
|
1777
1760
|
function makeGuiServer(options, pageContent, extraRoutes, logLabel) {
|
|
1778
1761
|
let expectedHost = `127.0.0.1:${(options.port ?? PORT) || 0}`;
|
|
1779
1762
|
let resolveDone;
|
|
@@ -1783,11 +1766,16 @@ function makeGuiServer(options, pageContent, extraRoutes, logLabel) {
|
|
|
1783
1766
|
const onDone = () => setTimeout(() => {
|
|
1784
1767
|
resolveDone();
|
|
1785
1768
|
}, 1500);
|
|
1769
|
+
let resolveActivity;
|
|
1770
|
+
const activity = new Promise((r) => {
|
|
1771
|
+
resolveActivity = r;
|
|
1772
|
+
});
|
|
1786
1773
|
const server = createServer2(async (req, res) => {
|
|
1787
1774
|
if (!isAllowedOrigin(req, expectedHost)) {
|
|
1788
1775
|
sendJson(res, 403, { ok: false, error: "forbidden" });
|
|
1789
1776
|
return;
|
|
1790
1777
|
}
|
|
1778
|
+
resolveActivity();
|
|
1791
1779
|
try {
|
|
1792
1780
|
if (req.method === "GET" && req.url === "/") {
|
|
1793
1781
|
const raw = await pageContent();
|
|
@@ -1811,8 +1799,16 @@ function makeGuiServer(options, pageContent, extraRoutes, logLabel) {
|
|
|
1811
1799
|
const url = `http://127.0.0.1:${port}/`;
|
|
1812
1800
|
process.stderr.write(`Leadbay MCP ${logLabel} GUI: ${url}
|
|
1813
1801
|
`);
|
|
1814
|
-
if (options.openBrowser !== false)
|
|
1815
|
-
|
|
1802
|
+
if (options.openBrowser !== false) {
|
|
1803
|
+
openInBrowser(url).catch(() => {
|
|
1804
|
+
process.stderr.write(`
|
|
1805
|
+
Open this URL in your browser to continue:
|
|
1806
|
+
${url}
|
|
1807
|
+
|
|
1808
|
+
`);
|
|
1809
|
+
});
|
|
1810
|
+
}
|
|
1811
|
+
resolve({ url, done, activity, close: () => new Promise((res, rej) => server.close((e) => e ? rej(e) : res())) });
|
|
1816
1812
|
});
|
|
1817
1813
|
});
|
|
1818
1814
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leadbay/mcp",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.8",
|
|
4
4
|
"mcpName": "io.github.leadbay/leadbay-mcp",
|
|
5
5
|
"description": "Model Context Protocol (MCP) server for Leadbay — AI lead discovery, qualification, and enrichment for Claude Desktop, Cursor, and Claude Code.",
|
|
6
6
|
"type": "module",
|