@leadbay/mcp 0.23.10 → 0.23.11

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 CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog — @leadbay/mcp
2
2
 
3
+ ## 0.23.11 — 2026-07-02
4
+
5
+ Windows `.dxt` sign-in now opens the browser reliably (product#3839).
6
+
7
+ - **`openInBrowser` (Windows)** — the auto-open resolved the moment `cmd.exe` was *created*, before its internal `start` builtin actually handed the URL to the default browser. So a silent no-op (no default-browser protocol association, a locked-down shell / AppLocker, a corrupt `HKCR\http\shell\open`) went undetected: `browserOpenFailedAtBootstrap` stayed false and the user was told "a browser may have opened" when nothing did. On Windows we now wait (bounded — 800ms for `cmd start`, 1200ms otherwise) for the launcher's **exit code**, treat a non-zero exit as failure, and fall through to `rundll32 url.dll,FileProtocolHandler` (no command interpreter — the same ShellExecute path Explorer uses, with an honest exit code) and finally PowerShell `Start-Process`. When every launcher fails, the `AUTH_REQUIRED` envelope honestly says the browser couldn't be opened and shows the clickable sign-in link. The #3801 `&`-quoting `cmd start` candidate is unchanged and still tried first. macOS/Linux keep resolve-on-`spawn` (the #3805 headless-hang fix — those launchers are the hand-off).
8
+ - **`bin.ts` shutdown** — the `browserOpenInFlight` teardown wait rose 1.5s → 3s so the multi-launcher Windows walk can finish dispatching before exit (the sibling bootstrap wait already allows 4s; the surfaced sign-in link is the fallback either way).
9
+
3
10
  ## 0.23.10 — 2026-07-01
4
11
 
5
12
  A freshly-created lens no longer reads as "empty" (product#3833).
@@ -467,7 +474,7 @@ Every tool invocation now fires an `mcp tool called` event to PostHog (same proj
467
474
 
468
475
  ## 0.6.1 — 2026-05-10
469
476
 
470
- **Distribution**: listed in the official MCP Registry as `io.github.leadbay/leadbay-mcp` (auto-published from CI via GitHub OIDC); installable as a Claude Code plugin via `/plugin marketplace add leadbay/leadclaw` then `/plugin install leadbay@leadbay-leadclaw`; submission packets prepared for the Claude.ai connector directory and Anthropic's curated MCPB extension directory.
477
+ **Distribution**: listed in the official MCP Registry as `io.github.leadbay/leadbay-mcp` (auto-published from CI via GitHub OIDC); installable as a Claude Code plugin via `/plugin marketplace add leadbay/mcp` then `/plugin install leadbay@leadbay-mcp`; submission packets prepared for the Claude.ai connector directory and Anthropic's curated MCPB extension directory.
471
478
 
472
479
  **Registry verification**: adds `mcpName` to the npm package metadata so the MCP Registry can verify ownership of `io.github.leadbay/leadbay-mcp` against the published `@leadbay/mcp` package.
473
480
 
@@ -649,7 +656,7 @@ New `leadbay_import_leads` composite write tool.
649
656
  Claude Desktop 2026 compatibility + install UX polish. Also publishes the `refine_prompt` `/user_prompt` wire-key fix that landed on `main` in 0.2.3 but never reached npm.
650
657
 
651
658
  - **Fix [product#3504](https://github.com/leadbay/product/issues/3504)** — `install --target claude-desktop` no longer silently no-ops on Claude Desktop 2026. The app moved to the DXT (Desktop Extension) system; the legacy `claude_desktop_config.json` is UI-prefs-only there and gets overwritten on every prefs save, so a block written to it disappears a few minutes later. `install` now detects DXT (via `Claude Extensions/`, `extensions-installations.json`, or `dxt:*` keys in `config.json`), prints a loud warning pointing at the `.dxt` bundle, and default-skips the legacy write. Pass `--force-legacy` to override.
652
- - **New: shipping a `.dxt` bundle** — drag-drop into Claude Desktop 2026 → Settings → Extensions. Uploaded to each [GitHub Release](https://github.com/leadbay/leadclaw/releases). Dialog asks for token + region + write-toggle; no terminal required. Manifest is DXT 0.2 (`dxt_version: "0.2"`, `user_config.leadbay_token.sensitive: true`). Source for the build lives in `packages/dxt/`.
659
+ - **New: shipping a `.dxt` bundle** — drag-drop into Claude Desktop 2026 → Settings → Extensions. Uploaded to each [GitHub Release](https://github.com/leadbay/mcp/releases). Dialog asks for token + region + write-toggle; no terminal required. Manifest is DXT 0.2 (`dxt_version: "0.2"`, `user_config.leadbay_token.sensitive: true`). Source for the build lives in `packages/dxt/`.
653
660
  - **Fix**: `login` 401 errors no longer end with a dangling `:` when the backend returns an empty body. Messages now read `login failed (401) at <url> (wrong email or password?)`. 429/5xx get their own hints too. The core helper `formatLoginError` is exported from `@leadbay/core` so the MCP and ClawHub surfaces stay in sync.
654
661
  - **README**: new section on `npm install -g` EACCES (sudo / npx / nvm workarounds — common on the official nodejs.org `.pkg`), and a pointer to the `.dxt` install for Claude Desktop 2026.
655
662
  - **Also shipping** (previously merged but not yet published to npm) — `leadbay_refine_prompt` / `leadbay_set_user_prompt` now send `{ user_prompt }` instead of `{ prompt }` to `POST /user_prompt` ([product#3508](https://github.com/leadbay/product/issues/3508)). Fixes the JSON deserialization 400 Ludo hit during the 0.2.2 install session.
package/MIGRATION.md CHANGED
@@ -166,7 +166,7 @@ The legacy `leadbay-mcp install --include-write` flag is accepted but a no-op
166
166
 
167
167
  ---
168
168
 
169
- # Migration: leadclaw / leadbay-mcp 0.1.x → 0.2.0
169
+ # Migration: leadbay-mcp 0.1.x → 0.2.0
170
170
 
171
171
  This release is the autoplan-reviewed agent-experience overhaul. The OpenClaw
172
172
  plugin and MCP server gain a coherent composite-tool surface so an AI agent
@@ -277,7 +277,7 @@ additionally exposes the granular tools; most users don't need it.
277
277
 
278
278
  ### OpenClaw plugin
279
279
 
280
- In the plugin config (e.g. `openclaw config set plugins.entries.leadclaw.exposeWrite true`):
280
+ In the plugin config (e.g. `openclaw config set plugins.entries.leadbay-mcp.exposeWrite true`):
281
281
 
282
282
  ```json
283
283
  {
package/dist/bin.js CHANGED
@@ -9407,12 +9407,12 @@ var init_login = __esm({
9407
9407
  execute: async (client, params, ctx) => {
9408
9408
  const cleanPassword = params.password.replace(/\\(.)/g, "$1");
9409
9409
  const startWith = client.region === "fr" ? "fr" : "us";
9410
- ctx?.logger?.info?.(`LeadClaw login: email=${params.email} startRegion=${startWith}`);
9410
+ ctx?.logger?.info?.(`Leadbay login: email=${params.email} startRegion=${startWith}`);
9411
9411
  try {
9412
9412
  const result = await resolveRegion(params.email, cleanPassword, startWith);
9413
9413
  if (client.baseUrl !== result.baseUrl) {
9414
9414
  client.setBaseUrl(result.baseUrl, result.region);
9415
- ctx?.logger?.info?.(`LeadClaw login: switched to region=${result.region} (account is in the ${result.region.toUpperCase()} backend)`);
9415
+ ctx?.logger?.info?.(`Leadbay login: switched to region=${result.region} (account is in the ${result.region.toUpperCase()} backend)`);
9416
9416
  }
9417
9417
  client.setToken(result.token);
9418
9418
  client.prefetchOrgData().catch(() => {
@@ -9424,7 +9424,7 @@ var init_login = __esm({
9424
9424
  verified: result.verified
9425
9425
  };
9426
9426
  } catch (err) {
9427
- ctx?.logger?.error?.(`LeadClaw login: failed: ${err?.message}`);
9427
+ ctx?.logger?.error?.(`Leadbay login: failed: ${err?.message}`);
9428
9428
  return {
9429
9429
  error: true,
9430
9430
  code: "LOGIN_FAILED",
@@ -25800,7 +25800,7 @@ function buildServer(client, opts = {}) {
25800
25800
  }
25801
25801
  const exposedNames = new Set(toolByName.keys());
25802
25802
  const server = new Server(
25803
- { name: "leadbay", version: opts.version ?? "0.0.0-dev" },
25803
+ { name: "leadbay-mcp", version: opts.version ?? "0.0.0-dev" },
25804
25804
  {
25805
25805
  capabilities: {
25806
25806
  tools: {},
@@ -27582,6 +27582,17 @@ function browserOpenCandidates(url) {
27582
27582
  { cmd: "xdg-open", args: [url] }
27583
27583
  ];
27584
27584
  }
27585
+ function windowsFallbackCandidates(url) {
27586
+ const sysRoot = process.env.SystemRoot || process.env.windir || "C:\\Windows";
27587
+ const psLiteral = `'${url.replace(/'/g, "''")}'`;
27588
+ return [
27589
+ { cmd: `${sysRoot}\\System32\\rundll32.exe`, args: ["url.dll,FileProtocolHandler", url] },
27590
+ {
27591
+ cmd: `${sysRoot}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`,
27592
+ args: ["-NoProfile", "-NonInteractive", "-Command", "Start-Process", psLiteral]
27593
+ }
27594
+ ];
27595
+ }
27585
27596
  function browserLaunchEnv(debug) {
27586
27597
  const env = { ...process.env };
27587
27598
  if (process.platform !== "linux") return env;
@@ -27646,7 +27657,7 @@ function browserLaunchEnv(debug) {
27646
27657
  return env;
27647
27658
  }
27648
27659
  async function openInBrowser(url, debug) {
27649
- const candidates = browserOpenCandidates(url);
27660
+ const candidates = process.platform === "win32" ? [...browserOpenCandidates(url), ...windowsFallbackCandidates(url)] : browserOpenCandidates(url);
27650
27661
  const launchEnv = browserLaunchEnv(debug);
27651
27662
  debug?.(
27652
27663
  `openInBrowser: platform=${process.platform} DISPLAY=${launchEnv.DISPLAY ?? "<unset>"} WAYLAND=${launchEnv.WAYLAND_DISPLAY ?? "<unset>"} DBUS=${launchEnv.DBUS_SESSION_BUS_ADDRESS ? "set" : "<unset>"} candidates=[${candidates.map((c) => c.cmd).join(", ")}]`
@@ -27655,14 +27666,43 @@ async function openInBrowser(url, debug) {
27655
27666
  for (const { cmd, args } of candidates) {
27656
27667
  try {
27657
27668
  await new Promise((resolve, reject) => {
27658
- const spawnOpts = { stdio: "ignore", detached: true, env: launchEnv };
27659
- if (process.platform === "win32") spawnOpts.windowsVerbatimArguments = true;
27669
+ const isWin = process.platform === "win32";
27670
+ const spawnOpts = { stdio: "ignore", detached: !isWin, env: launchEnv };
27671
+ if (isWin) spawnOpts.windowsVerbatimArguments = true;
27660
27672
  const child = spawn3(cmd, args, spawnOpts);
27661
27673
  child.on("error", reject);
27662
- child.on("spawn", () => {
27663
- debug?.(`spawn OK: ${cmd} (pid=${child.pid})`);
27674
+ if (!isWin) {
27675
+ child.on("spawn", () => {
27676
+ debug?.(`spawn OK: ${cmd} (pid=${child.pid})`);
27677
+ child.unref();
27678
+ resolve();
27679
+ });
27680
+ return;
27681
+ }
27682
+ const isCmd = /cmd(\.exe)?$/i.test(cmd);
27683
+ const budgetMs = isCmd ? 800 : 1200;
27684
+ let settled = false;
27685
+ const finish = (fn) => {
27686
+ if (settled) return;
27687
+ settled = true;
27688
+ fn();
27689
+ };
27690
+ const timer = setTimeout(() => {
27691
+ debug?.(`exit-wait timeout (${budgetMs}ms): ${cmd} \u2014 assuming launched`);
27664
27692
  child.unref();
27665
- resolve();
27693
+ finish(resolve);
27694
+ }, budgetMs);
27695
+ timer.unref?.();
27696
+ child.on("spawn", () => debug?.(`spawn OK: ${cmd} (pid=${child.pid}) \u2014 awaiting exit`));
27697
+ child.on("exit", (code) => {
27698
+ clearTimeout(timer);
27699
+ if (code === 0 || code === null) {
27700
+ debug?.(`exit ${code}: ${cmd} \u2014 launched OK`);
27701
+ finish(resolve);
27702
+ } else {
27703
+ debug?.(`exit ${code}: ${cmd} \u2014 treating as failed, next candidate`);
27704
+ finish(() => reject(new Error(`${cmd} exited ${code}`)));
27705
+ }
27666
27706
  });
27667
27707
  });
27668
27708
  return;
@@ -27767,7 +27807,7 @@ var OAUTH_BASE_URLS = {
27767
27807
  fr: "https://staging.api.leadbay.app"
27768
27808
  }
27769
27809
  };
27770
- var VERSION = "0.23.10";
27810
+ var VERSION = "0.23.11";
27771
27811
  var HELP = `
27772
27812
  leadbay-mcp ${VERSION} \u2014 Leadbay Model Context Protocol server
27773
27813
 
@@ -29044,11 +29084,11 @@ async function main() {
29044
29084
  }
29045
29085
  }
29046
29086
  if (browserOpenInFlight) {
29047
- bootstrapDebug(`shutdown(code=${code}) browser-open still in flight \u2014 waiting up to 1.5s`);
29087
+ bootstrapDebug(`shutdown(code=${code}) browser-open still in flight \u2014 waiting up to 3s`);
29048
29088
  try {
29049
29089
  await Promise.race([
29050
29090
  browserOpenInFlight,
29051
- new Promise((r) => setTimeout(r, 1500))
29091
+ new Promise((r) => setTimeout(r, 3e3))
29052
29092
  ]);
29053
29093
  } catch {
29054
29094
  }
@@ -10529,12 +10529,12 @@ var login = {
10529
10529
  execute: async (client, params, ctx) => {
10530
10530
  const cleanPassword = params.password.replace(/\\(.)/g, "$1");
10531
10531
  const startWith = client.region === "fr" ? "fr" : "us";
10532
- ctx?.logger?.info?.(`LeadClaw login: email=${params.email} startRegion=${startWith}`);
10532
+ ctx?.logger?.info?.(`Leadbay login: email=${params.email} startRegion=${startWith}`);
10533
10533
  try {
10534
10534
  const result = await resolveRegion(params.email, cleanPassword, startWith);
10535
10535
  if (client.baseUrl !== result.baseUrl) {
10536
10536
  client.setBaseUrl(result.baseUrl, result.region);
10537
- ctx?.logger?.info?.(`LeadClaw login: switched to region=${result.region} (account is in the ${result.region.toUpperCase()} backend)`);
10537
+ ctx?.logger?.info?.(`Leadbay login: switched to region=${result.region} (account is in the ${result.region.toUpperCase()} backend)`);
10538
10538
  }
10539
10539
  client.setToken(result.token);
10540
10540
  client.prefetchOrgData().catch(() => {
@@ -10546,7 +10546,7 @@ var login = {
10546
10546
  verified: result.verified
10547
10547
  };
10548
10548
  } catch (err) {
10549
- ctx?.logger?.error?.(`LeadClaw login: failed: ${err?.message}`);
10549
+ ctx?.logger?.error?.(`Leadbay login: failed: ${err?.message}`);
10550
10550
  return {
10551
10551
  error: true,
10552
10552
  code: "LOGIN_FAILED",
@@ -23375,7 +23375,7 @@ function buildServer(client, opts = {}) {
23375
23375
  }
23376
23376
  const exposedNames = new Set(toolByName.keys());
23377
23377
  const server = new Server(
23378
- { name: "leadbay", version: opts.version ?? "0.0.0-dev" },
23378
+ { name: "leadbay-mcp", version: opts.version ?? "0.0.0-dev" },
23379
23379
  {
23380
23380
  capabilities: {
23381
23381
  tools: {},
@@ -24062,7 +24062,7 @@ function parseWriteEnv(env = process.env) {
24062
24062
  }
24063
24063
 
24064
24064
  // src/http-server.ts
24065
- var VERSION = true ? "0.23.10" : "0.0.0-dev";
24065
+ var VERSION = true ? "0.23.11" : "0.0.0-dev";
24066
24066
  var PORT = Number(process.env.PORT ?? 8080);
24067
24067
  var HOST = process.env.HOST ?? "0.0.0.0";
24068
24068
  var sseSessions = /* @__PURE__ */ new Map();
@@ -867,6 +867,17 @@ function browserOpenCandidates(url) {
867
867
  { cmd: "xdg-open", args: [url] }
868
868
  ];
869
869
  }
870
+ function windowsFallbackCandidates(url) {
871
+ const sysRoot = process.env.SystemRoot || process.env.windir || "C:\\Windows";
872
+ const psLiteral = `'${url.replace(/'/g, "''")}'`;
873
+ return [
874
+ { cmd: `${sysRoot}\\System32\\rundll32.exe`, args: ["url.dll,FileProtocolHandler", url] },
875
+ {
876
+ cmd: `${sysRoot}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`,
877
+ args: ["-NoProfile", "-NonInteractive", "-Command", "Start-Process", psLiteral]
878
+ }
879
+ ];
880
+ }
870
881
  function browserLaunchEnv(debug) {
871
882
  const env = { ...process.env };
872
883
  if (process.platform !== "linux") return env;
@@ -931,7 +942,7 @@ function browserLaunchEnv(debug) {
931
942
  return env;
932
943
  }
933
944
  async function openInBrowser(url, debug) {
934
- const candidates = browserOpenCandidates(url);
945
+ const candidates = process.platform === "win32" ? [...browserOpenCandidates(url), ...windowsFallbackCandidates(url)] : browserOpenCandidates(url);
935
946
  const launchEnv = browserLaunchEnv(debug);
936
947
  debug?.(
937
948
  `openInBrowser: platform=${process.platform} DISPLAY=${launchEnv.DISPLAY ?? "<unset>"} WAYLAND=${launchEnv.WAYLAND_DISPLAY ?? "<unset>"} DBUS=${launchEnv.DBUS_SESSION_BUS_ADDRESS ? "set" : "<unset>"} candidates=[${candidates.map((c) => c.cmd).join(", ")}]`
@@ -940,14 +951,43 @@ async function openInBrowser(url, debug) {
940
951
  for (const { cmd, args } of candidates) {
941
952
  try {
942
953
  await new Promise((resolve, reject) => {
943
- const spawnOpts = { stdio: "ignore", detached: true, env: launchEnv };
944
- if (process.platform === "win32") spawnOpts.windowsVerbatimArguments = true;
954
+ const isWin = process.platform === "win32";
955
+ const spawnOpts = { stdio: "ignore", detached: !isWin, env: launchEnv };
956
+ if (isWin) spawnOpts.windowsVerbatimArguments = true;
945
957
  const child = spawn3(cmd, args, spawnOpts);
946
958
  child.on("error", reject);
947
- child.on("spawn", () => {
948
- debug?.(`spawn OK: ${cmd} (pid=${child.pid})`);
959
+ if (!isWin) {
960
+ child.on("spawn", () => {
961
+ debug?.(`spawn OK: ${cmd} (pid=${child.pid})`);
962
+ child.unref();
963
+ resolve();
964
+ });
965
+ return;
966
+ }
967
+ const isCmd = /cmd(\.exe)?$/i.test(cmd);
968
+ const budgetMs = isCmd ? 800 : 1200;
969
+ let settled = false;
970
+ const finish = (fn) => {
971
+ if (settled) return;
972
+ settled = true;
973
+ fn();
974
+ };
975
+ const timer = setTimeout(() => {
976
+ debug?.(`exit-wait timeout (${budgetMs}ms): ${cmd} \u2014 assuming launched`);
949
977
  child.unref();
950
- resolve();
978
+ finish(resolve);
979
+ }, budgetMs);
980
+ timer.unref?.();
981
+ child.on("spawn", () => debug?.(`spawn OK: ${cmd} (pid=${child.pid}) \u2014 awaiting exit`));
982
+ child.on("exit", (code) => {
983
+ clearTimeout(timer);
984
+ if (code === 0 || code === null) {
985
+ debug?.(`exit ${code}: ${cmd} \u2014 launched OK`);
986
+ finish(resolve);
987
+ } else {
988
+ debug?.(`exit ${code}: ${cmd} \u2014 treating as failed, next candidate`);
989
+ finish(() => reject(new Error(`${cmd} exited ${code}`)));
990
+ }
951
991
  });
952
992
  });
953
993
  return;
@@ -1764,7 +1804,7 @@ var init_installer_gui = __esm({
1764
1804
  init_install_dxt();
1765
1805
  init_install_shared();
1766
1806
  init_oauth();
1767
- VERSION = true ? "0.23.10" : "0.0.0-dev";
1807
+ VERSION = true ? "0.23.11" : "0.0.0-dev";
1768
1808
  MESSAGES = {
1769
1809
  en: {
1770
1810
  installer: {
@@ -853,6 +853,17 @@ function browserOpenCandidates(url) {
853
853
  { cmd: "xdg-open", args: [url] }
854
854
  ];
855
855
  }
856
+ function windowsFallbackCandidates(url) {
857
+ const sysRoot = process.env.SystemRoot || process.env.windir || "C:\\Windows";
858
+ const psLiteral = `'${url.replace(/'/g, "''")}'`;
859
+ return [
860
+ { cmd: `${sysRoot}\\System32\\rundll32.exe`, args: ["url.dll,FileProtocolHandler", url] },
861
+ {
862
+ cmd: `${sysRoot}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`,
863
+ args: ["-NoProfile", "-NonInteractive", "-Command", "Start-Process", psLiteral]
864
+ }
865
+ ];
866
+ }
856
867
  function browserLaunchEnv(debug) {
857
868
  const env = { ...process.env };
858
869
  if (process.platform !== "linux") return env;
@@ -917,7 +928,7 @@ function browserLaunchEnv(debug) {
917
928
  return env;
918
929
  }
919
930
  async function openInBrowser(url, debug) {
920
- const candidates = browserOpenCandidates(url);
931
+ const candidates = process.platform === "win32" ? [...browserOpenCandidates(url), ...windowsFallbackCandidates(url)] : browserOpenCandidates(url);
921
932
  const launchEnv = browserLaunchEnv(debug);
922
933
  debug?.(
923
934
  `openInBrowser: platform=${process.platform} DISPLAY=${launchEnv.DISPLAY ?? "<unset>"} WAYLAND=${launchEnv.WAYLAND_DISPLAY ?? "<unset>"} DBUS=${launchEnv.DBUS_SESSION_BUS_ADDRESS ? "set" : "<unset>"} candidates=[${candidates.map((c) => c.cmd).join(", ")}]`
@@ -926,14 +937,43 @@ async function openInBrowser(url, debug) {
926
937
  for (const { cmd, args } of candidates) {
927
938
  try {
928
939
  await new Promise((resolve, reject) => {
929
- const spawnOpts = { stdio: "ignore", detached: true, env: launchEnv };
930
- if (process.platform === "win32") spawnOpts.windowsVerbatimArguments = true;
940
+ const isWin = process.platform === "win32";
941
+ const spawnOpts = { stdio: "ignore", detached: !isWin, env: launchEnv };
942
+ if (isWin) spawnOpts.windowsVerbatimArguments = true;
931
943
  const child = spawn3(cmd, args, spawnOpts);
932
944
  child.on("error", reject);
933
- child.on("spawn", () => {
934
- debug?.(`spawn OK: ${cmd} (pid=${child.pid})`);
945
+ if (!isWin) {
946
+ child.on("spawn", () => {
947
+ debug?.(`spawn OK: ${cmd} (pid=${child.pid})`);
948
+ child.unref();
949
+ resolve();
950
+ });
951
+ return;
952
+ }
953
+ const isCmd = /cmd(\.exe)?$/i.test(cmd);
954
+ const budgetMs = isCmd ? 800 : 1200;
955
+ let settled = false;
956
+ const finish = (fn) => {
957
+ if (settled) return;
958
+ settled = true;
959
+ fn();
960
+ };
961
+ const timer = setTimeout(() => {
962
+ debug?.(`exit-wait timeout (${budgetMs}ms): ${cmd} \u2014 assuming launched`);
935
963
  child.unref();
936
- resolve();
964
+ finish(resolve);
965
+ }, budgetMs);
966
+ timer.unref?.();
967
+ child.on("spawn", () => debug?.(`spawn OK: ${cmd} (pid=${child.pid}) \u2014 awaiting exit`));
968
+ child.on("exit", (code) => {
969
+ clearTimeout(timer);
970
+ if (code === 0 || code === null) {
971
+ debug?.(`exit ${code}: ${cmd} \u2014 launched OK`);
972
+ finish(resolve);
973
+ } else {
974
+ debug?.(`exit ${code}: ${cmd} \u2014 treating as failed, next candidate`);
975
+ finish(() => reject(new Error(`${cmd} exited ${code}`)));
976
+ }
937
977
  });
938
978
  });
939
979
  return;
@@ -1027,7 +1067,7 @@ async function oauthLogin(opts) {
1027
1067
  }
1028
1068
 
1029
1069
  // installer/installer-gui.ts
1030
- var VERSION = true ? "0.23.10" : "0.0.0-dev";
1070
+ var VERSION = true ? "0.23.11" : "0.0.0-dev";
1031
1071
  var MESSAGES = {
1032
1072
  en: {
1033
1073
  installer: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leadbay/mcp",
3
- "version": "0.23.10",
3
+ "version": "0.23.11",
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",