@leadbay/mcp 0.23.4 → 0.23.6
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 +6 -0
- package/dist/bin.js +177 -25
- package/dist/http-server.js +157 -19
- package/dist/installer-electron.js +244 -77
- package/dist/installer-gui.js +243 -76
- package/package.json +1 -1
package/dist/installer-gui.js
CHANGED
|
@@ -378,7 +378,7 @@ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
|
378
378
|
|
|
379
379
|
// installer/install-shared.ts
|
|
380
380
|
import { spawn as spawn2 } from "child_process";
|
|
381
|
-
import { existsSync, readFileSync } from "fs";
|
|
381
|
+
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
382
382
|
import { join } from "path";
|
|
383
383
|
import { homedir } from "os";
|
|
384
384
|
var HOSTED_MCP_URL = "https://leadbay-mcp-prod.fly.dev/mcp";
|
|
@@ -435,6 +435,15 @@ async function windowsStoreAppInstalled(packageName, appName) {
|
|
|
435
435
|
child.on("error", () => resolve(false));
|
|
436
436
|
});
|
|
437
437
|
}
|
|
438
|
+
function isClaudeStorePackagePresent(localAppData) {
|
|
439
|
+
const packagesDir = join(localAppData, "Packages");
|
|
440
|
+
if (!existsSync(packagesDir)) return false;
|
|
441
|
+
try {
|
|
442
|
+
return readdirSync(packagesDir).some((name) => /^Claude_/i.test(name));
|
|
443
|
+
} catch {
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
438
447
|
async function isClaudeDesktopInstalled(home) {
|
|
439
448
|
if (process.platform === "darwin") {
|
|
440
449
|
return existsSync("/Applications/Claude.app") || existsSync(home + "/Applications/Claude.app");
|
|
@@ -443,12 +452,15 @@ async function isClaudeDesktopInstalled(home) {
|
|
|
443
452
|
const local = process.env.LOCALAPPDATA ?? home + "/AppData/Local";
|
|
444
453
|
const programFiles = process.env.ProgramFiles;
|
|
445
454
|
const programFilesX86 = process.env["ProgramFiles(x86)"];
|
|
446
|
-
|
|
455
|
+
const exeInstalled = [
|
|
447
456
|
local + "/Programs/Claude/Claude.exe",
|
|
448
457
|
local + "/Claude/Claude.exe",
|
|
449
458
|
programFiles ? programFiles + "/Claude/Claude.exe" : null,
|
|
450
459
|
programFilesX86 ? programFilesX86 + "/Claude/Claude.exe" : null
|
|
451
460
|
].some((candidate) => candidate !== null && existsSync(candidate));
|
|
461
|
+
if (exeInstalled) return true;
|
|
462
|
+
if (isClaudeStorePackagePresent(local)) return true;
|
|
463
|
+
return await windowsStoreAppInstalled("AnthropicPBC.Claude", "Claude");
|
|
452
464
|
}
|
|
453
465
|
const desktopBin = await findOnPath("claude-desktop");
|
|
454
466
|
if (desktopBin) return true;
|
|
@@ -523,7 +535,7 @@ import { createHash, randomBytes } from "crypto";
|
|
|
523
535
|
import { createServer } from "http";
|
|
524
536
|
import { request as httpsRequestRaw } from "https";
|
|
525
537
|
import { spawn as spawn3 } from "child_process";
|
|
526
|
-
import { readdirSync, existsSync as existsSync2 } from "fs";
|
|
538
|
+
import { readdirSync as readdirSync2, existsSync as existsSync2 } from "fs";
|
|
527
539
|
var LEADBAY_LOOPBACK_PORTS = [51789, 51790, 51791, 51792];
|
|
528
540
|
var BrowserOpenFailedError = class extends Error {
|
|
529
541
|
authorizeUrl;
|
|
@@ -859,7 +871,7 @@ function browserLaunchEnv(debug) {
|
|
|
859
871
|
}
|
|
860
872
|
if (!env.WAYLAND_DISPLAY && runtimeDir) {
|
|
861
873
|
try {
|
|
862
|
-
const sock =
|
|
874
|
+
const sock = readdirSync2(runtimeDir).find((f) => /^wayland-\d+$/.test(f));
|
|
863
875
|
if (sock) {
|
|
864
876
|
env.WAYLAND_DISPLAY = sock;
|
|
865
877
|
debug?.(`browserLaunchEnv: injected WAYLAND_DISPLAY=${sock}`);
|
|
@@ -876,7 +888,7 @@ function browserLaunchEnv(debug) {
|
|
|
876
888
|
}
|
|
877
889
|
if (!env.DISPLAY) {
|
|
878
890
|
try {
|
|
879
|
-
const x =
|
|
891
|
+
const x = readdirSync2("/tmp/.X11-unix").map((f) => f.match(/^X(\d+)$/)?.[1]).filter((n) => !!n).sort((a, b) => Number(a) - Number(b))[0];
|
|
880
892
|
env.DISPLAY = x !== void 0 ? `:${x}` : ":0";
|
|
881
893
|
} catch {
|
|
882
894
|
env.DISPLAY = ":0";
|
|
@@ -887,7 +899,7 @@ function browserLaunchEnv(debug) {
|
|
|
887
899
|
try {
|
|
888
900
|
let xauth;
|
|
889
901
|
if (runtimeDir) {
|
|
890
|
-
const cookie =
|
|
902
|
+
const cookie = readdirSync2(runtimeDir).find(
|
|
891
903
|
(f) => /^\.mutter-Xwaylandauth\./.test(f)
|
|
892
904
|
);
|
|
893
905
|
if (cookie) xauth = `${runtimeDir}/${cookie}`;
|
|
@@ -1015,7 +1027,157 @@ async function oauthLogin(opts) {
|
|
|
1015
1027
|
}
|
|
1016
1028
|
|
|
1017
1029
|
// installer/installer-gui.ts
|
|
1018
|
-
var VERSION = true ? "0.23.
|
|
1030
|
+
var VERSION = true ? "0.23.6" : "0.0.0-dev";
|
|
1031
|
+
var MESSAGES = {
|
|
1032
|
+
en: {
|
|
1033
|
+
installer: {
|
|
1034
|
+
docTitle: "Leadbay MCP installer",
|
|
1035
|
+
steps: {
|
|
1036
|
+
1: { title: "Connect Leadbay", sub: "Sign in to install Leadbay across your AI agents." },
|
|
1037
|
+
2: { title: "Choose your agents", sub: "Pick where to install Leadbay." },
|
|
1038
|
+
3: { title: "Installing", sub: "Keep this window open until it's done." }
|
|
1039
|
+
},
|
|
1040
|
+
btnSignIn: "Sign in with Leadbay",
|
|
1041
|
+
btnInstall: "Install",
|
|
1042
|
+
btnBack: "Back",
|
|
1043
|
+
btnRefresh: "Refresh",
|
|
1044
|
+
noClientsDetected: "No supported MCP client detected on this machine.",
|
|
1045
|
+
noAgentsDetected: "No supported agents detected.",
|
|
1046
|
+
selectAtLeastOne: "Select at least one agent.",
|
|
1047
|
+
openingSignIn: "Opening Leadbay sign-in in your browser...",
|
|
1048
|
+
oauthFailed: "OAuth login failed.",
|
|
1049
|
+
successInstalled: "MCP successfully installed",
|
|
1050
|
+
noneInstalled: "No agents were installed.",
|
|
1051
|
+
streamDisconnected: "Install stream disconnected.",
|
|
1052
|
+
closeWindow: "You can close this window.",
|
|
1053
|
+
allSet: "All set",
|
|
1054
|
+
somethingWrong: "Something went wrong",
|
|
1055
|
+
badgeManual: "manual",
|
|
1056
|
+
badgeUpdate: "update",
|
|
1057
|
+
badgeInstall: "install"
|
|
1058
|
+
},
|
|
1059
|
+
uninstaller: {
|
|
1060
|
+
docTitle: "Leadbay MCP uninstaller",
|
|
1061
|
+
steps: {
|
|
1062
|
+
1: { title: "Remove Leadbay MCP", sub: "Select the agents to remove Leadbay MCP from." },
|
|
1063
|
+
2: { title: "Removing", sub: "Keep this window open until it's done." }
|
|
1064
|
+
},
|
|
1065
|
+
btnRemove: "Remove selected",
|
|
1066
|
+
btnBack: "Back",
|
|
1067
|
+
btnRefresh: "Refresh",
|
|
1068
|
+
noInstallDetected: "No Leadbay MCP installation detected on this machine.",
|
|
1069
|
+
selectAtLeastOne: "Select at least one agent.",
|
|
1070
|
+
successRemoved: "MCP successfully removed",
|
|
1071
|
+
noneRemoved: "No agents were removed.",
|
|
1072
|
+
streamDisconnected: "Uninstall stream disconnected.",
|
|
1073
|
+
closeWindow: "You can close this window.",
|
|
1074
|
+
allSet: "All set",
|
|
1075
|
+
somethingWrong: "Something went wrong"
|
|
1076
|
+
},
|
|
1077
|
+
server: {
|
|
1078
|
+
loginExpired: "Login expired. Go back and sign in again.",
|
|
1079
|
+
selectAtLeastOne: "Select at least one agent.",
|
|
1080
|
+
noSelectedDetected: "No selected agents were detected on this machine.",
|
|
1081
|
+
installStopped: "Install stopped.",
|
|
1082
|
+
uninstallStopped: "Uninstall stopped.",
|
|
1083
|
+
connectedTo: (label) => `Connected to ${label}.`,
|
|
1084
|
+
toolFlags: (write, telemetry) => `Write tools ${write ? "enabled" : "disabled"}; telemetry ${telemetry ? "enabled" : "disabled"}.`,
|
|
1085
|
+
refreshingDetection: "Refreshing installed-agent detection...",
|
|
1086
|
+
installing: (label) => `Installing ${label}...`,
|
|
1087
|
+
preparingManual: (label) => `Preparing ${label} manual setup...`,
|
|
1088
|
+
removing: (label) => `Removing from ${label}...`,
|
|
1089
|
+
installedSummary: (ok, total) => `${ok}/${total} agent(s) installed, updated, or prepared.`,
|
|
1090
|
+
manualReady: "Manual ChatGPT setup instructions ready.",
|
|
1091
|
+
removedSummary: (ok, total) => `${ok}/${total} agent(s) removed.`,
|
|
1092
|
+
restartClients: "Restart your MCP client(s) to pick up the new server.",
|
|
1093
|
+
followManual: "Follow the manual setup instructions shown above.",
|
|
1094
|
+
completeRemoval: "Restart your MCP client(s) to complete the removal.",
|
|
1095
|
+
loggedInBackend: (region) => `Logged in to ${region.toUpperCase()} backend.`,
|
|
1096
|
+
settingsLine: (write, telemetry) => `Settings: write tools ${write ? "on" : "off"}, telemetry ${telemetry ? "on" : "off"}.`,
|
|
1097
|
+
installSummaryHeader: "Install summary:",
|
|
1098
|
+
noAgentsRemoved: "No agents were removed."
|
|
1099
|
+
}
|
|
1100
|
+
},
|
|
1101
|
+
fr: {
|
|
1102
|
+
installer: {
|
|
1103
|
+
docTitle: "Installateur Leadbay MCP",
|
|
1104
|
+
steps: {
|
|
1105
|
+
1: { title: "Connectez Leadbay", sub: "Connectez-vous pour installer Leadbay sur vos agents IA." },
|
|
1106
|
+
2: { title: "Choisissez vos agents", sub: "Choisissez o\xF9 installer Leadbay." },
|
|
1107
|
+
3: { title: "Installation en cours", sub: "Gardez cette fen\xEAtre ouverte jusqu'\xE0 la fin." }
|
|
1108
|
+
},
|
|
1109
|
+
btnSignIn: "Se connecter avec Leadbay",
|
|
1110
|
+
btnInstall: "Installer",
|
|
1111
|
+
btnBack: "Retour",
|
|
1112
|
+
btnRefresh: "Actualiser",
|
|
1113
|
+
noClientsDetected: "Aucun client MCP compatible d\xE9tect\xE9 sur cette machine.",
|
|
1114
|
+
noAgentsDetected: "Aucun agent compatible d\xE9tect\xE9.",
|
|
1115
|
+
selectAtLeastOne: "S\xE9lectionnez au moins un agent.",
|
|
1116
|
+
openingSignIn: "Ouverture de la connexion Leadbay dans votre navigateur...",
|
|
1117
|
+
oauthFailed: "\xC9chec de la connexion OAuth.",
|
|
1118
|
+
successInstalled: "MCP install\xE9 avec succ\xE8s",
|
|
1119
|
+
noneInstalled: "Aucun agent n'a \xE9t\xE9 install\xE9.",
|
|
1120
|
+
streamDisconnected: "Flux d'installation interrompu.",
|
|
1121
|
+
closeWindow: "Vous pouvez fermer cette fen\xEAtre.",
|
|
1122
|
+
allSet: "Termin\xE9",
|
|
1123
|
+
somethingWrong: "Une erreur est survenue",
|
|
1124
|
+
badgeManual: "manuel",
|
|
1125
|
+
badgeUpdate: "mettre \xE0 jour",
|
|
1126
|
+
badgeInstall: "installer"
|
|
1127
|
+
},
|
|
1128
|
+
uninstaller: {
|
|
1129
|
+
docTitle: "D\xE9sinstallateur Leadbay MCP",
|
|
1130
|
+
steps: {
|
|
1131
|
+
1: { title: "Supprimer Leadbay MCP", sub: "S\xE9lectionnez les agents desquels supprimer Leadbay MCP." },
|
|
1132
|
+
2: { title: "Suppression en cours", sub: "Gardez cette fen\xEAtre ouverte jusqu'\xE0 la fin." }
|
|
1133
|
+
},
|
|
1134
|
+
btnRemove: "Supprimer la s\xE9lection",
|
|
1135
|
+
btnBack: "Retour",
|
|
1136
|
+
btnRefresh: "Actualiser",
|
|
1137
|
+
noInstallDetected: "Aucune installation Leadbay MCP d\xE9tect\xE9e sur cette machine.",
|
|
1138
|
+
selectAtLeastOne: "S\xE9lectionnez au moins un agent.",
|
|
1139
|
+
successRemoved: "MCP supprim\xE9 avec succ\xE8s",
|
|
1140
|
+
noneRemoved: "Aucun agent n'a \xE9t\xE9 supprim\xE9.",
|
|
1141
|
+
streamDisconnected: "Flux de d\xE9sinstallation interrompu.",
|
|
1142
|
+
closeWindow: "Vous pouvez fermer cette fen\xEAtre.",
|
|
1143
|
+
allSet: "Termin\xE9",
|
|
1144
|
+
somethingWrong: "Une erreur est survenue"
|
|
1145
|
+
},
|
|
1146
|
+
server: {
|
|
1147
|
+
loginExpired: "Session expir\xE9e. Revenez en arri\xE8re et reconnectez-vous.",
|
|
1148
|
+
selectAtLeastOne: "S\xE9lectionnez au moins un agent.",
|
|
1149
|
+
noSelectedDetected: "Aucun des agents s\xE9lectionn\xE9s n'a \xE9t\xE9 d\xE9tect\xE9 sur cette machine.",
|
|
1150
|
+
installStopped: "Installation arr\xEAt\xE9e.",
|
|
1151
|
+
uninstallStopped: "D\xE9sinstallation arr\xEAt\xE9e.",
|
|
1152
|
+
connectedTo: (label) => `Connect\xE9 \xE0 ${label}.`,
|
|
1153
|
+
toolFlags: (write, telemetry) => `Outils d'\xE9criture ${write ? "activ\xE9s" : "d\xE9sactiv\xE9s"} ; t\xE9l\xE9m\xE9trie ${telemetry ? "activ\xE9e" : "d\xE9sactiv\xE9e"}.`,
|
|
1154
|
+
refreshingDetection: "Actualisation de la d\xE9tection des agents install\xE9s...",
|
|
1155
|
+
installing: (label) => `Installation de ${label}...`,
|
|
1156
|
+
preparingManual: (label) => `Pr\xE9paration de la configuration manuelle de ${label}...`,
|
|
1157
|
+
removing: (label) => `Suppression de ${label}...`,
|
|
1158
|
+
installedSummary: (ok, total) => `${ok}/${total} agent(s) install\xE9(s), mis \xE0 jour ou pr\xE9par\xE9(s).`,
|
|
1159
|
+
manualReady: "Instructions de configuration manuelle de ChatGPT pr\xEAtes.",
|
|
1160
|
+
removedSummary: (ok, total) => `${ok}/${total} agent(s) supprim\xE9(s).`,
|
|
1161
|
+
restartClients: "Red\xE9marrez votre/vos client(s) MCP pour charger le nouveau serveur.",
|
|
1162
|
+
followManual: "Suivez les instructions de configuration manuelle ci-dessus.",
|
|
1163
|
+
completeRemoval: "Red\xE9marrez votre/vos client(s) MCP pour terminer la suppression.",
|
|
1164
|
+
loggedInBackend: (region) => `Connect\xE9 au backend ${region.toUpperCase()}.`,
|
|
1165
|
+
settingsLine: (write, telemetry) => `Param\xE8tres : outils d'\xE9criture ${write ? "activ\xE9s" : "d\xE9sactiv\xE9s"}, t\xE9l\xE9m\xE9trie ${telemetry ? "activ\xE9e" : "d\xE9sactiv\xE9e"}.`,
|
|
1166
|
+
installSummaryHeader: "R\xE9capitulatif de l'installation :",
|
|
1167
|
+
noAgentsRemoved: "Aucun agent n'a \xE9t\xE9 supprim\xE9."
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
};
|
|
1171
|
+
async function detectLocale() {
|
|
1172
|
+
try {
|
|
1173
|
+
return await inferRegionViaStargate({ staging: false }) === "fr" ? "fr" : "en";
|
|
1174
|
+
} catch {
|
|
1175
|
+
return "en";
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
function parseLocale(raw) {
|
|
1179
|
+
return raw === "fr" ? "fr" : "en";
|
|
1180
|
+
}
|
|
1019
1181
|
var PORT = Number(process.env.LEADBAY_INSTALLER_PORT ?? 0);
|
|
1020
1182
|
var sessions = /* @__PURE__ */ new Map();
|
|
1021
1183
|
var OAUTH_BASE_URLS = {
|
|
@@ -1164,23 +1326,25 @@ async function install(body) {
|
|
|
1164
1326
|
cleanupSessions();
|
|
1165
1327
|
const session = body.sessionId ? sessions.get(body.sessionId) : void 0;
|
|
1166
1328
|
const clientIds = body.clientIds ?? [];
|
|
1167
|
-
if (!session) return { ok: false, output:
|
|
1168
|
-
if (!clientIds.length) return { ok: false, output:
|
|
1329
|
+
if (!session) return { ok: false, output: MESSAGES.en.server.loginExpired };
|
|
1330
|
+
if (!clientIds.length) return { ok: false, output: MESSAGES.en.server.selectAtLeastOne };
|
|
1331
|
+
const locale = session.region === "fr" ? "fr" : "en";
|
|
1332
|
+
const s = MESSAGES[locale].server;
|
|
1169
1333
|
const detected = await detectClients();
|
|
1170
1334
|
const selected = detected.filter((client) => clientIds.includes(client.id));
|
|
1171
|
-
if (!selected.length) return { ok: false, output:
|
|
1335
|
+
if (!selected.length) return { ok: false, output: s.noSelectedDetected };
|
|
1172
1336
|
const includeWrite = body.includeWrite !== false;
|
|
1173
1337
|
const telemetryEnabled = body.telemetryEnabled !== false;
|
|
1174
1338
|
const results = [];
|
|
1175
1339
|
for (const client of selected) results.push(await installInto(client, session, includeWrite, telemetryEnabled));
|
|
1176
1340
|
const output = [
|
|
1177
|
-
|
|
1178
|
-
|
|
1341
|
+
s.loggedInBackend(session.region),
|
|
1342
|
+
s.settingsLine(includeWrite, telemetryEnabled),
|
|
1179
1343
|
"",
|
|
1180
|
-
|
|
1344
|
+
s.installSummaryHeader,
|
|
1181
1345
|
...results.map((result) => `${result.ok ? "OK" : "ERROR"} ${result.label}: ${result.message}`),
|
|
1182
1346
|
"",
|
|
1183
|
-
|
|
1347
|
+
s.restartClients
|
|
1184
1348
|
].join("\n");
|
|
1185
1349
|
return { ok: results.some((result) => result.ok), output: sanitizeOutput(output), results };
|
|
1186
1350
|
}
|
|
@@ -1195,6 +1359,8 @@ async function streamInstall(url, res, onDone) {
|
|
|
1195
1359
|
const clientIds = (url.searchParams.get("clients") ?? "").split(",").filter(Boolean);
|
|
1196
1360
|
const includeWrite = url.searchParams.get("write") !== "0";
|
|
1197
1361
|
const telemetryEnabled = url.searchParams.get("telemetry") !== "0";
|
|
1362
|
+
const locale = parseLocale(url.searchParams.get("locale"));
|
|
1363
|
+
const s = MESSAGES[locale].server;
|
|
1198
1364
|
const emit = (level, message) => sendSse(res, { level, message: sanitizeOutput(message) });
|
|
1199
1365
|
const abort = (msg) => {
|
|
1200
1366
|
emit("done", msg);
|
|
@@ -1206,29 +1372,29 @@ async function streamInstall(url, res, onDone) {
|
|
|
1206
1372
|
onDone?.();
|
|
1207
1373
|
};
|
|
1208
1374
|
if (!session) {
|
|
1209
|
-
emit("error",
|
|
1210
|
-
abort(
|
|
1375
|
+
emit("error", s.loginExpired);
|
|
1376
|
+
abort(s.installStopped);
|
|
1211
1377
|
return;
|
|
1212
1378
|
}
|
|
1213
1379
|
if (!clientIds.length) {
|
|
1214
|
-
emit("error",
|
|
1215
|
-
abort(
|
|
1380
|
+
emit("error", s.selectAtLeastOne);
|
|
1381
|
+
abort(s.installStopped);
|
|
1216
1382
|
return;
|
|
1217
1383
|
}
|
|
1218
|
-
emit("info",
|
|
1219
|
-
emit("info",
|
|
1220
|
-
emit("info",
|
|
1384
|
+
emit("info", s.connectedTo(session.accountLabel));
|
|
1385
|
+
emit("info", s.toolFlags(includeWrite, telemetryEnabled));
|
|
1386
|
+
emit("info", s.refreshingDetection);
|
|
1221
1387
|
const detected = await detectClients();
|
|
1222
1388
|
const selected = detected.filter((client) => clientIds.includes(client.id));
|
|
1223
1389
|
const selectedHasOnlyManualSetup = selected.length > 0 && selected.every(isManualSetupClient);
|
|
1224
1390
|
if (!selected.length) {
|
|
1225
|
-
emit("error",
|
|
1226
|
-
abort(
|
|
1391
|
+
emit("error", s.noSelectedDetected);
|
|
1392
|
+
abort(s.installStopped);
|
|
1227
1393
|
return;
|
|
1228
1394
|
}
|
|
1229
1395
|
let okCount = 0;
|
|
1230
1396
|
for (const client of selected) {
|
|
1231
|
-
emit("active", isManualSetupClient(client) ?
|
|
1397
|
+
emit("active", isManualSetupClient(client) ? s.preparingManual(client.label) : s.installing(client.label));
|
|
1232
1398
|
const result = await installInto(client, session, includeWrite, telemetryEnabled);
|
|
1233
1399
|
if (result.ok) {
|
|
1234
1400
|
okCount += 1;
|
|
@@ -1237,8 +1403,8 @@ async function streamInstall(url, res, onDone) {
|
|
|
1237
1403
|
emit("error", `${result.label}: ${result.message}`);
|
|
1238
1404
|
}
|
|
1239
1405
|
}
|
|
1240
|
-
const summary = selectedHasOnlyManualSetup ?
|
|
1241
|
-
const closing = selectedHasOnlyManualSetup ?
|
|
1406
|
+
const summary = selectedHasOnlyManualSetup ? s.manualReady : s.installedSummary(okCount, selected.length);
|
|
1407
|
+
const closing = selectedHasOnlyManualSetup ? s.followManual : s.restartClients;
|
|
1242
1408
|
emit(okCount > 0 ? "success" : "error", summary);
|
|
1243
1409
|
if (okCount > 0) {
|
|
1244
1410
|
finish(closing);
|
|
@@ -1253,6 +1419,8 @@ async function streamUninstall(url, res, onDone) {
|
|
|
1253
1419
|
connection: "keep-alive"
|
|
1254
1420
|
});
|
|
1255
1421
|
const clientIds = (url.searchParams.get("clients") ?? "").split(",").filter(Boolean);
|
|
1422
|
+
const locale = parseLocale(url.searchParams.get("locale"));
|
|
1423
|
+
const s = MESSAGES[locale].server;
|
|
1256
1424
|
const emit = (level, message) => sendSse(res, { level, message });
|
|
1257
1425
|
const abort = (msg) => {
|
|
1258
1426
|
emit("done", msg);
|
|
@@ -1264,20 +1432,20 @@ async function streamUninstall(url, res, onDone) {
|
|
|
1264
1432
|
onDone?.();
|
|
1265
1433
|
};
|
|
1266
1434
|
if (!clientIds.length) {
|
|
1267
|
-
emit("error",
|
|
1268
|
-
abort(
|
|
1435
|
+
emit("error", s.selectAtLeastOne);
|
|
1436
|
+
abort(s.uninstallStopped);
|
|
1269
1437
|
return;
|
|
1270
1438
|
}
|
|
1271
1439
|
const detected = await detectClients();
|
|
1272
1440
|
const selected = detected.filter((c) => clientIds.includes(c.id));
|
|
1273
1441
|
if (!selected.length) {
|
|
1274
|
-
emit("error",
|
|
1275
|
-
abort(
|
|
1442
|
+
emit("error", s.noSelectedDetected);
|
|
1443
|
+
abort(s.uninstallStopped);
|
|
1276
1444
|
return;
|
|
1277
1445
|
}
|
|
1278
1446
|
let okCount = 0;
|
|
1279
1447
|
for (const client of selected) {
|
|
1280
|
-
emit("active",
|
|
1448
|
+
emit("active", s.removing(client.label));
|
|
1281
1449
|
let res2;
|
|
1282
1450
|
if (client.id === "claude-code") {
|
|
1283
1451
|
res2 = await uninstallFromClaudeCode();
|
|
@@ -1295,16 +1463,17 @@ async function streamUninstall(url, res, onDone) {
|
|
|
1295
1463
|
emit("error", `${client.label}: ${res2.message}`);
|
|
1296
1464
|
}
|
|
1297
1465
|
}
|
|
1298
|
-
emit(okCount > 0 ? "success" : "error",
|
|
1299
|
-
finish(
|
|
1466
|
+
emit(okCount > 0 ? "success" : "error", s.removedSummary(okCount, selected.length));
|
|
1467
|
+
finish(s.completeRemoval);
|
|
1300
1468
|
}
|
|
1301
|
-
function pageUninstallHtml() {
|
|
1469
|
+
function pageUninstallHtml(locale = "en") {
|
|
1470
|
+
const ui = MESSAGES[locale].uninstaller;
|
|
1302
1471
|
return `<!doctype html>
|
|
1303
|
-
<html lang="
|
|
1472
|
+
<html lang="${locale}">
|
|
1304
1473
|
<head>
|
|
1305
1474
|
<meta charset="utf-8" />
|
|
1306
1475
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
1307
|
-
<title
|
|
1476
|
+
<title>${ui.docTitle}</title>
|
|
1308
1477
|
<style>
|
|
1309
1478
|
:root { color-scheme: light; --bg:#fff; --card:#fff; --strong:#1d2228; --muted:#9aa0ab; --line:#e7e9ee; --accent:#0d0f0e; --danger:#d14343; --ok:#16a34a; --warn:#b06a00; }
|
|
1310
1479
|
* { box-sizing:border-box; }
|
|
@@ -1356,8 +1525,8 @@ function pageUninstallHtml() {
|
|
|
1356
1525
|
<main>
|
|
1357
1526
|
<div class="steps"><div class="dot active" id="dot-1"></div><div class="dot" id="dot-2"></div></div>
|
|
1358
1527
|
<div class="card">
|
|
1359
|
-
<h1 id="title"
|
|
1360
|
-
<p class="sub" id="sub"
|
|
1528
|
+
<h1 id="title">${ui.steps[1].title}</h1>
|
|
1529
|
+
<p class="sub" id="sub">${ui.steps[1].sub}</p>
|
|
1361
1530
|
|
|
1362
1531
|
<section id="step-1">
|
|
1363
1532
|
<div class="spinner" id="spinner"></div>
|
|
@@ -1375,19 +1544,18 @@ function pageUninstallHtml() {
|
|
|
1375
1544
|
</section>
|
|
1376
1545
|
|
|
1377
1546
|
<div class="actions">
|
|
1378
|
-
<button id="back" class="ghost hidden"
|
|
1379
|
-
<button id="refresh"
|
|
1380
|
-
<button class="danger" id="next"
|
|
1547
|
+
<button id="back" class="ghost hidden">${ui.btnBack}</button>
|
|
1548
|
+
<button id="refresh">${ui.btnRefresh}</button>
|
|
1549
|
+
<button class="danger" id="next">${ui.btnRemove}</button>
|
|
1381
1550
|
</div>
|
|
1382
1551
|
</div>
|
|
1383
1552
|
<p class="version">v${VERSION}</p>
|
|
1384
1553
|
</main>
|
|
1385
1554
|
<script>
|
|
1555
|
+
const LOCALE = "${locale}";
|
|
1556
|
+
const T = ${JSON.stringify(ui)};
|
|
1386
1557
|
const $ = (id) => document.getElementById(id);
|
|
1387
|
-
const STEPS =
|
|
1388
|
-
1: { title: "Remove Leadbay MCP", sub: "Select the agents to remove Leadbay MCP from." },
|
|
1389
|
-
2: { title: "Removing", sub: "Keep this window open until it's done." },
|
|
1390
|
-
};
|
|
1558
|
+
const STEPS = T.steps;
|
|
1391
1559
|
const CHECK = "M20 33 l8 8 l16 -18";
|
|
1392
1560
|
const CROSS = "M22 22 l20 20 M42 22 l-20 20";
|
|
1393
1561
|
let step = 1;
|
|
@@ -1399,18 +1567,18 @@ function pageUninstallHtml() {
|
|
|
1399
1567
|
function showResult(ok, msg) {
|
|
1400
1568
|
$("sub").classList.add("hidden");
|
|
1401
1569
|
$("result-msg").textContent = msg;
|
|
1402
|
-
$("result-note").textContent = ok ?
|
|
1570
|
+
$("result-note").textContent = ok ? T.closeWindow : "";
|
|
1403
1571
|
$("ring-mark").setAttribute("d", ok ? CHECK : CROSS);
|
|
1404
1572
|
const ring = $("ring"); ring.classList.remove("ok", "err"); void ring.getBoundingClientRect();
|
|
1405
1573
|
ring.classList.add(ok ? "ok" : "err");
|
|
1406
1574
|
$("result").classList.toggle("err", !ok);
|
|
1407
1575
|
$("result").classList.remove("hidden");
|
|
1408
|
-
$("title").textContent = ok ?
|
|
1576
|
+
$("title").textContent = ok ? T.allSet : T.somethingWrong;
|
|
1409
1577
|
["next", "back", "refresh"].forEach((id) => $(id).classList.add("hidden"));
|
|
1410
1578
|
}
|
|
1411
|
-
function renderAgents() { $("spinner").classList.add("hidden"); const root = $("agents"); if (!clients.length) { root.innerHTML = '<div class="sub">
|
|
1412
|
-
async function refresh() { $("spinner").classList.remove("hidden"); $("agents").innerHTML = ""; const res = await fetch("/api/status"); const data = await res.json(); clients = (data.clients || []).filter((c) => c.configured); renderAgents(); if (!clients.length) say(
|
|
1413
|
-
async function doUninstall() { const selected = [...document.querySelectorAll("[data-client]:checked")].map((el) => el.dataset.client); if (!selected.length) return say(
|
|
1579
|
+
function renderAgents() { $("spinner").classList.add("hidden"); const root = $("agents"); if (!clients.length) { root.innerHTML = '<div class="sub">' + esc(T.noInstallDetected) + '</div>'; return; } root.innerHTML = clients.map((c) => '<label class="agent"><input type="checkbox" data-client="' + esc(c.id) + '" checked /><span><strong>' + esc(c.label) + '</strong><span class="detail">' + esc(c.detail) + '</span></span></label>').join(""); }
|
|
1580
|
+
async function refresh() { $("spinner").classList.remove("hidden"); $("agents").innerHTML = ""; const res = await fetch("/api/status"); const data = await res.json(); clients = (data.clients || []).filter((c) => c.configured); renderAgents(); if (!clients.length) say(T.noInstallDetected); }
|
|
1581
|
+
async function doUninstall() { const selected = [...document.querySelectorAll("[data-client]:checked")].map((el) => el.dataset.client); if (!selected.length) return say(T.selectAtLeastOne, true); setStep(2); let okCount = 0, lastError = ""; const params = new URLSearchParams({ clients: selected.join(","), locale: LOCALE }); const events = new EventSource("/api/uninstall-stream?" + params.toString()); events.onmessage = (event) => { const data = JSON.parse(event.data); if (data.level === "error") lastError = data.message; if (data.level === "success") okCount += 1; if (data.level === "done") { events.close(); const ok = okCount > 0 && !lastError; showResult(ok, ok ? T.successRemoved : (lastError || T.noneRemoved)); } else { say(data.message, data.level === "error"); } }; events.onerror = () => { events.close(); showResult(false, T.streamDisconnected); }; }
|
|
1414
1582
|
$("back").addEventListener("click", () => setStep(1));
|
|
1415
1583
|
$("refresh").addEventListener("click", refresh);
|
|
1416
1584
|
$("next").addEventListener("click", doUninstall);
|
|
@@ -1419,13 +1587,14 @@ function pageUninstallHtml() {
|
|
|
1419
1587
|
</body>
|
|
1420
1588
|
</html>`;
|
|
1421
1589
|
}
|
|
1422
|
-
function pageHtml() {
|
|
1590
|
+
function pageHtml(locale = "en") {
|
|
1591
|
+
const ui = MESSAGES[locale].installer;
|
|
1423
1592
|
return `<!doctype html>
|
|
1424
|
-
<html lang="
|
|
1593
|
+
<html lang="${locale}">
|
|
1425
1594
|
<head>
|
|
1426
1595
|
<meta charset="utf-8" />
|
|
1427
1596
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
1428
|
-
<title
|
|
1597
|
+
<title>${ui.docTitle}</title>
|
|
1429
1598
|
<style>
|
|
1430
1599
|
:root { color-scheme: light; --bg:#fff; --card:#fff; --strong:#1d2228; --muted:#9aa0ab; --line:#e7e9ee; --accent:#0d0f0e; --cancel-line:#f0c8b8; --danger:#d14343; --ok:#16a34a; --warn:#b06a00; }
|
|
1431
1600
|
* { box-sizing:border-box; }
|
|
@@ -1481,8 +1650,8 @@ function pageHtml() {
|
|
|
1481
1650
|
<main>
|
|
1482
1651
|
<div class="steps"><div class="dot active" id="dot-1"></div><div class="dot" id="dot-2"></div><div class="dot" id="dot-3"></div></div>
|
|
1483
1652
|
<div class="card">
|
|
1484
|
-
<h1 id="title"
|
|
1485
|
-
<p class="sub" id="sub"
|
|
1653
|
+
<h1 id="title">${ui.steps[1].title}</h1>
|
|
1654
|
+
<p class="sub" id="sub">${ui.steps[1].sub}</p>
|
|
1486
1655
|
|
|
1487
1656
|
<section id="step-2" class="hidden">
|
|
1488
1657
|
<div class="spinner" id="spinner"></div>
|
|
@@ -1500,20 +1669,18 @@ function pageHtml() {
|
|
|
1500
1669
|
</section>
|
|
1501
1670
|
|
|
1502
1671
|
<div class="actions">
|
|
1503
|
-
<button id="back" class="cancel hidden"
|
|
1504
|
-
<button id="refresh" class="ghost hidden"
|
|
1505
|
-
<button class="primary" id="next"
|
|
1672
|
+
<button id="back" class="cancel hidden">${ui.btnBack}</button>
|
|
1673
|
+
<button id="refresh" class="ghost hidden">${ui.btnRefresh}</button>
|
|
1674
|
+
<button class="primary" id="next">${ui.btnSignIn}</button>
|
|
1506
1675
|
</div>
|
|
1507
1676
|
</div>
|
|
1508
1677
|
<p class="version">v${VERSION}</p>
|
|
1509
1678
|
</main>
|
|
1510
1679
|
<script>
|
|
1680
|
+
const LOCALE = "${locale}";
|
|
1681
|
+
const T = ${JSON.stringify(ui)};
|
|
1511
1682
|
const $ = (id) => document.getElementById(id);
|
|
1512
|
-
const STEPS =
|
|
1513
|
-
1: { title: "Connect Leadbay", sub: "Sign in to install Leadbay across your AI agents." },
|
|
1514
|
-
2: { title: "Choose your agents", sub: "Pick where to install Leadbay." },
|
|
1515
|
-
3: { title: "Installing", sub: "Keep this window open until it's done." },
|
|
1516
|
-
};
|
|
1683
|
+
const STEPS = T.steps;
|
|
1517
1684
|
const CHECK = "M20 33 l8 8 l16 -18";
|
|
1518
1685
|
const CROSS = "M22 22 l20 20 M42 22 l-20 20";
|
|
1519
1686
|
let step = 1;
|
|
@@ -1531,30 +1698,30 @@ function pageHtml() {
|
|
|
1531
1698
|
$("back").classList.toggle("hidden", step !== 2);
|
|
1532
1699
|
$("refresh").classList.toggle("hidden", step !== 2);
|
|
1533
1700
|
$("next").classList.toggle("hidden", step === 3);
|
|
1534
|
-
$("next").textContent = step === 2 ?
|
|
1701
|
+
$("next").textContent = step === 2 ? T.btnInstall : T.btnSignIn;
|
|
1535
1702
|
}
|
|
1536
1703
|
// Final completion state: animated green check / red cross + message.
|
|
1537
1704
|
function showResult(ok, msg) {
|
|
1538
1705
|
$("sub").classList.add("hidden");
|
|
1539
1706
|
$("result-msg").textContent = msg;
|
|
1540
|
-
$("result-note").textContent = ok ?
|
|
1707
|
+
$("result-note").textContent = ok ? T.closeWindow : "";
|
|
1541
1708
|
$("ring-mark").setAttribute("d", ok ? CHECK : CROSS);
|
|
1542
1709
|
const ring = $("ring"); ring.classList.remove("ok", "err"); void ring.getBoundingClientRect();
|
|
1543
1710
|
ring.classList.add(ok ? "ok" : "err");
|
|
1544
1711
|
$("result").classList.toggle("err", !ok);
|
|
1545
1712
|
$("result").classList.remove("hidden");
|
|
1546
|
-
$("title").textContent = ok ?
|
|
1713
|
+
$("title").textContent = ok ? T.allSet : T.somethingWrong;
|
|
1547
1714
|
["next", "back", "refresh"].forEach((id) => $(id).classList.add("hidden"));
|
|
1548
1715
|
}
|
|
1549
|
-
function renderAgents() { $("spinner").classList.add("hidden"); const root = $("agents"); if (!clients.length) { root.innerHTML = '<div class="sub">
|
|
1550
|
-
async function refresh() { $("spinner").classList.remove("hidden"); $("agents").innerHTML = ""; const res = await fetch("/api/status"); const data = await res.json(); clients = data.clients || []; renderAgents(); if (!clients.length) say(
|
|
1551
|
-
async function doLogin() { $("next").disabled = true; say(
|
|
1716
|
+
function renderAgents() { $("spinner").classList.add("hidden"); const root = $("agents"); if (!clients.length) { root.innerHTML = '<div class="sub">' + esc(T.noClientsDetected) + '</div>'; return; } root.innerHTML = clients.map((client) => { const manual = client.id === "chatgpt-desktop"; const badgeText = manual ? T.badgeManual : client.configured ? T.badgeUpdate : T.badgeInstall; const badgeClass = manual ? "badge-update" : client.configured ? "badge-update" : "badge-install"; return '<label class="agent"><input type="checkbox" data-client="' + esc(client.id) + '" checked /><span><strong>' + esc(client.label) + ' <span class="badge-pill ' + badgeClass + '">' + esc(badgeText) + '</span></strong><span class="detail">' + esc(client.detail) + '</span></span></label>'; }).join(""); }
|
|
1717
|
+
async function refresh() { $("spinner").classList.remove("hidden"); $("agents").innerHTML = ""; const res = await fetch("/api/status"); const data = await res.json(); clients = data.clients || []; renderAgents(); if (!clients.length) say(T.noAgentsDetected); }
|
|
1718
|
+
async function doLogin() { $("next").disabled = true; say(T.openingSignIn); try { const res = await fetch("/api/oauth-login", { method:"POST" }); const data = await res.json(); if (!data.ok) return say(data.error || T.oauthFailed, true); sessionId = data.sessionId; setStep(2); await refresh(); } finally { $("next").disabled = false; } }
|
|
1552
1719
|
async function install() {
|
|
1553
1720
|
const selected = [...document.querySelectorAll("[data-client]:checked")].map((el) => el.dataset.client);
|
|
1554
|
-
if (!selected.length) return say(
|
|
1721
|
+
if (!selected.length) return say(T.selectAtLeastOne, true);
|
|
1555
1722
|
setStep(3);
|
|
1556
1723
|
let okCount = 0, lastError = "";
|
|
1557
|
-
const params = new URLSearchParams({ sessionId, clients: selected.join(","), write: "1", telemetry: "1" });
|
|
1724
|
+
const params = new URLSearchParams({ sessionId, clients: selected.join(","), write: "1", telemetry: "1", locale: LOCALE });
|
|
1558
1725
|
const events = new EventSource("/api/install-stream?" + params.toString());
|
|
1559
1726
|
events.onmessage = (event) => {
|
|
1560
1727
|
const data = JSON.parse(event.data);
|
|
@@ -1563,12 +1730,12 @@ function pageHtml() {
|
|
|
1563
1730
|
if (data.level === "done") {
|
|
1564
1731
|
events.close();
|
|
1565
1732
|
const ok = okCount > 0 && !lastError;
|
|
1566
|
-
showResult(ok, ok ?
|
|
1733
|
+
showResult(ok, ok ? T.successInstalled : (lastError || T.noneInstalled));
|
|
1567
1734
|
} else {
|
|
1568
1735
|
say(data.message, data.level === "error");
|
|
1569
1736
|
}
|
|
1570
1737
|
};
|
|
1571
|
-
events.onerror = () => { events.close(); showResult(false,
|
|
1738
|
+
events.onerror = () => { events.close(); showResult(false, T.streamDisconnected); };
|
|
1572
1739
|
}
|
|
1573
1740
|
$("back").addEventListener("click", () => setStep(1));
|
|
1574
1741
|
$("refresh").addEventListener("click", refresh);
|
|
@@ -1623,7 +1790,7 @@ function makeGuiServer(options, pageContent, extraRoutes, logLabel) {
|
|
|
1623
1790
|
}
|
|
1624
1791
|
try {
|
|
1625
1792
|
if (req.method === "GET" && req.url === "/") {
|
|
1626
|
-
const raw = pageContent();
|
|
1793
|
+
const raw = await pageContent();
|
|
1627
1794
|
res.writeHead(200, { "content-type": "text/html; charset=utf-8", "content-length": Buffer.byteLength(raw) });
|
|
1628
1795
|
res.end(raw);
|
|
1629
1796
|
return;
|
|
@@ -1650,7 +1817,7 @@ function makeGuiServer(options, pageContent, extraRoutes, logLabel) {
|
|
|
1650
1817
|
});
|
|
1651
1818
|
}
|
|
1652
1819
|
function startInstallerGui(options = {}) {
|
|
1653
|
-
return makeGuiServer(options, pageHtml, async (req, res, onDone) => {
|
|
1820
|
+
return makeGuiServer(options, async () => pageHtml(await detectLocale()), async (req, res, onDone) => {
|
|
1654
1821
|
if (req.method === "GET" && req.url === "/api/status") {
|
|
1655
1822
|
sendJson(res, 200, { os: formatInstallOsLabel(), hostedMcpUrl: HOSTED_MCP_URL, clients: await clientsWithConfiguredStatus() });
|
|
1656
1823
|
return true;
|
|
@@ -1671,7 +1838,7 @@ function startInstallerGui(options = {}) {
|
|
|
1671
1838
|
}, "installer");
|
|
1672
1839
|
}
|
|
1673
1840
|
function startUninstallerGui(options = {}) {
|
|
1674
|
-
return makeGuiServer(options, pageUninstallHtml, async (req, res, onDone) => {
|
|
1841
|
+
return makeGuiServer(options, async () => pageUninstallHtml(await detectLocale()), async (req, res, onDone) => {
|
|
1675
1842
|
if (req.method === "GET" && req.url === "/api/status") {
|
|
1676
1843
|
sendJson(res, 200, { os: formatInstallOsLabel(), clients: await clientsWithConfiguredStatus() });
|
|
1677
1844
|
return true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@leadbay/mcp",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.6",
|
|
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",
|