@browserbridge/bbx 1.6.2 → 1.6.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@browserbridge/bbx",
3
- "version": "1.6.2",
3
+ "version": "1.6.4",
4
4
  "private": false,
5
5
  "keywords": [
6
6
  "agent-tools",
@@ -973,15 +973,20 @@ function getProxyExampleHost(bindHost) {
973
973
  return bindHost;
974
974
  }
975
975
 
976
- for (const entries of Object.values(os.networkInterfaces())) {
976
+ /** @type {string | null} */
977
+ let firstIpv4 = null;
978
+ for (const [name, entries] of Object.entries(os.networkInterfaces())) {
977
979
  for (const entry of entries ?? []) {
978
980
  if (!entry.internal && entry.family === 'IPv4') {
979
- return entry.address;
981
+ if (name.toLowerCase() === 'ethernet 0') {
982
+ return entry.address;
983
+ }
984
+ firstIpv4 ??= entry.address;
980
985
  }
981
986
  }
982
987
  }
983
988
 
984
- return '<host>';
989
+ return firstIpv4 ?? '<host>';
985
990
  }
986
991
 
987
992
  /**
@@ -132,6 +132,67 @@ export function summarizeToolError(error) {
132
132
  );
133
133
  }
134
134
 
135
+ /**
136
+ * @param {ToolResult} result
137
+ * @returns {boolean}
138
+ */
139
+ function shouldTryRemoteFallback(result) {
140
+ if (!result.isError && result.structuredContent.ok !== false) {
141
+ return false;
142
+ }
143
+ const text = result.content[0]?.text ?? '';
144
+ return /\b(ACCESS_DENIED|DAEMON_OFFLINE|EXTENSION_DISCONNECTED|NATIVE_HOST_UNAVAILABLE|CONNECTION_LOST)\b|\b(ENOENT|ECONNREFUSED|ETIMEDOUT)\b|ERROR: connect\b/u.test(
145
+ text
146
+ );
147
+ }
148
+
149
+ /**
150
+ * @param {ToolResult} result
151
+ * @param {string} destinationId
152
+ * @returns {ToolResult}
153
+ */
154
+ function annotateAutoDestination(result, destinationId) {
155
+ return {
156
+ ...result,
157
+ structuredContent: {
158
+ ...result.structuredContent,
159
+ destinationId,
160
+ autoSelectedDestinationId: destinationId,
161
+ },
162
+ };
163
+ }
164
+
165
+ /**
166
+ * @param {(client: import('../../agent-client/src/client.js').BridgeClient) => Promise<ToolResult>} callback
167
+ * @param {ToolResult} localResult
168
+ * @returns {Promise<ToolResult>}
169
+ */
170
+ async function tryRemoteFallback(callback, localResult) {
171
+ const remotes = (await listBridgeDestinations()).filter((destination) => !destination.local);
172
+ if (remotes.length === 0) {
173
+ return localResult;
174
+ }
175
+
176
+ let lastFallbackResult = localResult;
177
+ for (const remote of remotes) {
178
+ const client = await createBridgeClientForDestination(remote.id);
179
+ try {
180
+ await client.connect();
181
+ const result = annotateAutoDestination(await callback(client), remote.id);
182
+ lastFallbackResult = result;
183
+ if (!shouldTryRemoteFallback(result)) {
184
+ return result;
185
+ }
186
+ } catch (error) {
187
+ lastFallbackResult = annotateAutoDestination(summarizeToolError(error), remote.id);
188
+ } finally {
189
+ await client.close();
190
+ }
191
+ }
192
+
193
+ return lastFallbackResult;
194
+ }
195
+
135
196
  /**
136
197
  * @param {(client: import('../../agent-client/src/client.js').BridgeClient) => Promise<ToolResult>} callback
137
198
  * @param {{ destinationId?: string | null }} [options]
@@ -140,7 +201,8 @@ export function summarizeToolError(error) {
140
201
  export async function withToolClient(callback, options = {}) {
141
202
  try {
142
203
  if (!options.destinationId) {
143
- return await withBridgeClient(callback);
204
+ const result = await withBridgeClient(callback);
205
+ return shouldTryRemoteFallback(result) ? await tryRemoteFallback(callback, result) : result;
144
206
  }
145
207
  const client = await createBridgeClientForDestination(options.destinationId);
146
208
  await client.connect();
@@ -67,10 +67,31 @@ export {
67
67
  const HOME_DIR = os.homedir();
68
68
 
69
69
  /**
70
+ * @param {{ destinationId?: string }} [args]
70
71
  * @returns {Promise<ToolResult>}
71
72
  */
72
- export async function handleStatusTool() {
73
+ export async function handleStatusTool(args = {}) {
73
74
  try {
75
+ const requestedDestinationId =
76
+ typeof args.destinationId === 'string' ? args.destinationId : null;
77
+ if (requestedDestinationId && requestedDestinationId !== 'local') {
78
+ const destination = (await getBridgeDestinations()).find(
79
+ (entry) => entry.id === requestedDestinationId
80
+ );
81
+ const result = await callRemoteHealth(requestedDestinationId);
82
+ return createToolResult(
83
+ result.reachable
84
+ ? `Browser Bridge destination "${requestedDestinationId}" is reachable.`
85
+ : `Browser Bridge destination "${requestedDestinationId}" is not reachable.`,
86
+ {
87
+ ok: result.reachable === true,
88
+ destination: destination ?? { id: requestedDestinationId, local: false },
89
+ ...result,
90
+ },
91
+ result.reachable !== true
92
+ );
93
+ }
94
+
74
95
  const report = await getDoctorReport();
75
96
  const destinations = await Promise.all(
76
97
  (await getBridgeDestinations()).map(async (destination) => {
@@ -235,6 +235,9 @@ export function applyWindowsTcpTransportDefaults(env = process.env) {
235
235
  if (env[BRIDGE_TCP_PORT_ENV] != null && env[BRIDGE_TCP_PORT_ENV] !== '') {
236
236
  return false;
237
237
  }
238
+ if (readProxyConfig(env)) {
239
+ return false;
240
+ }
238
241
  if (env[BRIDGE_HOME_ENV]) {
239
242
  return false;
240
243
  }