@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
|
@@ -399,7 +399,7 @@ var init_install_dxt = __esm({
|
|
|
399
399
|
|
|
400
400
|
// installer/install-shared.ts
|
|
401
401
|
import { spawn as spawn2 } from "child_process";
|
|
402
|
-
import { existsSync, readFileSync } from "fs";
|
|
402
|
+
import { existsSync, readFileSync, readdirSync } from "fs";
|
|
403
403
|
import { join } from "path";
|
|
404
404
|
import { homedir } from "os";
|
|
405
405
|
function formatInstallOsLabel(platform = process.platform, arch = process.arch) {
|
|
@@ -455,6 +455,15 @@ async function windowsStoreAppInstalled(packageName, appName) {
|
|
|
455
455
|
child.on("error", () => resolve(false));
|
|
456
456
|
});
|
|
457
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
|
+
}
|
|
458
467
|
async function isClaudeDesktopInstalled(home) {
|
|
459
468
|
if (process.platform === "darwin") {
|
|
460
469
|
return existsSync("/Applications/Claude.app") || existsSync(home + "/Applications/Claude.app");
|
|
@@ -463,12 +472,15 @@ async function isClaudeDesktopInstalled(home) {
|
|
|
463
472
|
const local = process.env.LOCALAPPDATA ?? home + "/AppData/Local";
|
|
464
473
|
const programFiles = process.env.ProgramFiles;
|
|
465
474
|
const programFilesX86 = process.env["ProgramFiles(x86)"];
|
|
466
|
-
|
|
475
|
+
const exeInstalled = [
|
|
467
476
|
local + "/Programs/Claude/Claude.exe",
|
|
468
477
|
local + "/Claude/Claude.exe",
|
|
469
478
|
programFiles ? programFiles + "/Claude/Claude.exe" : null,
|
|
470
479
|
programFilesX86 ? programFilesX86 + "/Claude/Claude.exe" : null
|
|
471
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");
|
|
472
484
|
}
|
|
473
485
|
const desktopBin = await findOnPath("claude-desktop");
|
|
474
486
|
if (desktopBin) return true;
|
|
@@ -550,7 +562,7 @@ import { createHash, randomBytes } from "crypto";
|
|
|
550
562
|
import { createServer } from "http";
|
|
551
563
|
import { request as httpsRequestRaw } from "https";
|
|
552
564
|
import { spawn as spawn3 } from "child_process";
|
|
553
|
-
import { readdirSync, existsSync as existsSync2 } from "fs";
|
|
565
|
+
import { readdirSync as readdirSync2, existsSync as existsSync2 } from "fs";
|
|
554
566
|
async function inferRegionViaStargate(opts) {
|
|
555
567
|
const url = STARGATE_URLS[opts.staging ? "staging" : "prod"];
|
|
556
568
|
const res = await httpsCall("GET", url, { Accept: "application/json" });
|
|
@@ -853,7 +865,7 @@ function browserLaunchEnv(debug) {
|
|
|
853
865
|
}
|
|
854
866
|
if (!env.WAYLAND_DISPLAY && runtimeDir) {
|
|
855
867
|
try {
|
|
856
|
-
const sock =
|
|
868
|
+
const sock = readdirSync2(runtimeDir).find((f) => /^wayland-\d+$/.test(f));
|
|
857
869
|
if (sock) {
|
|
858
870
|
env.WAYLAND_DISPLAY = sock;
|
|
859
871
|
debug?.(`browserLaunchEnv: injected WAYLAND_DISPLAY=${sock}`);
|
|
@@ -870,7 +882,7 @@ function browserLaunchEnv(debug) {
|
|
|
870
882
|
}
|
|
871
883
|
if (!env.DISPLAY) {
|
|
872
884
|
try {
|
|
873
|
-
const x =
|
|
885
|
+
const x = readdirSync2("/tmp/.X11-unix").map((f) => f.match(/^X(\d+)$/)?.[1]).filter((n) => !!n).sort((a, b) => Number(a) - Number(b))[0];
|
|
874
886
|
env.DISPLAY = x !== void 0 ? `:${x}` : ":0";
|
|
875
887
|
} catch {
|
|
876
888
|
env.DISPLAY = ":0";
|
|
@@ -881,7 +893,7 @@ function browserLaunchEnv(debug) {
|
|
|
881
893
|
try {
|
|
882
894
|
let xauth;
|
|
883
895
|
if (runtimeDir) {
|
|
884
|
-
const cookie =
|
|
896
|
+
const cookie = readdirSync2(runtimeDir).find(
|
|
885
897
|
(f) => /^\.mutter-Xwaylandauth\./.test(f)
|
|
886
898
|
);
|
|
887
899
|
if (cookie) xauth = `${runtimeDir}/${cookie}`;
|
|
@@ -1062,6 +1074,16 @@ import { randomUUID } from "crypto";
|
|
|
1062
1074
|
import { resolve as resolvePath } from "path";
|
|
1063
1075
|
import { fileURLToPath } from "url";
|
|
1064
1076
|
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
1077
|
+
async function detectLocale() {
|
|
1078
|
+
try {
|
|
1079
|
+
return await inferRegionViaStargate({ staging: false }) === "fr" ? "fr" : "en";
|
|
1080
|
+
} catch {
|
|
1081
|
+
return "en";
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
function parseLocale(raw) {
|
|
1085
|
+
return raw === "fr" ? "fr" : "en";
|
|
1086
|
+
}
|
|
1065
1087
|
async function isLeadbayConfigured(client) {
|
|
1066
1088
|
if (client.id === "claude-code") {
|
|
1067
1089
|
return await isLeadbayConfiguredInClaudeCode();
|
|
@@ -1192,23 +1214,25 @@ async function install(body) {
|
|
|
1192
1214
|
cleanupSessions();
|
|
1193
1215
|
const session = body.sessionId ? sessions.get(body.sessionId) : void 0;
|
|
1194
1216
|
const clientIds = body.clientIds ?? [];
|
|
1195
|
-
if (!session) return { ok: false, output:
|
|
1196
|
-
if (!clientIds.length) return { ok: false, output:
|
|
1217
|
+
if (!session) return { ok: false, output: MESSAGES.en.server.loginExpired };
|
|
1218
|
+
if (!clientIds.length) return { ok: false, output: MESSAGES.en.server.selectAtLeastOne };
|
|
1219
|
+
const locale = session.region === "fr" ? "fr" : "en";
|
|
1220
|
+
const s = MESSAGES[locale].server;
|
|
1197
1221
|
const detected = await detectClients();
|
|
1198
1222
|
const selected = detected.filter((client) => clientIds.includes(client.id));
|
|
1199
|
-
if (!selected.length) return { ok: false, output:
|
|
1223
|
+
if (!selected.length) return { ok: false, output: s.noSelectedDetected };
|
|
1200
1224
|
const includeWrite = body.includeWrite !== false;
|
|
1201
1225
|
const telemetryEnabled = body.telemetryEnabled !== false;
|
|
1202
1226
|
const results = [];
|
|
1203
1227
|
for (const client of selected) results.push(await installInto(client, session, includeWrite, telemetryEnabled));
|
|
1204
1228
|
const output = [
|
|
1205
|
-
|
|
1206
|
-
|
|
1229
|
+
s.loggedInBackend(session.region),
|
|
1230
|
+
s.settingsLine(includeWrite, telemetryEnabled),
|
|
1207
1231
|
"",
|
|
1208
|
-
|
|
1232
|
+
s.installSummaryHeader,
|
|
1209
1233
|
...results.map((result) => `${result.ok ? "OK" : "ERROR"} ${result.label}: ${result.message}`),
|
|
1210
1234
|
"",
|
|
1211
|
-
|
|
1235
|
+
s.restartClients
|
|
1212
1236
|
].join("\n");
|
|
1213
1237
|
return { ok: results.some((result) => result.ok), output: sanitizeOutput(output), results };
|
|
1214
1238
|
}
|
|
@@ -1223,6 +1247,8 @@ async function streamInstall(url, res, onDone) {
|
|
|
1223
1247
|
const clientIds = (url.searchParams.get("clients") ?? "").split(",").filter(Boolean);
|
|
1224
1248
|
const includeWrite = url.searchParams.get("write") !== "0";
|
|
1225
1249
|
const telemetryEnabled = url.searchParams.get("telemetry") !== "0";
|
|
1250
|
+
const locale = parseLocale(url.searchParams.get("locale"));
|
|
1251
|
+
const s = MESSAGES[locale].server;
|
|
1226
1252
|
const emit = (level, message) => sendSse(res, { level, message: sanitizeOutput(message) });
|
|
1227
1253
|
const abort = (msg) => {
|
|
1228
1254
|
emit("done", msg);
|
|
@@ -1234,29 +1260,29 @@ async function streamInstall(url, res, onDone) {
|
|
|
1234
1260
|
onDone?.();
|
|
1235
1261
|
};
|
|
1236
1262
|
if (!session) {
|
|
1237
|
-
emit("error",
|
|
1238
|
-
abort(
|
|
1263
|
+
emit("error", s.loginExpired);
|
|
1264
|
+
abort(s.installStopped);
|
|
1239
1265
|
return;
|
|
1240
1266
|
}
|
|
1241
1267
|
if (!clientIds.length) {
|
|
1242
|
-
emit("error",
|
|
1243
|
-
abort(
|
|
1268
|
+
emit("error", s.selectAtLeastOne);
|
|
1269
|
+
abort(s.installStopped);
|
|
1244
1270
|
return;
|
|
1245
1271
|
}
|
|
1246
|
-
emit("info",
|
|
1247
|
-
emit("info",
|
|
1248
|
-
emit("info",
|
|
1272
|
+
emit("info", s.connectedTo(session.accountLabel));
|
|
1273
|
+
emit("info", s.toolFlags(includeWrite, telemetryEnabled));
|
|
1274
|
+
emit("info", s.refreshingDetection);
|
|
1249
1275
|
const detected = await detectClients();
|
|
1250
1276
|
const selected = detected.filter((client) => clientIds.includes(client.id));
|
|
1251
1277
|
const selectedHasOnlyManualSetup = selected.length > 0 && selected.every(isManualSetupClient);
|
|
1252
1278
|
if (!selected.length) {
|
|
1253
|
-
emit("error",
|
|
1254
|
-
abort(
|
|
1279
|
+
emit("error", s.noSelectedDetected);
|
|
1280
|
+
abort(s.installStopped);
|
|
1255
1281
|
return;
|
|
1256
1282
|
}
|
|
1257
1283
|
let okCount = 0;
|
|
1258
1284
|
for (const client of selected) {
|
|
1259
|
-
emit("active", isManualSetupClient(client) ?
|
|
1285
|
+
emit("active", isManualSetupClient(client) ? s.preparingManual(client.label) : s.installing(client.label));
|
|
1260
1286
|
const result = await installInto(client, session, includeWrite, telemetryEnabled);
|
|
1261
1287
|
if (result.ok) {
|
|
1262
1288
|
okCount += 1;
|
|
@@ -1265,8 +1291,8 @@ async function streamInstall(url, res, onDone) {
|
|
|
1265
1291
|
emit("error", `${result.label}: ${result.message}`);
|
|
1266
1292
|
}
|
|
1267
1293
|
}
|
|
1268
|
-
const summary = selectedHasOnlyManualSetup ?
|
|
1269
|
-
const closing = selectedHasOnlyManualSetup ?
|
|
1294
|
+
const summary = selectedHasOnlyManualSetup ? s.manualReady : s.installedSummary(okCount, selected.length);
|
|
1295
|
+
const closing = selectedHasOnlyManualSetup ? s.followManual : s.restartClients;
|
|
1270
1296
|
emit(okCount > 0 ? "success" : "error", summary);
|
|
1271
1297
|
if (okCount > 0) {
|
|
1272
1298
|
finish(closing);
|
|
@@ -1281,6 +1307,8 @@ async function streamUninstall(url, res, onDone) {
|
|
|
1281
1307
|
connection: "keep-alive"
|
|
1282
1308
|
});
|
|
1283
1309
|
const clientIds = (url.searchParams.get("clients") ?? "").split(",").filter(Boolean);
|
|
1310
|
+
const locale = parseLocale(url.searchParams.get("locale"));
|
|
1311
|
+
const s = MESSAGES[locale].server;
|
|
1284
1312
|
const emit = (level, message) => sendSse(res, { level, message });
|
|
1285
1313
|
const abort = (msg) => {
|
|
1286
1314
|
emit("done", msg);
|
|
@@ -1292,20 +1320,20 @@ async function streamUninstall(url, res, onDone) {
|
|
|
1292
1320
|
onDone?.();
|
|
1293
1321
|
};
|
|
1294
1322
|
if (!clientIds.length) {
|
|
1295
|
-
emit("error",
|
|
1296
|
-
abort(
|
|
1323
|
+
emit("error", s.selectAtLeastOne);
|
|
1324
|
+
abort(s.uninstallStopped);
|
|
1297
1325
|
return;
|
|
1298
1326
|
}
|
|
1299
1327
|
const detected = await detectClients();
|
|
1300
1328
|
const selected = detected.filter((c) => clientIds.includes(c.id));
|
|
1301
1329
|
if (!selected.length) {
|
|
1302
|
-
emit("error",
|
|
1303
|
-
abort(
|
|
1330
|
+
emit("error", s.noSelectedDetected);
|
|
1331
|
+
abort(s.uninstallStopped);
|
|
1304
1332
|
return;
|
|
1305
1333
|
}
|
|
1306
1334
|
let okCount = 0;
|
|
1307
1335
|
for (const client of selected) {
|
|
1308
|
-
emit("active",
|
|
1336
|
+
emit("active", s.removing(client.label));
|
|
1309
1337
|
let res2;
|
|
1310
1338
|
if (client.id === "claude-code") {
|
|
1311
1339
|
res2 = await uninstallFromClaudeCode();
|
|
@@ -1323,16 +1351,17 @@ async function streamUninstall(url, res, onDone) {
|
|
|
1323
1351
|
emit("error", `${client.label}: ${res2.message}`);
|
|
1324
1352
|
}
|
|
1325
1353
|
}
|
|
1326
|
-
emit(okCount > 0 ? "success" : "error",
|
|
1327
|
-
finish(
|
|
1354
|
+
emit(okCount > 0 ? "success" : "error", s.removedSummary(okCount, selected.length));
|
|
1355
|
+
finish(s.completeRemoval);
|
|
1328
1356
|
}
|
|
1329
|
-
function pageUninstallHtml() {
|
|
1357
|
+
function pageUninstallHtml(locale = "en") {
|
|
1358
|
+
const ui = MESSAGES[locale].uninstaller;
|
|
1330
1359
|
return `<!doctype html>
|
|
1331
|
-
<html lang="
|
|
1360
|
+
<html lang="${locale}">
|
|
1332
1361
|
<head>
|
|
1333
1362
|
<meta charset="utf-8" />
|
|
1334
1363
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
1335
|
-
<title
|
|
1364
|
+
<title>${ui.docTitle}</title>
|
|
1336
1365
|
<style>
|
|
1337
1366
|
:root { color-scheme: light; --bg:#fff; --card:#fff; --strong:#1d2228; --muted:#9aa0ab; --line:#e7e9ee; --accent:#0d0f0e; --danger:#d14343; --ok:#16a34a; --warn:#b06a00; }
|
|
1338
1367
|
* { box-sizing:border-box; }
|
|
@@ -1384,8 +1413,8 @@ function pageUninstallHtml() {
|
|
|
1384
1413
|
<main>
|
|
1385
1414
|
<div class="steps"><div class="dot active" id="dot-1"></div><div class="dot" id="dot-2"></div></div>
|
|
1386
1415
|
<div class="card">
|
|
1387
|
-
<h1 id="title"
|
|
1388
|
-
<p class="sub" id="sub"
|
|
1416
|
+
<h1 id="title">${ui.steps[1].title}</h1>
|
|
1417
|
+
<p class="sub" id="sub">${ui.steps[1].sub}</p>
|
|
1389
1418
|
|
|
1390
1419
|
<section id="step-1">
|
|
1391
1420
|
<div class="spinner" id="spinner"></div>
|
|
@@ -1403,19 +1432,18 @@ function pageUninstallHtml() {
|
|
|
1403
1432
|
</section>
|
|
1404
1433
|
|
|
1405
1434
|
<div class="actions">
|
|
1406
|
-
<button id="back" class="ghost hidden"
|
|
1407
|
-
<button id="refresh"
|
|
1408
|
-
<button class="danger" id="next"
|
|
1435
|
+
<button id="back" class="ghost hidden">${ui.btnBack}</button>
|
|
1436
|
+
<button id="refresh">${ui.btnRefresh}</button>
|
|
1437
|
+
<button class="danger" id="next">${ui.btnRemove}</button>
|
|
1409
1438
|
</div>
|
|
1410
1439
|
</div>
|
|
1411
1440
|
<p class="version">v${VERSION}</p>
|
|
1412
1441
|
</main>
|
|
1413
1442
|
<script>
|
|
1443
|
+
const LOCALE = "${locale}";
|
|
1444
|
+
const T = ${JSON.stringify(ui)};
|
|
1414
1445
|
const $ = (id) => document.getElementById(id);
|
|
1415
|
-
const STEPS =
|
|
1416
|
-
1: { title: "Remove Leadbay MCP", sub: "Select the agents to remove Leadbay MCP from." },
|
|
1417
|
-
2: { title: "Removing", sub: "Keep this window open until it's done." },
|
|
1418
|
-
};
|
|
1446
|
+
const STEPS = T.steps;
|
|
1419
1447
|
const CHECK = "M20 33 l8 8 l16 -18";
|
|
1420
1448
|
const CROSS = "M22 22 l20 20 M42 22 l-20 20";
|
|
1421
1449
|
let step = 1;
|
|
@@ -1427,18 +1455,18 @@ function pageUninstallHtml() {
|
|
|
1427
1455
|
function showResult(ok, msg) {
|
|
1428
1456
|
$("sub").classList.add("hidden");
|
|
1429
1457
|
$("result-msg").textContent = msg;
|
|
1430
|
-
$("result-note").textContent = ok ?
|
|
1458
|
+
$("result-note").textContent = ok ? T.closeWindow : "";
|
|
1431
1459
|
$("ring-mark").setAttribute("d", ok ? CHECK : CROSS);
|
|
1432
1460
|
const ring = $("ring"); ring.classList.remove("ok", "err"); void ring.getBoundingClientRect();
|
|
1433
1461
|
ring.classList.add(ok ? "ok" : "err");
|
|
1434
1462
|
$("result").classList.toggle("err", !ok);
|
|
1435
1463
|
$("result").classList.remove("hidden");
|
|
1436
|
-
$("title").textContent = ok ?
|
|
1464
|
+
$("title").textContent = ok ? T.allSet : T.somethingWrong;
|
|
1437
1465
|
["next", "back", "refresh"].forEach((id) => $(id).classList.add("hidden"));
|
|
1438
1466
|
}
|
|
1439
|
-
function renderAgents() { $("spinner").classList.add("hidden"); const root = $("agents"); if (!clients.length) { root.innerHTML = '<div class="sub">
|
|
1440
|
-
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(
|
|
1441
|
-
async function doUninstall() { const selected = [...document.querySelectorAll("[data-client]:checked")].map((el) => el.dataset.client); if (!selected.length) return say(
|
|
1467
|
+
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(""); }
|
|
1468
|
+
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); }
|
|
1469
|
+
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); }; }
|
|
1442
1470
|
$("back").addEventListener("click", () => setStep(1));
|
|
1443
1471
|
$("refresh").addEventListener("click", refresh);
|
|
1444
1472
|
$("next").addEventListener("click", doUninstall);
|
|
@@ -1447,13 +1475,14 @@ function pageUninstallHtml() {
|
|
|
1447
1475
|
</body>
|
|
1448
1476
|
</html>`;
|
|
1449
1477
|
}
|
|
1450
|
-
function pageHtml() {
|
|
1478
|
+
function pageHtml(locale = "en") {
|
|
1479
|
+
const ui = MESSAGES[locale].installer;
|
|
1451
1480
|
return `<!doctype html>
|
|
1452
|
-
<html lang="
|
|
1481
|
+
<html lang="${locale}">
|
|
1453
1482
|
<head>
|
|
1454
1483
|
<meta charset="utf-8" />
|
|
1455
1484
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
1456
|
-
<title
|
|
1485
|
+
<title>${ui.docTitle}</title>
|
|
1457
1486
|
<style>
|
|
1458
1487
|
: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; }
|
|
1459
1488
|
* { box-sizing:border-box; }
|
|
@@ -1509,8 +1538,8 @@ function pageHtml() {
|
|
|
1509
1538
|
<main>
|
|
1510
1539
|
<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>
|
|
1511
1540
|
<div class="card">
|
|
1512
|
-
<h1 id="title"
|
|
1513
|
-
<p class="sub" id="sub"
|
|
1541
|
+
<h1 id="title">${ui.steps[1].title}</h1>
|
|
1542
|
+
<p class="sub" id="sub">${ui.steps[1].sub}</p>
|
|
1514
1543
|
|
|
1515
1544
|
<section id="step-2" class="hidden">
|
|
1516
1545
|
<div class="spinner" id="spinner"></div>
|
|
@@ -1528,20 +1557,18 @@ function pageHtml() {
|
|
|
1528
1557
|
</section>
|
|
1529
1558
|
|
|
1530
1559
|
<div class="actions">
|
|
1531
|
-
<button id="back" class="cancel hidden"
|
|
1532
|
-
<button id="refresh" class="ghost hidden"
|
|
1533
|
-
<button class="primary" id="next"
|
|
1560
|
+
<button id="back" class="cancel hidden">${ui.btnBack}</button>
|
|
1561
|
+
<button id="refresh" class="ghost hidden">${ui.btnRefresh}</button>
|
|
1562
|
+
<button class="primary" id="next">${ui.btnSignIn}</button>
|
|
1534
1563
|
</div>
|
|
1535
1564
|
</div>
|
|
1536
1565
|
<p class="version">v${VERSION}</p>
|
|
1537
1566
|
</main>
|
|
1538
1567
|
<script>
|
|
1568
|
+
const LOCALE = "${locale}";
|
|
1569
|
+
const T = ${JSON.stringify(ui)};
|
|
1539
1570
|
const $ = (id) => document.getElementById(id);
|
|
1540
|
-
const STEPS =
|
|
1541
|
-
1: { title: "Connect Leadbay", sub: "Sign in to install Leadbay across your AI agents." },
|
|
1542
|
-
2: { title: "Choose your agents", sub: "Pick where to install Leadbay." },
|
|
1543
|
-
3: { title: "Installing", sub: "Keep this window open until it's done." },
|
|
1544
|
-
};
|
|
1571
|
+
const STEPS = T.steps;
|
|
1545
1572
|
const CHECK = "M20 33 l8 8 l16 -18";
|
|
1546
1573
|
const CROSS = "M22 22 l20 20 M42 22 l-20 20";
|
|
1547
1574
|
let step = 1;
|
|
@@ -1559,30 +1586,30 @@ function pageHtml() {
|
|
|
1559
1586
|
$("back").classList.toggle("hidden", step !== 2);
|
|
1560
1587
|
$("refresh").classList.toggle("hidden", step !== 2);
|
|
1561
1588
|
$("next").classList.toggle("hidden", step === 3);
|
|
1562
|
-
$("next").textContent = step === 2 ?
|
|
1589
|
+
$("next").textContent = step === 2 ? T.btnInstall : T.btnSignIn;
|
|
1563
1590
|
}
|
|
1564
1591
|
// Final completion state: animated green check / red cross + message.
|
|
1565
1592
|
function showResult(ok, msg) {
|
|
1566
1593
|
$("sub").classList.add("hidden");
|
|
1567
1594
|
$("result-msg").textContent = msg;
|
|
1568
|
-
$("result-note").textContent = ok ?
|
|
1595
|
+
$("result-note").textContent = ok ? T.closeWindow : "";
|
|
1569
1596
|
$("ring-mark").setAttribute("d", ok ? CHECK : CROSS);
|
|
1570
1597
|
const ring = $("ring"); ring.classList.remove("ok", "err"); void ring.getBoundingClientRect();
|
|
1571
1598
|
ring.classList.add(ok ? "ok" : "err");
|
|
1572
1599
|
$("result").classList.toggle("err", !ok);
|
|
1573
1600
|
$("result").classList.remove("hidden");
|
|
1574
|
-
$("title").textContent = ok ?
|
|
1601
|
+
$("title").textContent = ok ? T.allSet : T.somethingWrong;
|
|
1575
1602
|
["next", "back", "refresh"].forEach((id) => $(id).classList.add("hidden"));
|
|
1576
1603
|
}
|
|
1577
|
-
function renderAgents() { $("spinner").classList.add("hidden"); const root = $("agents"); if (!clients.length) { root.innerHTML = '<div class="sub">
|
|
1578
|
-
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(
|
|
1579
|
-
async function doLogin() { $("next").disabled = true; say(
|
|
1604
|
+
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(""); }
|
|
1605
|
+
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); }
|
|
1606
|
+
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; } }
|
|
1580
1607
|
async function install() {
|
|
1581
1608
|
const selected = [...document.querySelectorAll("[data-client]:checked")].map((el) => el.dataset.client);
|
|
1582
|
-
if (!selected.length) return say(
|
|
1609
|
+
if (!selected.length) return say(T.selectAtLeastOne, true);
|
|
1583
1610
|
setStep(3);
|
|
1584
1611
|
let okCount = 0, lastError = "";
|
|
1585
|
-
const params = new URLSearchParams({ sessionId, clients: selected.join(","), write: "1", telemetry: "1" });
|
|
1612
|
+
const params = new URLSearchParams({ sessionId, clients: selected.join(","), write: "1", telemetry: "1", locale: LOCALE });
|
|
1586
1613
|
const events = new EventSource("/api/install-stream?" + params.toString());
|
|
1587
1614
|
events.onmessage = (event) => {
|
|
1588
1615
|
const data = JSON.parse(event.data);
|
|
@@ -1591,12 +1618,12 @@ function pageHtml() {
|
|
|
1591
1618
|
if (data.level === "done") {
|
|
1592
1619
|
events.close();
|
|
1593
1620
|
const ok = okCount > 0 && !lastError;
|
|
1594
|
-
showResult(ok, ok ?
|
|
1621
|
+
showResult(ok, ok ? T.successInstalled : (lastError || T.noneInstalled));
|
|
1595
1622
|
} else {
|
|
1596
1623
|
say(data.message, data.level === "error");
|
|
1597
1624
|
}
|
|
1598
1625
|
};
|
|
1599
|
-
events.onerror = () => { events.close(); showResult(false,
|
|
1626
|
+
events.onerror = () => { events.close(); showResult(false, T.streamDisconnected); };
|
|
1600
1627
|
}
|
|
1601
1628
|
$("back").addEventListener("click", () => setStep(1));
|
|
1602
1629
|
$("refresh").addEventListener("click", refresh);
|
|
@@ -1651,7 +1678,7 @@ function makeGuiServer(options, pageContent, extraRoutes, logLabel) {
|
|
|
1651
1678
|
}
|
|
1652
1679
|
try {
|
|
1653
1680
|
if (req.method === "GET" && req.url === "/") {
|
|
1654
|
-
const raw = pageContent();
|
|
1681
|
+
const raw = await pageContent();
|
|
1655
1682
|
res.writeHead(200, { "content-type": "text/html; charset=utf-8", "content-length": Buffer.byteLength(raw) });
|
|
1656
1683
|
res.end(raw);
|
|
1657
1684
|
return;
|
|
@@ -1678,7 +1705,7 @@ function makeGuiServer(options, pageContent, extraRoutes, logLabel) {
|
|
|
1678
1705
|
});
|
|
1679
1706
|
}
|
|
1680
1707
|
function startInstallerGui(options = {}) {
|
|
1681
|
-
return makeGuiServer(options, pageHtml, async (req, res, onDone) => {
|
|
1708
|
+
return makeGuiServer(options, async () => pageHtml(await detectLocale()), async (req, res, onDone) => {
|
|
1682
1709
|
if (req.method === "GET" && req.url === "/api/status") {
|
|
1683
1710
|
sendJson(res, 200, { os: formatInstallOsLabel(), hostedMcpUrl: HOSTED_MCP_URL, clients: await clientsWithConfiguredStatus() });
|
|
1684
1711
|
return true;
|
|
@@ -1699,7 +1726,7 @@ function startInstallerGui(options = {}) {
|
|
|
1699
1726
|
}, "installer");
|
|
1700
1727
|
}
|
|
1701
1728
|
function startUninstallerGui(options = {}) {
|
|
1702
|
-
return makeGuiServer(options, pageUninstallHtml, async (req, res, onDone) => {
|
|
1729
|
+
return makeGuiServer(options, async () => pageUninstallHtml(await detectLocale()), async (req, res, onDone) => {
|
|
1703
1730
|
if (req.method === "GET" && req.url === "/api/status") {
|
|
1704
1731
|
sendJson(res, 200, { os: formatInstallOsLabel(), clients: await clientsWithConfiguredStatus() });
|
|
1705
1732
|
return true;
|
|
@@ -1711,7 +1738,7 @@ function startUninstallerGui(options = {}) {
|
|
|
1711
1738
|
return false;
|
|
1712
1739
|
}, "uninstaller");
|
|
1713
1740
|
}
|
|
1714
|
-
var VERSION, PORT, sessions, OAUTH_BASE_URLS, LOCAL_BIN_PATH;
|
|
1741
|
+
var VERSION, MESSAGES, PORT, sessions, OAUTH_BASE_URLS, LOCAL_BIN_PATH;
|
|
1715
1742
|
var init_installer_gui = __esm({
|
|
1716
1743
|
"installer/installer-gui.ts"() {
|
|
1717
1744
|
"use strict";
|
|
@@ -1721,7 +1748,147 @@ var init_installer_gui = __esm({
|
|
|
1721
1748
|
init_install_dxt();
|
|
1722
1749
|
init_install_shared();
|
|
1723
1750
|
init_oauth();
|
|
1724
|
-
VERSION = true ? "0.23.
|
|
1751
|
+
VERSION = true ? "0.23.6" : "0.0.0-dev";
|
|
1752
|
+
MESSAGES = {
|
|
1753
|
+
en: {
|
|
1754
|
+
installer: {
|
|
1755
|
+
docTitle: "Leadbay MCP installer",
|
|
1756
|
+
steps: {
|
|
1757
|
+
1: { title: "Connect Leadbay", sub: "Sign in to install Leadbay across your AI agents." },
|
|
1758
|
+
2: { title: "Choose your agents", sub: "Pick where to install Leadbay." },
|
|
1759
|
+
3: { title: "Installing", sub: "Keep this window open until it's done." }
|
|
1760
|
+
},
|
|
1761
|
+
btnSignIn: "Sign in with Leadbay",
|
|
1762
|
+
btnInstall: "Install",
|
|
1763
|
+
btnBack: "Back",
|
|
1764
|
+
btnRefresh: "Refresh",
|
|
1765
|
+
noClientsDetected: "No supported MCP client detected on this machine.",
|
|
1766
|
+
noAgentsDetected: "No supported agents detected.",
|
|
1767
|
+
selectAtLeastOne: "Select at least one agent.",
|
|
1768
|
+
openingSignIn: "Opening Leadbay sign-in in your browser...",
|
|
1769
|
+
oauthFailed: "OAuth login failed.",
|
|
1770
|
+
successInstalled: "MCP successfully installed",
|
|
1771
|
+
noneInstalled: "No agents were installed.",
|
|
1772
|
+
streamDisconnected: "Install stream disconnected.",
|
|
1773
|
+
closeWindow: "You can close this window.",
|
|
1774
|
+
allSet: "All set",
|
|
1775
|
+
somethingWrong: "Something went wrong",
|
|
1776
|
+
badgeManual: "manual",
|
|
1777
|
+
badgeUpdate: "update",
|
|
1778
|
+
badgeInstall: "install"
|
|
1779
|
+
},
|
|
1780
|
+
uninstaller: {
|
|
1781
|
+
docTitle: "Leadbay MCP uninstaller",
|
|
1782
|
+
steps: {
|
|
1783
|
+
1: { title: "Remove Leadbay MCP", sub: "Select the agents to remove Leadbay MCP from." },
|
|
1784
|
+
2: { title: "Removing", sub: "Keep this window open until it's done." }
|
|
1785
|
+
},
|
|
1786
|
+
btnRemove: "Remove selected",
|
|
1787
|
+
btnBack: "Back",
|
|
1788
|
+
btnRefresh: "Refresh",
|
|
1789
|
+
noInstallDetected: "No Leadbay MCP installation detected on this machine.",
|
|
1790
|
+
selectAtLeastOne: "Select at least one agent.",
|
|
1791
|
+
successRemoved: "MCP successfully removed",
|
|
1792
|
+
noneRemoved: "No agents were removed.",
|
|
1793
|
+
streamDisconnected: "Uninstall stream disconnected.",
|
|
1794
|
+
closeWindow: "You can close this window.",
|
|
1795
|
+
allSet: "All set",
|
|
1796
|
+
somethingWrong: "Something went wrong"
|
|
1797
|
+
},
|
|
1798
|
+
server: {
|
|
1799
|
+
loginExpired: "Login expired. Go back and sign in again.",
|
|
1800
|
+
selectAtLeastOne: "Select at least one agent.",
|
|
1801
|
+
noSelectedDetected: "No selected agents were detected on this machine.",
|
|
1802
|
+
installStopped: "Install stopped.",
|
|
1803
|
+
uninstallStopped: "Uninstall stopped.",
|
|
1804
|
+
connectedTo: (label) => `Connected to ${label}.`,
|
|
1805
|
+
toolFlags: (write, telemetry) => `Write tools ${write ? "enabled" : "disabled"}; telemetry ${telemetry ? "enabled" : "disabled"}.`,
|
|
1806
|
+
refreshingDetection: "Refreshing installed-agent detection...",
|
|
1807
|
+
installing: (label) => `Installing ${label}...`,
|
|
1808
|
+
preparingManual: (label) => `Preparing ${label} manual setup...`,
|
|
1809
|
+
removing: (label) => `Removing from ${label}...`,
|
|
1810
|
+
installedSummary: (ok, total) => `${ok}/${total} agent(s) installed, updated, or prepared.`,
|
|
1811
|
+
manualReady: "Manual ChatGPT setup instructions ready.",
|
|
1812
|
+
removedSummary: (ok, total) => `${ok}/${total} agent(s) removed.`,
|
|
1813
|
+
restartClients: "Restart your MCP client(s) to pick up the new server.",
|
|
1814
|
+
followManual: "Follow the manual setup instructions shown above.",
|
|
1815
|
+
completeRemoval: "Restart your MCP client(s) to complete the removal.",
|
|
1816
|
+
loggedInBackend: (region) => `Logged in to ${region.toUpperCase()} backend.`,
|
|
1817
|
+
settingsLine: (write, telemetry) => `Settings: write tools ${write ? "on" : "off"}, telemetry ${telemetry ? "on" : "off"}.`,
|
|
1818
|
+
installSummaryHeader: "Install summary:",
|
|
1819
|
+
noAgentsRemoved: "No agents were removed."
|
|
1820
|
+
}
|
|
1821
|
+
},
|
|
1822
|
+
fr: {
|
|
1823
|
+
installer: {
|
|
1824
|
+
docTitle: "Installateur Leadbay MCP",
|
|
1825
|
+
steps: {
|
|
1826
|
+
1: { title: "Connectez Leadbay", sub: "Connectez-vous pour installer Leadbay sur vos agents IA." },
|
|
1827
|
+
2: { title: "Choisissez vos agents", sub: "Choisissez o\xF9 installer Leadbay." },
|
|
1828
|
+
3: { title: "Installation en cours", sub: "Gardez cette fen\xEAtre ouverte jusqu'\xE0 la fin." }
|
|
1829
|
+
},
|
|
1830
|
+
btnSignIn: "Se connecter avec Leadbay",
|
|
1831
|
+
btnInstall: "Installer",
|
|
1832
|
+
btnBack: "Retour",
|
|
1833
|
+
btnRefresh: "Actualiser",
|
|
1834
|
+
noClientsDetected: "Aucun client MCP compatible d\xE9tect\xE9 sur cette machine.",
|
|
1835
|
+
noAgentsDetected: "Aucun agent compatible d\xE9tect\xE9.",
|
|
1836
|
+
selectAtLeastOne: "S\xE9lectionnez au moins un agent.",
|
|
1837
|
+
openingSignIn: "Ouverture de la connexion Leadbay dans votre navigateur...",
|
|
1838
|
+
oauthFailed: "\xC9chec de la connexion OAuth.",
|
|
1839
|
+
successInstalled: "MCP install\xE9 avec succ\xE8s",
|
|
1840
|
+
noneInstalled: "Aucun agent n'a \xE9t\xE9 install\xE9.",
|
|
1841
|
+
streamDisconnected: "Flux d'installation interrompu.",
|
|
1842
|
+
closeWindow: "Vous pouvez fermer cette fen\xEAtre.",
|
|
1843
|
+
allSet: "Termin\xE9",
|
|
1844
|
+
somethingWrong: "Une erreur est survenue",
|
|
1845
|
+
badgeManual: "manuel",
|
|
1846
|
+
badgeUpdate: "mettre \xE0 jour",
|
|
1847
|
+
badgeInstall: "installer"
|
|
1848
|
+
},
|
|
1849
|
+
uninstaller: {
|
|
1850
|
+
docTitle: "D\xE9sinstallateur Leadbay MCP",
|
|
1851
|
+
steps: {
|
|
1852
|
+
1: { title: "Supprimer Leadbay MCP", sub: "S\xE9lectionnez les agents desquels supprimer Leadbay MCP." },
|
|
1853
|
+
2: { title: "Suppression en cours", sub: "Gardez cette fen\xEAtre ouverte jusqu'\xE0 la fin." }
|
|
1854
|
+
},
|
|
1855
|
+
btnRemove: "Supprimer la s\xE9lection",
|
|
1856
|
+
btnBack: "Retour",
|
|
1857
|
+
btnRefresh: "Actualiser",
|
|
1858
|
+
noInstallDetected: "Aucune installation Leadbay MCP d\xE9tect\xE9e sur cette machine.",
|
|
1859
|
+
selectAtLeastOne: "S\xE9lectionnez au moins un agent.",
|
|
1860
|
+
successRemoved: "MCP supprim\xE9 avec succ\xE8s",
|
|
1861
|
+
noneRemoved: "Aucun agent n'a \xE9t\xE9 supprim\xE9.",
|
|
1862
|
+
streamDisconnected: "Flux de d\xE9sinstallation interrompu.",
|
|
1863
|
+
closeWindow: "Vous pouvez fermer cette fen\xEAtre.",
|
|
1864
|
+
allSet: "Termin\xE9",
|
|
1865
|
+
somethingWrong: "Une erreur est survenue"
|
|
1866
|
+
},
|
|
1867
|
+
server: {
|
|
1868
|
+
loginExpired: "Session expir\xE9e. Revenez en arri\xE8re et reconnectez-vous.",
|
|
1869
|
+
selectAtLeastOne: "S\xE9lectionnez au moins un agent.",
|
|
1870
|
+
noSelectedDetected: "Aucun des agents s\xE9lectionn\xE9s n'a \xE9t\xE9 d\xE9tect\xE9 sur cette machine.",
|
|
1871
|
+
installStopped: "Installation arr\xEAt\xE9e.",
|
|
1872
|
+
uninstallStopped: "D\xE9sinstallation arr\xEAt\xE9e.",
|
|
1873
|
+
connectedTo: (label) => `Connect\xE9 \xE0 ${label}.`,
|
|
1874
|
+
toolFlags: (write, telemetry) => `Outils d'\xE9criture ${write ? "activ\xE9s" : "d\xE9sactiv\xE9s"} ; t\xE9l\xE9m\xE9trie ${telemetry ? "activ\xE9e" : "d\xE9sactiv\xE9e"}.`,
|
|
1875
|
+
refreshingDetection: "Actualisation de la d\xE9tection des agents install\xE9s...",
|
|
1876
|
+
installing: (label) => `Installation de ${label}...`,
|
|
1877
|
+
preparingManual: (label) => `Pr\xE9paration de la configuration manuelle de ${label}...`,
|
|
1878
|
+
removing: (label) => `Suppression de ${label}...`,
|
|
1879
|
+
installedSummary: (ok, total) => `${ok}/${total} agent(s) install\xE9(s), mis \xE0 jour ou pr\xE9par\xE9(s).`,
|
|
1880
|
+
manualReady: "Instructions de configuration manuelle de ChatGPT pr\xEAtes.",
|
|
1881
|
+
removedSummary: (ok, total) => `${ok}/${total} agent(s) supprim\xE9(s).`,
|
|
1882
|
+
restartClients: "Red\xE9marrez votre/vos client(s) MCP pour charger le nouveau serveur.",
|
|
1883
|
+
followManual: "Suivez les instructions de configuration manuelle ci-dessus.",
|
|
1884
|
+
completeRemoval: "Red\xE9marrez votre/vos client(s) MCP pour terminer la suppression.",
|
|
1885
|
+
loggedInBackend: (region) => `Connect\xE9 au backend ${region.toUpperCase()}.`,
|
|
1886
|
+
settingsLine: (write, telemetry) => `Param\xE8tres : outils d'\xE9criture ${write ? "activ\xE9s" : "d\xE9sactiv\xE9s"}, t\xE9l\xE9m\xE9trie ${telemetry ? "activ\xE9e" : "d\xE9sactiv\xE9e"}.`,
|
|
1887
|
+
installSummaryHeader: "R\xE9capitulatif de l'installation :",
|
|
1888
|
+
noAgentsRemoved: "Aucun agent n'a \xE9t\xE9 supprim\xE9."
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
};
|
|
1725
1892
|
PORT = Number(process.env.LEADBAY_INSTALLER_PORT ?? 0);
|
|
1726
1893
|
sessions = /* @__PURE__ */ new Map();
|
|
1727
1894
|
OAUTH_BASE_URLS = {
|