@leadbay/mcp 0.17.2 → 0.17.3

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.
@@ -3,7 +3,6 @@
3
3
  // installer/installer-gui.ts
4
4
  import { createServer as createServer2 } from "http";
5
5
  import { randomUUID } from "crypto";
6
- import { realpathSync } from "fs";
7
6
  import { resolve as resolvePath } from "path";
8
7
  import { fileURLToPath } from "url";
9
8
 
@@ -874,7 +873,7 @@ async function oauthLogin(opts) {
874
873
  }
875
874
 
876
875
  // installer/installer-gui.ts
877
- var VERSION = "0.17.2";
876
+ var VERSION = "0.17.3";
878
877
  var PORT = Number(process.env.LEADBAY_INSTALLER_PORT ?? 0);
879
878
  var sessions = /* @__PURE__ */ new Map();
880
879
  var OAUTH_BASE_URLS = {
@@ -1043,7 +1042,7 @@ async function install(body) {
1043
1042
  ].join("\n");
1044
1043
  return { ok: results.some((result) => result.ok), output: sanitizeOutput(output), results };
1045
1044
  }
1046
- async function streamInstall(url, res) {
1045
+ async function streamInstall(url, res, onDone) {
1047
1046
  cleanupSessions();
1048
1047
  res.writeHead(200, {
1049
1048
  "content-type": "text/event-stream; charset=utf-8",
@@ -1055,16 +1054,23 @@ async function streamInstall(url, res) {
1055
1054
  const includeWrite = url.searchParams.get("write") !== "0";
1056
1055
  const telemetryEnabled = url.searchParams.get("telemetry") !== "0";
1057
1056
  const emit = (level, message) => sendSse(res, { level, message: sanitizeOutput(message) });
1057
+ const abort = (msg) => {
1058
+ emit("done", msg);
1059
+ res.end();
1060
+ };
1061
+ const finish = (msg) => {
1062
+ emit("done", msg);
1063
+ res.end();
1064
+ onDone?.();
1065
+ };
1058
1066
  if (!session) {
1059
1067
  emit("error", "Login expired. Go back and sign in again.");
1060
- emit("done", "Install stopped.");
1061
- res.end();
1068
+ abort("Install stopped.");
1062
1069
  return;
1063
1070
  }
1064
1071
  if (!clientIds.length) {
1065
1072
  emit("error", "Select at least one agent.");
1066
- emit("done", "Install stopped.");
1067
- res.end();
1073
+ abort("Install stopped.");
1068
1074
  return;
1069
1075
  }
1070
1076
  emit("info", `Connected to ${session.accountLabel}.`);
@@ -1075,8 +1081,7 @@ async function streamInstall(url, res) {
1075
1081
  const selectedHasOnlyManualSetup = selected.length > 0 && selected.every(isManualSetupClient);
1076
1082
  if (!selected.length) {
1077
1083
  emit("error", "No selected agents were detected on this machine.");
1078
- emit("done", "Install stopped.");
1079
- res.end();
1084
+ abort("Install stopped.");
1080
1085
  return;
1081
1086
  }
1082
1087
  let okCount = 0;
@@ -1090,11 +1095,16 @@ async function streamInstall(url, res) {
1090
1095
  emit("error", `${result.label}: ${result.message}`);
1091
1096
  }
1092
1097
  }
1093
- emit(okCount > 0 ? "success" : "error", selectedHasOnlyManualSetup ? "Manual ChatGPT setup instructions ready." : `${okCount}/${selected.length} agent(s) installed, updated, or prepared.`);
1094
- emit("done", selectedHasOnlyManualSetup ? "Follow the manual setup instructions shown above." : "Restart your MCP client(s) to pick up the new server.");
1095
- res.end();
1098
+ const summary = selectedHasOnlyManualSetup ? "Manual ChatGPT setup instructions ready." : `${okCount}/${selected.length} agent(s) installed, updated, or prepared.`;
1099
+ const closing = selectedHasOnlyManualSetup ? "Follow the manual setup instructions shown above." : "Restart your MCP client(s) to pick up the new server.";
1100
+ emit(okCount > 0 ? "success" : "error", summary);
1101
+ if (okCount > 0) {
1102
+ finish(closing);
1103
+ } else {
1104
+ abort(closing);
1105
+ }
1096
1106
  }
1097
- async function streamUninstall(url, res) {
1107
+ async function streamUninstall(url, res, onDone) {
1098
1108
  res.writeHead(200, {
1099
1109
  "content-type": "text/event-stream; charset=utf-8",
1100
1110
  "cache-control": "no-cache, no-transform",
@@ -1102,18 +1112,25 @@ async function streamUninstall(url, res) {
1102
1112
  });
1103
1113
  const clientIds = (url.searchParams.get("clients") ?? "").split(",").filter(Boolean);
1104
1114
  const emit = (level, message) => sendSse(res, { level, message });
1115
+ const abort = (msg) => {
1116
+ emit("done", msg);
1117
+ res.end();
1118
+ };
1119
+ const finish = (msg) => {
1120
+ emit("done", msg);
1121
+ res.end();
1122
+ onDone?.();
1123
+ };
1105
1124
  if (!clientIds.length) {
1106
1125
  emit("error", "Select at least one agent.");
1107
- emit("done", "Uninstall stopped.");
1108
- res.end();
1126
+ abort("Uninstall stopped.");
1109
1127
  return;
1110
1128
  }
1111
1129
  const detected = await detectClients();
1112
1130
  const selected = detected.filter((c) => clientIds.includes(c.id));
1113
1131
  if (!selected.length) {
1114
1132
  emit("error", "No selected agents were detected on this machine.");
1115
- emit("done", "Uninstall stopped.");
1116
- res.end();
1133
+ abort("Uninstall stopped.");
1117
1134
  return;
1118
1135
  }
1119
1136
  let okCount = 0;
@@ -1137,8 +1154,7 @@ async function streamUninstall(url, res) {
1137
1154
  }
1138
1155
  }
1139
1156
  emit(okCount > 0 ? "success" : "error", `${okCount}/${selected.length} agent(s) removed.`);
1140
- emit("done", "Restart your MCP client(s) to complete the removal.");
1141
- res.end();
1157
+ finish("Restart your MCP client(s) to complete the removal.");
1142
1158
  }
1143
1159
  function pageUninstallHtml() {
1144
1160
  return `<!doctype html>
@@ -1338,8 +1354,15 @@ async function openBrowser(url) {
1338
1354
 
1339
1355
  `);
1340
1356
  }
1341
- async function startInstallerGui(options = {}) {
1357
+ function makeGuiServer(options, pageContent, extraRoutes, logLabel) {
1342
1358
  let expectedHost = `127.0.0.1:${(options.port ?? PORT) || 0}`;
1359
+ let resolveDone;
1360
+ const done = new Promise((r) => {
1361
+ resolveDone = r;
1362
+ });
1363
+ const onDone = () => setTimeout(() => {
1364
+ resolveDone();
1365
+ }, 1500);
1343
1366
  const server = createServer2(async (req, res) => {
1344
1367
  if (!isAllowedOrigin(req, expectedHost)) {
1345
1368
  sendJson(res, 403, { ok: false, error: "forbidden" });
@@ -1347,37 +1370,18 @@ async function startInstallerGui(options = {}) {
1347
1370
  }
1348
1371
  try {
1349
1372
  if (req.method === "GET" && req.url === "/") {
1350
- const raw = pageHtml();
1373
+ const raw = pageContent();
1351
1374
  res.writeHead(200, { "content-type": "text/html; charset=utf-8", "content-length": Buffer.byteLength(raw) });
1352
1375
  res.end(raw);
1353
1376
  return;
1354
1377
  }
1355
- if (req.method === "GET" && req.url === "/api/status") {
1356
- sendJson(res, 200, {
1357
- os: formatInstallOsLabel(),
1358
- hostedMcpUrl: HOSTED_MCP_URL,
1359
- clients: await clientsWithConfiguredStatus()
1360
- });
1361
- return;
1362
- }
1363
- if (req.method === "POST" && req.url === "/api/oauth-login") {
1364
- sendJson(res, 200, await loginWithOAuth());
1365
- return;
1366
- }
1367
- if (req.method === "POST" && req.url === "/api/install") {
1368
- sendJson(res, 200, await install(await readJson(req)));
1369
- return;
1370
- }
1371
- if (req.method === "GET" && req.url?.startsWith("/api/install-stream")) {
1372
- await streamInstall(new URL(req.url, "http://127.0.0.1"), res);
1373
- return;
1374
- }
1378
+ if (await extraRoutes(req, res, onDone)) return;
1375
1379
  sendJson(res, 404, { ok: false, error: "not found" });
1376
1380
  } catch (err) {
1377
1381
  sendJson(res, 500, { ok: false, error: err?.message ?? String(err) });
1378
1382
  }
1379
1383
  });
1380
- return await new Promise((resolve, reject) => {
1384
+ return new Promise((resolve, reject) => {
1381
1385
  server.once("error", reject);
1382
1386
  server.listen(options.port ?? PORT, "127.0.0.1", async () => {
1383
1387
  server.off("error", reject);
@@ -1385,82 +1389,46 @@ async function startInstallerGui(options = {}) {
1385
1389
  const port = typeof address === "object" && address ? address.port : options.port ?? PORT;
1386
1390
  expectedHost = `127.0.0.1:${port}`;
1387
1391
  const url = `http://127.0.0.1:${port}/`;
1388
- process.stderr.write(`Leadbay MCP installer GUI: ${url}
1392
+ process.stderr.write(`Leadbay MCP ${logLabel} GUI: ${url}
1389
1393
  `);
1390
1394
  if (options.openBrowser !== false) await openBrowser(url).catch(() => void 0);
1391
- resolve({ url, close: () => new Promise((closeResolve, closeReject) => server.close((err) => err ? closeReject(err) : closeResolve())) });
1395
+ resolve({ url, done, close: () => new Promise((res, rej) => server.close((e) => e ? rej(e) : res())) });
1392
1396
  });
1393
1397
  });
1394
1398
  }
1395
- async function startUninstallerGui(options = {}) {
1396
- let expectedHost = `127.0.0.1:${(options.port ?? PORT) || 0}`;
1397
- const server = createServer2(async (req, res) => {
1398
- if (!isAllowedOrigin(req, expectedHost)) {
1399
- sendJson(res, 403, { ok: false, error: "forbidden" });
1400
- return;
1399
+ function startInstallerGui(options = {}) {
1400
+ return makeGuiServer(options, pageHtml, async (req, res, onDone) => {
1401
+ if (req.method === "GET" && req.url === "/api/status") {
1402
+ sendJson(res, 200, { os: formatInstallOsLabel(), hostedMcpUrl: HOSTED_MCP_URL, clients: await clientsWithConfiguredStatus() });
1403
+ return true;
1401
1404
  }
1402
- try {
1403
- if (req.method === "GET" && req.url === "/") {
1404
- const raw = pageUninstallHtml();
1405
- res.writeHead(200, { "content-type": "text/html; charset=utf-8", "content-length": Buffer.byteLength(raw) });
1406
- res.end(raw);
1407
- return;
1408
- }
1409
- if (req.method === "GET" && req.url === "/api/status") {
1410
- sendJson(res, 200, {
1411
- os: formatInstallOsLabel(),
1412
- clients: await clientsWithConfiguredStatus()
1413
- });
1414
- return;
1415
- }
1416
- if (req.method === "GET" && req.url?.startsWith("/api/uninstall-stream")) {
1417
- await streamUninstall(new URL(req.url, "http://127.0.0.1"), res);
1418
- return;
1419
- }
1420
- sendJson(res, 404, { ok: false, error: "not found" });
1421
- } catch (err) {
1422
- sendJson(res, 500, { ok: false, error: err?.message ?? String(err) });
1405
+ if (req.method === "POST" && req.url === "/api/oauth-login") {
1406
+ sendJson(res, 200, await loginWithOAuth());
1407
+ return true;
1423
1408
  }
1424
- });
1425
- return await new Promise((resolve, reject) => {
1426
- server.once("error", reject);
1427
- server.listen(options.port ?? PORT, "127.0.0.1", async () => {
1428
- server.off("error", reject);
1429
- const address = server.address();
1430
- const port = typeof address === "object" && address ? address.port : options.port ?? PORT;
1431
- expectedHost = `127.0.0.1:${port}`;
1432
- const url = `http://127.0.0.1:${port}/`;
1433
- process.stderr.write(`Leadbay MCP uninstaller GUI: ${url}
1434
- `);
1435
- if (options.openBrowser !== false) await openBrowser(url).catch(() => void 0);
1436
- resolve({ url, close: () => new Promise((closeResolve, closeReject) => server.close((err) => err ? closeReject(err) : closeResolve())) });
1437
- });
1438
- });
1439
- }
1440
- async function main() {
1441
- const uninstall = process.argv.includes("--uninstall");
1442
- const handle = uninstall ? await startUninstallerGui({ openBrowser: !process.argv.includes("--no-open") }) : await startInstallerGui({ openBrowser: !process.argv.includes("--no-open") });
1443
- await new Promise((resolve) => {
1444
- process.once("SIGINT", () => resolve());
1445
- process.once("SIGTERM", () => resolve());
1446
- });
1447
- await handle.close().catch(() => void 0);
1409
+ if (req.method === "POST" && req.url === "/api/install") {
1410
+ sendJson(res, 200, await install(await readJson(req)));
1411
+ return true;
1412
+ }
1413
+ if (req.method === "GET" && req.url?.startsWith("/api/install-stream")) {
1414
+ await streamInstall(new URL(req.url, "http://127.0.0.1"), res, onDone);
1415
+ return true;
1416
+ }
1417
+ return false;
1418
+ }, "installer");
1448
1419
  }
1449
- var isEntrypoint = (() => {
1450
- try {
1451
- const entry = process.argv[1];
1452
- if (!entry) return false;
1453
- return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(entry);
1454
- } catch {
1420
+ function startUninstallerGui(options = {}) {
1421
+ return makeGuiServer(options, pageUninstallHtml, async (req, res, onDone) => {
1422
+ if (req.method === "GET" && req.url === "/api/status") {
1423
+ sendJson(res, 200, { os: formatInstallOsLabel(), clients: await clientsWithConfiguredStatus() });
1424
+ return true;
1425
+ }
1426
+ if (req.method === "GET" && req.url?.startsWith("/api/uninstall-stream")) {
1427
+ await streamUninstall(new URL(req.url, "http://127.0.0.1"), res, onDone);
1428
+ return true;
1429
+ }
1455
1430
  return false;
1456
- }
1457
- })();
1458
- if (isEntrypoint) {
1459
- main().catch((err) => {
1460
- process.stderr.write(`leadbay-mcp-installer: ${err?.message ?? err}
1461
- `);
1462
- process.exit(1);
1463
- });
1431
+ }, "uninstaller");
1464
1432
  }
1465
1433
  export {
1466
1434
  install,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leadbay/mcp",
3
- "version": "0.17.2",
3
+ "version": "0.17.3",
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",
@@ -28,9 +28,8 @@
28
28
  "test": "vitest run",
29
29
  "test:smoke": "vitest run --config vitest.smoke.config.ts",
30
30
  "prepublishOnly": "pnpm run build && pnpm run typecheck && pnpm run test",
31
- "installer": "pnpm run build && electron --no-sandbox installer/electron-main.cjs",
32
- "installer:gui": "pnpm run installer",
33
- "installer:gui:web": "pnpm run build && node dist/installer-gui.js"
31
+ "installer": "pnpm run build && node dist/installer-electron.js",
32
+ "installer:gui": "pnpm run installer"
34
33
  },
35
34
  "dependencies": {
36
35
  "@hono/node-server": "^1.13.7",