@browserbridge/bbx 1.6.4 → 1.7.1

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.
@@ -24,9 +24,10 @@ import {
24
24
  DEFAULT_DAEMON_PENDING_TIMEOUT_MS,
25
25
  DEFAULT_LOG_TAIL_LIMIT,
26
26
  ERROR_CODES,
27
+ getProtocolVersion,
28
+ getSupportedProtocolVersions,
27
29
  parseJsonLines,
28
- PROTOCOL_VERSION,
29
- SUPPORTED_VERSIONS,
30
+ setProtocolPackageVersion,
30
31
  validateBridgeRequest,
31
32
  } from '../../protocol/src/index.js';
32
33
  import {
@@ -45,6 +46,7 @@ import { normalizeDaemonLogger } from './daemon-logger.js';
45
46
  import { writeJsonLine } from './framing.js';
46
47
 
47
48
  const DAEMON_VERSION = loadDaemonVersion();
49
+ setProtocolPackageVersion(DAEMON_VERSION);
48
50
 
49
51
  /** @typedef {import('../../protocol/src/types.js').BridgeRequest} BridgeRequest */
50
52
  /** @typedef {import('../../protocol/src/types.js').SetupInstallParams} SetupInstallParams */
@@ -89,14 +91,15 @@ function compareProtocolVersions(left, right) {
89
91
  * @returns {{ supported_versions: readonly string[], deprecated_since?: string, migration_hint?: string }}
90
92
  */
91
93
  function getVersionNegotiationPayload(requestedVersion) {
92
- const latestSupported = SUPPORTED_VERSIONS[0];
93
- if (!requestedVersion || !latestSupported || SUPPORTED_VERSIONS.includes(requestedVersion)) {
94
- return { supported_versions: SUPPORTED_VERSIONS };
94
+ const supportedVersions = getSupportedProtocolVersions();
95
+ const latestSupported = supportedVersions[0];
96
+ if (!requestedVersion || !latestSupported || supportedVersions.includes(requestedVersion)) {
97
+ return { supported_versions: supportedVersions };
95
98
  }
96
99
 
97
100
  const localIsNewer = compareProtocolVersions(latestSupported, requestedVersion) > 0;
98
101
  return {
99
- supported_versions: SUPPORTED_VERSIONS,
102
+ supported_versions: supportedVersions,
100
103
  ...(localIsNewer ? { deprecated_since: latestSupported } : {}),
101
104
  migration_hint: localIsNewer
102
105
  ? `Browser Bridge daemon is newer than the client protocol ${requestedVersion}. Restart or update the Browser Bridge CLI/npm package to ${latestSupported} or later.`
@@ -638,8 +641,9 @@ export class BridgeDaemon {
638
641
  extensionConnected: false,
639
642
  socketPath: this.socketPath,
640
643
  transport: formatBridgeTransport(this.transport),
644
+ proxy: this.getProxyStatusPayload(),
641
645
  connectedExtensions: [],
642
- daemon_supported_versions: SUPPORTED_VERSIONS,
646
+ daemon_supported_versions: getSupportedProtocolVersions(),
643
647
  ...getVersionNegotiationPayload(request.meta?.protocol_version),
644
648
  });
645
649
  await writeJsonLine(socket, { type: 'agent.response', response });
@@ -878,6 +882,24 @@ export class BridgeDaemon {
878
882
  socket.__accessEnabled = accessEnabled;
879
883
  }
880
884
 
885
+ /**
886
+ * Structured remote-exposure status for health payloads. The proxy counts
887
+ * as enabled only when the daemon listens on a TCP address other than
888
+ * loopback, i.e. agents on other machines can reach it.
889
+ *
890
+ * @returns {{ enabled: boolean, endpoint: string | null }}
891
+ */
892
+ getProxyStatusPayload() {
893
+ if (this.transport.type !== 'tcp') {
894
+ return { enabled: false, endpoint: null };
895
+ }
896
+ const bindHost = this.transport.bindHost ?? this.transport.host;
897
+ const isLoopback = bindHost === '127.0.0.1' || bindHost === '::1' || bindHost === 'localhost';
898
+ return isLoopback
899
+ ? { enabled: false, endpoint: null }
900
+ : { enabled: true, endpoint: `${bindHost}:${this.transport.port}` };
901
+ }
902
+
881
903
  /**
882
904
  * @param {ClientSocket} socket
883
905
  * @param {DaemonMessage} message
@@ -929,7 +951,8 @@ export class BridgeDaemon {
929
951
  ...getVersionNegotiationPayload(pending.protocolVersion),
930
952
  .../** @type {Record<string, unknown>} */ (responseMessage.result),
931
953
  daemonVersion: DAEMON_VERSION,
932
- daemon_supported_versions: SUPPORTED_VERSIONS,
954
+ daemon_supported_versions: getSupportedProtocolVersions(),
955
+ proxy: this.getProxyStatusPayload(),
933
956
  },
934
957
  {
935
958
  ...responseMessage.meta,
@@ -1141,7 +1164,7 @@ export async function pingExistingDaemon(transport) {
1141
1164
  method: 'health.ping',
1142
1165
  tab_id: null,
1143
1166
  params: {},
1144
- meta: { protocol_version: PROTOCOL_VERSION, token_budget: null },
1167
+ meta: { protocol_version: getProtocolVersion(), token_budget: null },
1145
1168
  },
1146
1169
  })}\n`
1147
1170
  );
@@ -125,7 +125,7 @@ export async function runNativeHost({
125
125
  socket = await connectWithBootstrap(resolvedTransport);
126
126
  } catch (error) {
127
127
  await writeNativeMessageQueued({
128
- type: 'agent.response',
128
+ type: 'host.bridge_response',
129
129
  response: createFailure(
130
130
  'native_bootstrap',
131
131
  ERROR_CODES.NATIVE_HOST_UNAVAILABLE,
@@ -82,10 +82,59 @@ import { BRIDGE_METHODS, METHOD_SET, createBridgeMethodGroups } from './registry
82
82
  /** @typedef {import('./types.js').WaitForLoadStateParams} WaitForLoadStateParams */
83
83
  /** @typedef {import('./types.js').WaitForParams} WaitForParams */
84
84
 
85
- export const PROTOCOL_VERSION = '1.0';
85
+ /** @type {string | null} */
86
+ let protocolPackageVersion = null;
87
+
88
+ /**
89
+ * @param {string | null | undefined} packageVersion
90
+ * @returns {string}
91
+ */
92
+ export function deriveProtocolVersion(packageVersion) {
93
+ const match = typeof packageVersion === 'string' ? /^(\d+)\.(\d+)/.exec(packageVersion) : null;
94
+ return match ? `${match[1]}.${match[2]}` : '0.0';
95
+ }
96
+
97
+ /**
98
+ * @returns {string | null}
99
+ */
100
+ function getRuntimePackageVersion() {
101
+ const chromeRuntime = /** @type {{ runtime?: { getManifest?: () => { version?: unknown } } }} */ (
102
+ globalThis.chrome ?? {}
103
+ ).runtime;
104
+ const manifestVersion = chromeRuntime?.getManifest?.().version;
105
+ if (typeof manifestVersion === 'string') {
106
+ return manifestVersion;
107
+ }
108
+
109
+ const npmPackageVersion = globalThis.process?.env?.npm_package_version;
110
+ return typeof npmPackageVersion === 'string' ? npmPackageVersion : null;
111
+ }
112
+
113
+ /**
114
+ * @param {string | null | undefined} packageVersion
115
+ * @returns {void}
116
+ */
117
+ export function setProtocolPackageVersion(packageVersion) {
118
+ protocolPackageVersion = typeof packageVersion === 'string' ? packageVersion : null;
119
+ }
120
+
121
+ /**
122
+ * @returns {string}
123
+ */
124
+ export function getProtocolVersion() {
125
+ return deriveProtocolVersion(protocolPackageVersion ?? getRuntimePackageVersion());
126
+ }
127
+
128
+ /** Snapshot retained for older callers; new code should call getProtocolVersion(). */
129
+ export const PROTOCOL_VERSION = getProtocolVersion();
86
130
 
87
131
  /** Versions this build can speak. Newest first. */
88
- export const SUPPORTED_VERSIONS = Object.freeze(['1.0']);
132
+ export function getSupportedProtocolVersions() {
133
+ return Object.freeze([getProtocolVersion()]);
134
+ }
135
+
136
+ /** Snapshot retained for older callers; new code should call getSupportedProtocolVersions(). */
137
+ export const SUPPORTED_VERSIONS = getSupportedProtocolVersions();
89
138
 
90
139
  /**
91
140
  * Clamp a numeric value between min and max, falling back to a default.
@@ -122,7 +171,7 @@ export function createRequest({ id, method, tabId = null, params = {}, meta = {}
122
171
  tab_id: tabId,
123
172
  params,
124
173
  meta: {
125
- protocol_version: PROTOCOL_VERSION,
174
+ protocol_version: getProtocolVersion(),
126
175
  ...meta,
127
176
  },
128
177
  });
@@ -141,7 +190,7 @@ export function createSuccess(id, result, meta = {}) {
141
190
  result,
142
191
  error: null,
143
192
  meta: {
144
- protocol_version: PROTOCOL_VERSION,
193
+ protocol_version: getProtocolVersion(),
145
194
  ...meta,
146
195
  },
147
196
  };
@@ -168,7 +217,7 @@ export function createFailure(id, code, message, details = null, meta = {}) {
168
217
  ...(recovery && { recovery }),
169
218
  },
170
219
  meta: {
171
- protocol_version: PROTOCOL_VERSION,
220
+ protocol_version: getProtocolVersion(),
172
221
  ...meta,
173
222
  },
174
223
  };
@@ -238,7 +287,7 @@ export function validateBridgeRequest(request) {
238
287
  meta: {
239
288
  ...meta,
240
289
  protocol_version:
241
- typeof meta.protocol_version === 'string' ? meta.protocol_version : PROTOCOL_VERSION,
290
+ typeof meta.protocol_version === 'string' ? meta.protocol_version : getProtocolVersion(),
242
291
  token_budget: typeof meta.token_budget === 'number' ? meta.token_budget : null,
243
292
  source: meta.source === 'cli' || meta.source === 'mcp' ? meta.source : undefined,
244
293
  },
@@ -842,7 +891,7 @@ export function createRuntimeContext() {
842
891
  const methodGroups = createBridgeMethodGroups();
843
892
 
844
893
  return {
845
- v: PROTOCOL_VERSION,
894
+ v: getProtocolVersion(),
846
895
  budgets: {
847
896
  quick: {
848
897
  n: BUDGET_PRESETS.quick.maxNodes,
@@ -1,6 +1,11 @@
1
1
  // @ts-check
2
2
 
3
- import { estimateSerializedPayloadCost, getCostClass, serializeJsonPayload } from './index.js';
3
+ import {
4
+ estimateSerializedPayloadCost,
5
+ getCostClass,
6
+ getProtocolVersion,
7
+ serializeJsonPayload,
8
+ } from './index.js';
4
9
 
5
10
  /** @typedef {import('./types.js').BridgeResponse} BridgeResponse */
6
11
  /** @typedef {import('./types.js').BridgeMethod} SummaryMethod */
@@ -692,7 +697,7 @@ export function summarizeBatchErrorItem({ method, tabId, error, durationMs }) {
692
697
  message,
693
698
  details: null,
694
699
  },
695
- meta: { protocol_version: '1.0' },
700
+ meta: { protocol_version: getProtocolVersion() },
696
701
  });
697
702
  const summary = annotateBridgeSummary(summarizeBridgeResponse(response, method), response);
698
703
  return {
@@ -34,6 +34,8 @@ bbx tab-activate <tabId> # bring a tab to the foreground
34
34
  bbx skill # live runtime presets + limits
35
35
  ```
36
36
 
37
+ Use the globally installed `bbx` command or `node packages/agent-client/src/cli.js ...` for live readiness checks from this repository. Avoid `rtk npm exec -- bbx status` as a health signal: that path can restart/replace the daemon and report `Extension: disconnected` before Chrome reconnects. If it happens, wait a few seconds and verify with `bbx status`.
38
+
37
39
  ### Inspect & Find
38
40
 
39
41
  ```bash
@@ -89,6 +91,19 @@ bbx screenshot <ref> [outPath] # capture partial element screenshot
89
91
  bbx call screenshot.capture_full_page '{}' # raw base64; avoid unless document context matters
90
92
  ```
91
93
 
94
+ ### Remote Destinations
95
+
96
+ Every bridge command accepts `--remote <name>` (or `BBX_REMOTE=<name>` env) to target a browser on another machine registered with `bbx remote add`:
97
+
98
+ ```bash
99
+ bbx remote list # configured remote destinations
100
+ bbx remote test <name> # ping a remote through the proxy
101
+ bbx tabs --remote <name> # any bridge command works with --remote
102
+ bbx dom-query "#app" --remote <name>
103
+ ```
104
+
105
+ In MCP mode, pass `destinationId` on any browser tool call instead; `browser_status` lists all destinations with reachability. Only use remotes the user has configured; local remains the default.
106
+
92
107
  ## Access Flow
93
108
 
94
109
  Browser Bridge access is window-scoped. The user turns it on once for the current browser window in the popup or side panel.
@@ -348,7 +348,7 @@ bbx call screenshot.capture_full_page '{}'
348
348
  "tab_id": 123,
349
349
  "method": "dom.query",
350
350
  "params": {},
351
- "meta": { "protocol_version": "1.0", "token_budget": 1200 }
351
+ "meta": { "protocol_version": "<package major.minor>", "token_budget": 1200 }
352
352
  }
353
353
  ```
354
354