@leadbay/mcp 0.23.5 → 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.
@@ -1027,7 +1027,157 @@ async function oauthLogin(opts) {
1027
1027
  }
1028
1028
 
1029
1029
  // installer/installer-gui.ts
1030
- var VERSION = true ? "0.23.5" : "0.0.0-dev";
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
+ }
1031
1181
  var PORT = Number(process.env.LEADBAY_INSTALLER_PORT ?? 0);
1032
1182
  var sessions = /* @__PURE__ */ new Map();
1033
1183
  var OAUTH_BASE_URLS = {
@@ -1176,23 +1326,25 @@ async function install(body) {
1176
1326
  cleanupSessions();
1177
1327
  const session = body.sessionId ? sessions.get(body.sessionId) : void 0;
1178
1328
  const clientIds = body.clientIds ?? [];
1179
- if (!session) return { ok: false, output: "Login expired. Go back and sign in again." };
1180
- if (!clientIds.length) return { ok: false, output: "Select at least one agent." };
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;
1181
1333
  const detected = await detectClients();
1182
1334
  const selected = detected.filter((client) => clientIds.includes(client.id));
1183
- if (!selected.length) return { ok: false, output: "No selected agents were detected on this machine." };
1335
+ if (!selected.length) return { ok: false, output: s.noSelectedDetected };
1184
1336
  const includeWrite = body.includeWrite !== false;
1185
1337
  const telemetryEnabled = body.telemetryEnabled !== false;
1186
1338
  const results = [];
1187
1339
  for (const client of selected) results.push(await installInto(client, session, includeWrite, telemetryEnabled));
1188
1340
  const output = [
1189
- `Logged in to ${session.region.toUpperCase()} backend.`,
1190
- `Settings: write tools ${includeWrite ? "on" : "off"}, telemetry ${telemetryEnabled ? "on" : "off"}.`,
1341
+ s.loggedInBackend(session.region),
1342
+ s.settingsLine(includeWrite, telemetryEnabled),
1191
1343
  "",
1192
- "Install summary:",
1344
+ s.installSummaryHeader,
1193
1345
  ...results.map((result) => `${result.ok ? "OK" : "ERROR"} ${result.label}: ${result.message}`),
1194
1346
  "",
1195
- "Restart your MCP client(s) to pick up the new server."
1347
+ s.restartClients
1196
1348
  ].join("\n");
1197
1349
  return { ok: results.some((result) => result.ok), output: sanitizeOutput(output), results };
1198
1350
  }
@@ -1207,6 +1359,8 @@ async function streamInstall(url, res, onDone) {
1207
1359
  const clientIds = (url.searchParams.get("clients") ?? "").split(",").filter(Boolean);
1208
1360
  const includeWrite = url.searchParams.get("write") !== "0";
1209
1361
  const telemetryEnabled = url.searchParams.get("telemetry") !== "0";
1362
+ const locale = parseLocale(url.searchParams.get("locale"));
1363
+ const s = MESSAGES[locale].server;
1210
1364
  const emit = (level, message) => sendSse(res, { level, message: sanitizeOutput(message) });
1211
1365
  const abort = (msg) => {
1212
1366
  emit("done", msg);
@@ -1218,29 +1372,29 @@ async function streamInstall(url, res, onDone) {
1218
1372
  onDone?.();
1219
1373
  };
1220
1374
  if (!session) {
1221
- emit("error", "Login expired. Go back and sign in again.");
1222
- abort("Install stopped.");
1375
+ emit("error", s.loginExpired);
1376
+ abort(s.installStopped);
1223
1377
  return;
1224
1378
  }
1225
1379
  if (!clientIds.length) {
1226
- emit("error", "Select at least one agent.");
1227
- abort("Install stopped.");
1380
+ emit("error", s.selectAtLeastOne);
1381
+ abort(s.installStopped);
1228
1382
  return;
1229
1383
  }
1230
- emit("info", `Connected to ${session.accountLabel}.`);
1231
- emit("info", `Write tools ${includeWrite ? "enabled" : "disabled"}; telemetry ${telemetryEnabled ? "enabled" : "disabled"}.`);
1232
- emit("info", "Refreshing installed-agent detection...");
1384
+ emit("info", s.connectedTo(session.accountLabel));
1385
+ emit("info", s.toolFlags(includeWrite, telemetryEnabled));
1386
+ emit("info", s.refreshingDetection);
1233
1387
  const detected = await detectClients();
1234
1388
  const selected = detected.filter((client) => clientIds.includes(client.id));
1235
1389
  const selectedHasOnlyManualSetup = selected.length > 0 && selected.every(isManualSetupClient);
1236
1390
  if (!selected.length) {
1237
- emit("error", "No selected agents were detected on this machine.");
1238
- abort("Install stopped.");
1391
+ emit("error", s.noSelectedDetected);
1392
+ abort(s.installStopped);
1239
1393
  return;
1240
1394
  }
1241
1395
  let okCount = 0;
1242
1396
  for (const client of selected) {
1243
- emit("active", isManualSetupClient(client) ? `Preparing ${client.label} manual setup...` : `Installing ${client.label}...`);
1397
+ emit("active", isManualSetupClient(client) ? s.preparingManual(client.label) : s.installing(client.label));
1244
1398
  const result = await installInto(client, session, includeWrite, telemetryEnabled);
1245
1399
  if (result.ok) {
1246
1400
  okCount += 1;
@@ -1249,8 +1403,8 @@ async function streamInstall(url, res, onDone) {
1249
1403
  emit("error", `${result.label}: ${result.message}`);
1250
1404
  }
1251
1405
  }
1252
- const summary = selectedHasOnlyManualSetup ? "Manual ChatGPT setup instructions ready." : `${okCount}/${selected.length} agent(s) installed, updated, or prepared.`;
1253
- const closing = selectedHasOnlyManualSetup ? "Follow the manual setup instructions shown above." : "Restart your MCP client(s) to pick up the new server.";
1406
+ const summary = selectedHasOnlyManualSetup ? s.manualReady : s.installedSummary(okCount, selected.length);
1407
+ const closing = selectedHasOnlyManualSetup ? s.followManual : s.restartClients;
1254
1408
  emit(okCount > 0 ? "success" : "error", summary);
1255
1409
  if (okCount > 0) {
1256
1410
  finish(closing);
@@ -1265,6 +1419,8 @@ async function streamUninstall(url, res, onDone) {
1265
1419
  connection: "keep-alive"
1266
1420
  });
1267
1421
  const clientIds = (url.searchParams.get("clients") ?? "").split(",").filter(Boolean);
1422
+ const locale = parseLocale(url.searchParams.get("locale"));
1423
+ const s = MESSAGES[locale].server;
1268
1424
  const emit = (level, message) => sendSse(res, { level, message });
1269
1425
  const abort = (msg) => {
1270
1426
  emit("done", msg);
@@ -1276,20 +1432,20 @@ async function streamUninstall(url, res, onDone) {
1276
1432
  onDone?.();
1277
1433
  };
1278
1434
  if (!clientIds.length) {
1279
- emit("error", "Select at least one agent.");
1280
- abort("Uninstall stopped.");
1435
+ emit("error", s.selectAtLeastOne);
1436
+ abort(s.uninstallStopped);
1281
1437
  return;
1282
1438
  }
1283
1439
  const detected = await detectClients();
1284
1440
  const selected = detected.filter((c) => clientIds.includes(c.id));
1285
1441
  if (!selected.length) {
1286
- emit("error", "No selected agents were detected on this machine.");
1287
- abort("Uninstall stopped.");
1442
+ emit("error", s.noSelectedDetected);
1443
+ abort(s.uninstallStopped);
1288
1444
  return;
1289
1445
  }
1290
1446
  let okCount = 0;
1291
1447
  for (const client of selected) {
1292
- emit("active", `Removing from ${client.label}...`);
1448
+ emit("active", s.removing(client.label));
1293
1449
  let res2;
1294
1450
  if (client.id === "claude-code") {
1295
1451
  res2 = await uninstallFromClaudeCode();
@@ -1307,16 +1463,17 @@ async function streamUninstall(url, res, onDone) {
1307
1463
  emit("error", `${client.label}: ${res2.message}`);
1308
1464
  }
1309
1465
  }
1310
- emit(okCount > 0 ? "success" : "error", `${okCount}/${selected.length} agent(s) removed.`);
1311
- finish("Restart your MCP client(s) to complete the removal.");
1466
+ emit(okCount > 0 ? "success" : "error", s.removedSummary(okCount, selected.length));
1467
+ finish(s.completeRemoval);
1312
1468
  }
1313
- function pageUninstallHtml() {
1469
+ function pageUninstallHtml(locale = "en") {
1470
+ const ui = MESSAGES[locale].uninstaller;
1314
1471
  return `<!doctype html>
1315
- <html lang="en">
1472
+ <html lang="${locale}">
1316
1473
  <head>
1317
1474
  <meta charset="utf-8" />
1318
1475
  <meta name="viewport" content="width=device-width, initial-scale=1" />
1319
- <title>Leadbay MCP uninstaller</title>
1476
+ <title>${ui.docTitle}</title>
1320
1477
  <style>
1321
1478
  :root { color-scheme: light; --bg:#fff; --card:#fff; --strong:#1d2228; --muted:#9aa0ab; --line:#e7e9ee; --accent:#0d0f0e; --danger:#d14343; --ok:#16a34a; --warn:#b06a00; }
1322
1479
  * { box-sizing:border-box; }
@@ -1368,8 +1525,8 @@ function pageUninstallHtml() {
1368
1525
  <main>
1369
1526
  <div class="steps"><div class="dot active" id="dot-1"></div><div class="dot" id="dot-2"></div></div>
1370
1527
  <div class="card">
1371
- <h1 id="title">Remove Leadbay MCP</h1>
1372
- <p class="sub" id="sub">Select the agents to remove Leadbay MCP from.</p>
1528
+ <h1 id="title">${ui.steps[1].title}</h1>
1529
+ <p class="sub" id="sub">${ui.steps[1].sub}</p>
1373
1530
 
1374
1531
  <section id="step-1">
1375
1532
  <div class="spinner" id="spinner"></div>
@@ -1387,19 +1544,18 @@ function pageUninstallHtml() {
1387
1544
  </section>
1388
1545
 
1389
1546
  <div class="actions">
1390
- <button id="back" class="ghost hidden">Back</button>
1391
- <button id="refresh">Refresh</button>
1392
- <button class="danger" id="next">Remove selected</button>
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>
1393
1550
  </div>
1394
1551
  </div>
1395
1552
  <p class="version">v${VERSION}</p>
1396
1553
  </main>
1397
1554
  <script>
1555
+ const LOCALE = "${locale}";
1556
+ const T = ${JSON.stringify(ui)};
1398
1557
  const $ = (id) => document.getElementById(id);
1399
- const STEPS = {
1400
- 1: { title: "Remove Leadbay MCP", sub: "Select the agents to remove Leadbay MCP from." },
1401
- 2: { title: "Removing", sub: "Keep this window open until it's done." },
1402
- };
1558
+ const STEPS = T.steps;
1403
1559
  const CHECK = "M20 33 l8 8 l16 -18";
1404
1560
  const CROSS = "M22 22 l20 20 M42 22 l-20 20";
1405
1561
  let step = 1;
@@ -1411,18 +1567,18 @@ function pageUninstallHtml() {
1411
1567
  function showResult(ok, msg) {
1412
1568
  $("sub").classList.add("hidden");
1413
1569
  $("result-msg").textContent = msg;
1414
- $("result-note").textContent = ok ? "You can close this window." : "";
1570
+ $("result-note").textContent = ok ? T.closeWindow : "";
1415
1571
  $("ring-mark").setAttribute("d", ok ? CHECK : CROSS);
1416
1572
  const ring = $("ring"); ring.classList.remove("ok", "err"); void ring.getBoundingClientRect();
1417
1573
  ring.classList.add(ok ? "ok" : "err");
1418
1574
  $("result").classList.toggle("err", !ok);
1419
1575
  $("result").classList.remove("hidden");
1420
- $("title").textContent = ok ? "All set" : "Something went wrong";
1576
+ $("title").textContent = ok ? T.allSet : T.somethingWrong;
1421
1577
  ["next", "back", "refresh"].forEach((id) => $(id).classList.add("hidden"));
1422
1578
  }
1423
- function renderAgents() { $("spinner").classList.add("hidden"); const root = $("agents"); if (!clients.length) { root.innerHTML = '<div class="sub">No Leadbay MCP installation detected on this machine.</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(""); }
1424
- 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("No Leadbay MCP installation detected on this machine."); }
1425
- async function doUninstall() { const selected = [...document.querySelectorAll("[data-client]:checked")].map((el) => el.dataset.client); if (!selected.length) return say("Select at least one agent.", true); setStep(2); let okCount = 0, lastError = ""; const params = new URLSearchParams({ clients: selected.join(",") }); 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 ? "MCP successfully removed" : (lastError || "No agents were removed.")); } else { say(data.message, data.level === "error"); } }; events.onerror = () => { events.close(); showResult(false, "Uninstall stream disconnected."); }; }
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); }; }
1426
1582
  $("back").addEventListener("click", () => setStep(1));
1427
1583
  $("refresh").addEventListener("click", refresh);
1428
1584
  $("next").addEventListener("click", doUninstall);
@@ -1431,13 +1587,14 @@ function pageUninstallHtml() {
1431
1587
  </body>
1432
1588
  </html>`;
1433
1589
  }
1434
- function pageHtml() {
1590
+ function pageHtml(locale = "en") {
1591
+ const ui = MESSAGES[locale].installer;
1435
1592
  return `<!doctype html>
1436
- <html lang="en">
1593
+ <html lang="${locale}">
1437
1594
  <head>
1438
1595
  <meta charset="utf-8" />
1439
1596
  <meta name="viewport" content="width=device-width, initial-scale=1" />
1440
- <title>Leadbay MCP installer</title>
1597
+ <title>${ui.docTitle}</title>
1441
1598
  <style>
1442
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; }
1443
1600
  * { box-sizing:border-box; }
@@ -1493,8 +1650,8 @@ function pageHtml() {
1493
1650
  <main>
1494
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>
1495
1652
  <div class="card">
1496
- <h1 id="title">Connect Leadbay</h1>
1497
- <p class="sub" id="sub">Sign in to install Leadbay across your AI agents.</p>
1653
+ <h1 id="title">${ui.steps[1].title}</h1>
1654
+ <p class="sub" id="sub">${ui.steps[1].sub}</p>
1498
1655
 
1499
1656
  <section id="step-2" class="hidden">
1500
1657
  <div class="spinner" id="spinner"></div>
@@ -1512,20 +1669,18 @@ function pageHtml() {
1512
1669
  </section>
1513
1670
 
1514
1671
  <div class="actions">
1515
- <button id="back" class="cancel hidden">Back</button>
1516
- <button id="refresh" class="ghost hidden">Refresh</button>
1517
- <button class="primary" id="next">Sign in with Leadbay</button>
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>
1518
1675
  </div>
1519
1676
  </div>
1520
1677
  <p class="version">v${VERSION}</p>
1521
1678
  </main>
1522
1679
  <script>
1680
+ const LOCALE = "${locale}";
1681
+ const T = ${JSON.stringify(ui)};
1523
1682
  const $ = (id) => document.getElementById(id);
1524
- const STEPS = {
1525
- 1: { title: "Connect Leadbay", sub: "Sign in to install Leadbay across your AI agents." },
1526
- 2: { title: "Choose your agents", sub: "Pick where to install Leadbay." },
1527
- 3: { title: "Installing", sub: "Keep this window open until it's done." },
1528
- };
1683
+ const STEPS = T.steps;
1529
1684
  const CHECK = "M20 33 l8 8 l16 -18";
1530
1685
  const CROSS = "M22 22 l20 20 M42 22 l-20 20";
1531
1686
  let step = 1;
@@ -1543,30 +1698,30 @@ function pageHtml() {
1543
1698
  $("back").classList.toggle("hidden", step !== 2);
1544
1699
  $("refresh").classList.toggle("hidden", step !== 2);
1545
1700
  $("next").classList.toggle("hidden", step === 3);
1546
- $("next").textContent = step === 2 ? "Install" : "Sign in with Leadbay";
1701
+ $("next").textContent = step === 2 ? T.btnInstall : T.btnSignIn;
1547
1702
  }
1548
1703
  // Final completion state: animated green check / red cross + message.
1549
1704
  function showResult(ok, msg) {
1550
1705
  $("sub").classList.add("hidden");
1551
1706
  $("result-msg").textContent = msg;
1552
- $("result-note").textContent = ok ? "You can close this window." : "";
1707
+ $("result-note").textContent = ok ? T.closeWindow : "";
1553
1708
  $("ring-mark").setAttribute("d", ok ? CHECK : CROSS);
1554
1709
  const ring = $("ring"); ring.classList.remove("ok", "err"); void ring.getBoundingClientRect();
1555
1710
  ring.classList.add(ok ? "ok" : "err");
1556
1711
  $("result").classList.toggle("err", !ok);
1557
1712
  $("result").classList.remove("hidden");
1558
- $("title").textContent = ok ? "All set" : "Something went wrong";
1713
+ $("title").textContent = ok ? T.allSet : T.somethingWrong;
1559
1714
  ["next", "back", "refresh"].forEach((id) => $(id).classList.add("hidden"));
1560
1715
  }
1561
- function renderAgents() { $("spinner").classList.add("hidden"); const root = $("agents"); if (!clients.length) { root.innerHTML = '<div class="sub">No supported MCP client detected on this machine.</div>'; return; } root.innerHTML = clients.map((client) => { const manual = client.id === "chatgpt-desktop"; const badgeText = manual ? "manual" : client.configured ? "update" : "install"; 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 + '">' + badgeText + '</span></strong><span class="detail">' + esc(client.detail) + '</span></span></label>'; }).join(""); }
1562
- 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("No supported agents detected."); }
1563
- async function doLogin() { $("next").disabled = true; say("Opening Leadbay sign-in in your browser..."); try { const res = await fetch("/api/oauth-login", { method:"POST" }); const data = await res.json(); if (!data.ok) return say(data.error || "OAuth login failed.", true); sessionId = data.sessionId; setStep(2); await refresh(); } finally { $("next").disabled = false; } }
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; } }
1564
1719
  async function install() {
1565
1720
  const selected = [...document.querySelectorAll("[data-client]:checked")].map((el) => el.dataset.client);
1566
- if (!selected.length) return say("Select at least one agent.", true);
1721
+ if (!selected.length) return say(T.selectAtLeastOne, true);
1567
1722
  setStep(3);
1568
1723
  let okCount = 0, lastError = "";
1569
- 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 });
1570
1725
  const events = new EventSource("/api/install-stream?" + params.toString());
1571
1726
  events.onmessage = (event) => {
1572
1727
  const data = JSON.parse(event.data);
@@ -1575,12 +1730,12 @@ function pageHtml() {
1575
1730
  if (data.level === "done") {
1576
1731
  events.close();
1577
1732
  const ok = okCount > 0 && !lastError;
1578
- showResult(ok, ok ? "MCP successfully installed" : (lastError || "No agents were installed."));
1733
+ showResult(ok, ok ? T.successInstalled : (lastError || T.noneInstalled));
1579
1734
  } else {
1580
1735
  say(data.message, data.level === "error");
1581
1736
  }
1582
1737
  };
1583
- events.onerror = () => { events.close(); showResult(false, "Install stream disconnected."); };
1738
+ events.onerror = () => { events.close(); showResult(false, T.streamDisconnected); };
1584
1739
  }
1585
1740
  $("back").addEventListener("click", () => setStep(1));
1586
1741
  $("refresh").addEventListener("click", refresh);
@@ -1635,7 +1790,7 @@ function makeGuiServer(options, pageContent, extraRoutes, logLabel) {
1635
1790
  }
1636
1791
  try {
1637
1792
  if (req.method === "GET" && req.url === "/") {
1638
- const raw = pageContent();
1793
+ const raw = await pageContent();
1639
1794
  res.writeHead(200, { "content-type": "text/html; charset=utf-8", "content-length": Buffer.byteLength(raw) });
1640
1795
  res.end(raw);
1641
1796
  return;
@@ -1662,7 +1817,7 @@ function makeGuiServer(options, pageContent, extraRoutes, logLabel) {
1662
1817
  });
1663
1818
  }
1664
1819
  function startInstallerGui(options = {}) {
1665
- return makeGuiServer(options, pageHtml, async (req, res, onDone) => {
1820
+ return makeGuiServer(options, async () => pageHtml(await detectLocale()), async (req, res, onDone) => {
1666
1821
  if (req.method === "GET" && req.url === "/api/status") {
1667
1822
  sendJson(res, 200, { os: formatInstallOsLabel(), hostedMcpUrl: HOSTED_MCP_URL, clients: await clientsWithConfiguredStatus() });
1668
1823
  return true;
@@ -1683,7 +1838,7 @@ function startInstallerGui(options = {}) {
1683
1838
  }, "installer");
1684
1839
  }
1685
1840
  function startUninstallerGui(options = {}) {
1686
- return makeGuiServer(options, pageUninstallHtml, async (req, res, onDone) => {
1841
+ return makeGuiServer(options, async () => pageUninstallHtml(await detectLocale()), async (req, res, onDone) => {
1687
1842
  if (req.method === "GET" && req.url === "/api/status") {
1688
1843
  sendJson(res, 200, { os: formatInstallOsLabel(), clients: await clientsWithConfiguredStatus() });
1689
1844
  return true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leadbay/mcp",
3
- "version": "0.23.5",
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",