chrome-bridge-mcp 1.23.3 → 1.23.4

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.
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Esiti dei comandi che prima si fidavano di un valore di ripiego.
3
+ *
4
+ * Due casi osservati nel test Windows del 27/09/2026, validi su ogni OS:
5
+ * - `click #checkout-button` con `query_dom` a count 0 rispondeva
6
+ * `{"clicked":true}` (type_text, hover e press_key allo stesso modo): la
7
+ * funzione iniettata lancia «Element not found», `executeScript` risolve
8
+ * con `result` undefined e il ripiego `?? { clicked: true }` lo
9
+ * trasformava in un successo.
10
+ * - `navigate` verso una porta chiusa rispondeva `{url, title}` senza errore:
11
+ * la scheda mostra la pagina d'errore di Chrome, che arriva comunque a
12
+ * status 'complete'. L'errore vero passa solo da webNavigation.onErrorOccurred.
13
+ */
14
+
15
+ /**
16
+ * Risultato di click, type_text, hover e press_key: senza un risultato della
17
+ * funzione iniettata il comando non è avvenuto, qualunque sia il motivo. Tutte
18
+ * e quattro rispondevano con un successo di ripiego ({clicked|typed|hovered|
19
+ * pressed: true}) su un selettore che non trova niente.
20
+ */
21
+ export function elementOutcome(results, selector) {
22
+ const first = results?.[0];
23
+ if (first?.result != null) return first.result;
24
+ if (first?.error?.message) throw new Error(first.error.message);
25
+ if (!selector) throw new Error('The page returned no result for the command');
26
+ throw new Error(`Element not found: ${selector} (no element matches; check it with query_dom)`);
27
+ }
28
+
29
+ /**
30
+ * net::ERR_ABORTED non è un fallimento da riferire: è un download, una
31
+ * risposta 204 o una navigazione sostituita da un'altra.
32
+ */
33
+ export function isReportableNavError(details) {
34
+ return details?.frameId === 0 && !!details.error && details.error !== 'net::ERR_ABORTED';
35
+ }
36
+
37
+ /**
38
+ * Raccoglie gli errori di rete del frame principale, per scheda, dal momento
39
+ * della chiamata. Va aperta PRIMA di tabs.create/tabs.update: un rifiuto di
40
+ * connessione in locale arriva in pochi millisecondi, prima che l'id della
41
+ * scheda nuova sia noto.
42
+ */
43
+ export function watchNavErrors(onErrorOccurred) {
44
+ const byTab = new Map();
45
+ const listener = (details) => {
46
+ if (isReportableNavError(details)) byTab.set(details.tabId, details);
47
+ };
48
+ onErrorOccurred.addListener(listener);
49
+ return {
50
+ errorFor: (tabId) => byTab.get(tabId) ?? null,
51
+ stop: () => onErrorOccurred.removeListener(listener),
52
+ };
53
+ }
54
+
55
+ export function navErrorMessage(details, tabId) {
56
+ return `${details.error} loading ${details.url} (tab ${tabId} shows the browser's error page)`;
57
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "Chrome Bridge for Claude Code",
4
- "version": "1.23.0",
4
+ "version": "1.23.4",
5
5
  "description": "Bridge your browser to Claude Code: 60 token-efficient web-dev automation tools over a local WebSocket. ChromeOS included.",
6
6
  "homepage_url": "https://github.com/frsorrentino/chrome-bridge",
7
7
  "minimum_chrome_version": "135",
@@ -12,6 +12,7 @@ import { createPacer } from './lib/capture-pacing.js';
12
12
  import { computeTiles } from './lib/tile-layout.js';
13
13
  import { classifyDownload } from './lib/download-state.js';
14
14
  import { findTextInPage } from './lib/find-text.js';
15
+ import { elementOutcome, watchNavErrors, navErrorMessage } from './lib/command-outcome.js';
15
16
  const { pushError } = globalThis.__cbTelemetry;
16
17
 
17
18
  const DEFAULT_PORT = 8765;
@@ -779,29 +780,40 @@ async function cmdGetTabs({ include_windows = false, ended } = {}) {
779
780
  async function cmdNavigate({ url, tab_id }) {
780
781
  if (!url) throw new Error('Missing required parameter: url');
781
782
 
782
- // Senza tab_id esplicito: apri SEMPRE in una nuova scheda (attiva) per non
783
- // sovrascrivere la scheda/sessione corrente dell'utente. Con tab_id: naviga
784
- // quella scheda specifica.
785
- if (!tab_id) {
786
- const newTab = await chrome.tabs.create({ url, active: true });
787
- // Se la pagina è già 'complete' (es. cache) non aspettare il listener
788
- if (newTab.status !== 'complete') {
789
- await waitForComplete(newTab.id);
783
+ // La pagina d'errore di Chrome (connessione rifiutata, DNS) arriva a
784
+ // 'complete' come una pagina vera: l'errore si legge solo qui.
785
+ const navErrors = watchNavErrors(chrome.webNavigation.onErrorOccurred);
786
+ try {
787
+ // Senza tab_id esplicito: apri SEMPRE in una nuova scheda (attiva) per non
788
+ // sovrascrivere la scheda/sessione corrente dell'utente. Con tab_id: naviga
789
+ // quella scheda specifica.
790
+ if (!tab_id) {
791
+ const newTab = await chrome.tabs.create({ url, active: true });
792
+ // Se la pagina è già 'complete' (es. cache) non aspettare il listener
793
+ if (newTab.status !== 'complete') {
794
+ await waitForComplete(newTab.id);
795
+ }
796
+ const failed = navErrors.errorFor(newTab.id);
797
+ if (failed) throw new Error(navErrorMessage(failed, newTab.id));
798
+ const createdTab = await chrome.tabs.get(newTab.id);
799
+ return { url: createdTab.url, title: createdTab.title, tabId: newTab.id };
790
800
  }
791
- const createdTab = await chrome.tabs.get(newTab.id);
792
- return { url: createdTab.url, title: createdTab.title, tabId: newTab.id };
793
- }
794
801
 
795
- const tabId = await resolveTabId(tab_id);
802
+ const tabId = await resolveTabId(tab_id);
796
803
 
797
- // Registra il listener PRIMA di tabs.update: una navigazione veloce (cache)
798
- // può emettere 'complete' prima che il listener esista
799
- const done = waitForComplete(tabId);
800
- await chrome.tabs.update(tabId, { url });
801
- await done;
804
+ // Registra il listener PRIMA di tabs.update: una navigazione veloce (cache)
805
+ // può emettere 'complete' prima che il listener esista
806
+ const done = waitForComplete(tabId);
807
+ await chrome.tabs.update(tabId, { url });
808
+ await done;
802
809
 
803
- const updatedTab = await chrome.tabs.get(tabId);
804
- return { url: updatedTab.url, title: updatedTab.title, tabId };
810
+ const failed = navErrors.errorFor(tabId);
811
+ if (failed) throw new Error(navErrorMessage(failed, tabId));
812
+ const updatedTab = await chrome.tabs.get(tabId);
813
+ return { url: updatedTab.url, title: updatedTab.title, tabId };
814
+ } finally {
815
+ navErrors.stop();
816
+ }
805
817
  }
806
818
 
807
819
  async function cmdScreenshot({ tab_id }) {
@@ -907,7 +919,7 @@ async function cmdClick({ selector, tab_id, frame_id, force = false, button = 'l
907
919
  world: 'MAIN',
908
920
  });
909
921
 
910
- return results?.[0]?.result ?? { clicked: true };
922
+ return elementOutcome(results, selector);
911
923
  }
912
924
 
913
925
  async function cmdTypeText({ selector, text, mode = 'set', tab_id, frame_id }) {
@@ -982,7 +994,7 @@ async function cmdTypeText({ selector, text, mode = 'set', tab_id, frame_id }) {
982
994
  world: 'MAIN',
983
995
  });
984
996
 
985
- return results?.[0]?.result ?? { typed: true };
997
+ return elementOutcome(results, selector);
986
998
  }
987
999
 
988
1000
  // Impronta per page_changed: pochi interi letti nell'ISOLATED world (nessuna
@@ -3331,7 +3343,7 @@ async function cmdHover({ selector, tab_id, frame_id }) {
3331
3343
  args: [selector],
3332
3344
  world: 'MAIN',
3333
3345
  });
3334
- return results?.[0]?.result ?? { hovered: true };
3346
+ return elementOutcome(results, selector);
3335
3347
  }
3336
3348
 
3337
3349
  // --- press_key ---
@@ -3398,7 +3410,7 @@ async function cmdPressKey({ key, selector, ctrl = false, shift = false, alt = f
3398
3410
  args: [key, selector || null, { ctrl, shift, alt, meta }],
3399
3411
  world: 'MAIN',
3400
3412
  });
3401
- return results?.[0]?.result ?? { pressed: true };
3413
+ return elementOutcome(results, selector);
3402
3414
  }
3403
3415
 
3404
3416
  // --- get_frames ---
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "chrome-bridge-mcp",
3
3
  "mcpName": "io.github.frsorrentino/chrome-bridge",
4
- "version": "1.23.3",
4
+ "version": "1.23.4",
5
5
  "type": "module",
6
6
  "description": "Chrome Bridge by frsorrentino: MCP server that lets Claude Code borrow a tab from your own logged-in Chrome and hand it back — logins, 2FA and CAPTCHAs stay with you (handoff). 60 tools; about half the cost of Claude in Chrome, measured; ChromeOS included.",
7
7
  "main": "server/index.js",
@@ -93,7 +93,7 @@ export const MessageType = Object.freeze({
93
93
  });
94
94
 
95
95
  // Versione
96
- export const VERSION = '1.23.3';
96
+ export const VERSION = '1.23.4';
97
97
 
98
98
  // Configurazione
99
99
  export const DEFAULT_PORT = 8765;