@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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@browserbridge/bbx",
3
- "version": "1.6.4",
3
+ "version": "1.7.1",
4
4
  "private": false,
5
5
  "keywords": [
6
6
  "agent-tools",
@@ -9,21 +9,23 @@ import { fileURLToPath } from 'node:url';
9
9
 
10
10
  import {
11
11
  applyWindowsTcpTransportDefaults,
12
+ createTcpBridgeTransport,
12
13
  getProxyConfigPath,
13
14
  readProxyConfig,
14
15
  SUPPORTED_BROWSERS,
15
16
  } from '../../native-host/src/config.js';
16
17
  import { writeBridgeAuthToken } from '../../native-host/src/auth-token.js';
18
+ import { pingExistingDaemon } from '../../native-host/src/daemon.js';
17
19
  import { restartBridgeDaemon } from '../../native-host/src/daemon-process.js';
18
20
  import { uninstallNativeManifest } from '../../native-host/src/install-manifest.js';
19
21
  import {
20
22
  createRuntimeContext,
23
+ getProtocolVersion,
21
24
  METHODS,
22
25
  summarizeBatchErrorItem,
23
26
  summarizeBatchResponseItem,
24
27
  } from '../../protocol/src/index.js';
25
28
  import { startBridgeMcpServer } from '../../mcp-server/src/server.js';
26
- import { BridgeClient } from './client.js';
27
29
  import { CLI_HELP_SECTIONS, SHORTCUT_COMMANDS } from './command-registry.js';
28
30
  import {
29
31
  interactiveCheckbox,
@@ -59,6 +61,7 @@ import {
59
61
  parseRemoteEndpoint,
60
62
  readRemoteConfig,
61
63
  removeRemoteDestination,
64
+ resolveProxyEnableSettings,
62
65
  } from './remotes.js';
63
66
  import { collectSetupStatus } from './setup-status.js';
64
67
  import { annotateBridgeSummary, summarizeBridgeResponse } from './subagent.js';
@@ -69,6 +72,32 @@ import { annotateBridgeSummary, summarizeBridgeResponse } from './subagent.js';
69
72
  const REQUEST_SOURCE = 'cli';
70
73
  const TEST_TIMEOUT_ENV = 'BBX_CLIENT_REQUEST_TIMEOUT_MS';
71
74
  const TEST_DETECTED_MCP_CLIENTS_ENV = 'BBX_TEST_DETECTED_MCP_CLIENTS';
75
+ const REMOTE_ENV = 'BBX_REMOTE';
76
+
77
+ /**
78
+ * Commands that only make sense against the local machine (setup, daemon
79
+ * lifecycle, and config management). The explicit --remote flag is rejected
80
+ * for these; the ambient BBX_REMOTE env var is ignored.
81
+ *
82
+ * @type {ReadonlySet<string>}
83
+ */
84
+ const LOCAL_ONLY_COMMANDS = new Set([
85
+ 'help',
86
+ '--help',
87
+ '-h',
88
+ '--version',
89
+ '-v',
90
+ 'skill',
91
+ 'install',
92
+ 'uninstall',
93
+ 'install-skill',
94
+ 'install-mcp',
95
+ 'mcp',
96
+ 'proxy',
97
+ 'remote',
98
+ 'doctor',
99
+ 'restart',
100
+ ]);
72
101
 
73
102
  /**
74
103
  * Read all of stdin as UTF-8 text. Resolves once stdin closes.
@@ -89,7 +118,18 @@ function readStdin() {
89
118
  });
90
119
  }
91
120
 
92
- const [, , command, ...rest] = process.argv;
121
+ const remoteFlag = extractRemoteFlag(process.argv.slice(2));
122
+ const [command, ...rest] = remoteFlag.rest;
123
+
124
+ if (remoteFlag.explicit && command && LOCAL_ONLY_COMMANDS.has(command)) {
125
+ process.stderr.write(`The --remote flag is not supported with "${command}".\n`);
126
+ process.exit(1);
127
+ }
128
+
129
+ const remoteDestinationId =
130
+ command && LOCAL_ONLY_COMMANDS.has(command)
131
+ ? null
132
+ : (remoteFlag.remoteId ?? process.env[REMOTE_ENV] ?? null);
93
133
 
94
134
  if (!command || ['help', '--help', '-h'].includes(command)) {
95
135
  printUsage();
@@ -396,23 +436,60 @@ if (command === 'mcp') {
396
436
  }
397
437
 
398
438
  if (command === 'proxy') {
399
- await handleProxyCommand(rest);
400
- process.exit(0);
439
+ await runLocalCommand(() => handleProxyCommand(rest));
440
+ process.exit();
401
441
  }
402
442
 
403
443
  if (command === 'remote') {
404
- await handleRemoteCommand(rest);
405
- process.exit(0);
444
+ await runLocalCommand(() => handleRemoteCommand(rest));
445
+ process.exit();
406
446
  }
407
447
 
408
448
  const clientTimeoutMs = getClientTimeoutOverride();
409
449
  applyWindowsTcpTransportDefaults();
410
- const client = new BridgeClient(
411
- clientTimeoutMs ? { defaultTimeoutMs: clientTimeoutMs } : undefined
412
- );
450
+ const client = await createCliClient();
413
451
 
414
452
  await main();
415
453
 
454
+ /**
455
+ * Run a local (pre-main) command, converting thrown errors into a friendly
456
+ * one-line message instead of an unhandled stack trace.
457
+ *
458
+ * @param {() => Promise<void>} run
459
+ * @returns {Promise<void>}
460
+ */
461
+ async function runLocalCommand(run) {
462
+ try {
463
+ await run();
464
+ } catch (error) {
465
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
466
+ process.exit(1);
467
+ }
468
+ }
469
+
470
+ /**
471
+ * Create the bridge client for this invocation, honoring the --remote flag
472
+ * and BBX_REMOTE env var. Exits with a friendly error when the destination
473
+ * is not configured.
474
+ *
475
+ * @returns {Promise<import('./client.js').BridgeClient>}
476
+ */
477
+ async function createCliClient() {
478
+ try {
479
+ return await createBridgeClientForDestination(
480
+ remoteDestinationId,
481
+ clientTimeoutMs ? { defaultTimeoutMs: clientTimeoutMs } : {}
482
+ );
483
+ } catch (error) {
484
+ printJson({
485
+ ok: false,
486
+ summary: `ERROR: ${error instanceof Error ? error.message : String(error)}`,
487
+ evidence: null,
488
+ });
489
+ process.exit(1);
490
+ }
491
+ }
492
+
416
493
  async function main() {
417
494
  try {
418
495
  if (command === 'status') {
@@ -552,7 +629,7 @@ async function main() {
552
629
  evidence: null,
553
630
  durationMs: 0,
554
631
  approxTokens: 0,
555
- meta: { protocol_version: '1.0' },
632
+ meta: { protocol_version: getProtocolVersion() },
556
633
  error: {
557
634
  code: 'INVALID_REQUEST',
558
635
  message: 'Each batch call needs a method.',
@@ -569,7 +646,7 @@ async function main() {
569
646
  evidence: null,
570
647
  durationMs: 0,
571
648
  approxTokens: 0,
572
- meta: { protocol_version: '1.0' },
649
+ meta: { protocol_version: getProtocolVersion() },
573
650
  error: {
574
651
  code: 'INVALID_REQUEST',
575
652
  message: `Unknown bridge method "${call.method}".`,
@@ -859,7 +936,12 @@ async function handleProxyCommand(args) {
859
936
  const [subcommand, ...restArgs] = args;
860
937
  if (subcommand === 'enable') {
861
938
  const options = parseProxyEnableArgs(restArgs);
862
- const token = options.token ?? randomUUID();
939
+ const existing = readProxyConfig();
940
+ const { port, bindHost, token, tokenSource } = resolveProxyEnableSettings(
941
+ existing,
942
+ options,
943
+ randomUUID
944
+ );
863
945
  await writeBridgeAuthToken(token);
864
946
  const configPath = getProxyConfigPath();
865
947
  await fs.promises.mkdir(path.dirname(configPath), { recursive: true });
@@ -868,8 +950,8 @@ async function handleProxyCommand(args) {
868
950
  `${JSON.stringify(
869
951
  {
870
952
  enabled: true,
871
- port: options.port,
872
- bindHost: options.bindHost,
953
+ port,
954
+ bindHost,
873
955
  token,
874
956
  },
875
957
  null,
@@ -878,14 +960,22 @@ async function handleProxyCommand(args) {
878
960
  { encoding: 'utf8', mode: 0o600 }
879
961
  );
880
962
  const result = await restartBridgeDaemon();
881
- const exampleHost = getProxyExampleHost(options.bindHost);
882
- process.stdout.write(
883
- [
884
- `Browser Bridge proxy enabled on ${options.bindHost}:${options.port}.`,
885
- '',
886
- `Token: ${token}`,
887
- `Config: ${configPath}`,
888
- `Daemon: ${result.previouslyRunning ? 'restarted' : 'started'} (pid ${result.pid})`,
963
+ const exampleHost = getProxyExampleHost(bindHost);
964
+ const tokenNote =
965
+ tokenSource === 'existing'
966
+ ? ' (unchanged — already-configured clients keep working; pass --rotate-token to generate a new secret)'
967
+ : tokenSource === 'generated' && existing
968
+ ? ' (rotated — update every configured client with `bbx remote add`)'
969
+ : '';
970
+ const lines = [
971
+ `Browser Bridge proxy enabled on ${bindHost}:${port}.`,
972
+ '',
973
+ `Token: ${token}${tokenNote}`,
974
+ `Config: ${configPath}`,
975
+ `Daemon: ${result.previouslyRunning ? 'restarted' : 'started'} (pid ${result.pid})`,
976
+ ];
977
+ if (tokenSource !== 'existing') {
978
+ lines.push(
889
979
  '',
890
980
  'On your dev machine, add this remote:',
891
981
  '',
@@ -893,10 +983,11 @@ async function handleProxyCommand(args) {
893
983
  '',
894
984
  'Example:',
895
985
  '',
896
- `bbx remote add remote-bbx ${exampleHost}:${options.port} --token ${token}`,
897
- '',
898
- ].join('\n')
899
- );
986
+ `bbx remote add remote-bbx ${exampleHost}:${port} --token ${token}`
987
+ );
988
+ }
989
+ lines.push('');
990
+ process.stdout.write(lines.join('\n'));
900
991
  return;
901
992
  }
902
993
 
@@ -916,10 +1007,12 @@ async function handleProxyCommand(args) {
916
1007
  process.stdout.write('Browser Bridge proxy is disabled.\n');
917
1008
  return;
918
1009
  }
1010
+ const reachable = await pingExistingDaemon(createTcpBridgeTransport(config.port, '127.0.0.1'));
919
1011
  process.stdout.write(
920
1012
  [
921
1013
  `Browser Bridge proxy is enabled on ${config.bindHost}:${config.port}.`,
922
1014
  `Config: ${getProxyConfigPath()}`,
1015
+ `Daemon: ${reachable ? 'reachable' : 'not reachable'} on 127.0.0.1:${config.port}`,
923
1016
  '',
924
1017
  ].join('\n')
925
1018
  );
@@ -930,12 +1023,15 @@ async function handleProxyCommand(args) {
930
1023
  }
931
1024
 
932
1025
  /**
1026
+ * Parse `bbx proxy enable` flags. Values stay undefined when not passed so an
1027
+ * existing proxy config can be reused instead of silently reset to defaults.
1028
+ *
933
1029
  * @param {string[]} args
934
- * @returns {{ port: number, bindHost: string, token?: string }}
1030
+ * @returns {import('./remotes.js').ProxyEnableOptions}
935
1031
  */
936
1032
  function parseProxyEnableArgs(args) {
937
- /** @type {{ port: number, bindHost: string, token?: string }} */
938
- const options = { port: 9223, bindHost: '0.0.0.0' };
1033
+ /** @type {import('./remotes.js').ProxyEnableOptions} */
1034
+ const options = {};
939
1035
  for (let index = 0; index < args.length; index += 1) {
940
1036
  const arg = args[index];
941
1037
  if (arg === '--port') {
@@ -956,9 +1052,16 @@ function parseProxyEnableArgs(args) {
956
1052
  }
957
1053
  continue;
958
1054
  }
1055
+ if (arg === '--rotate-token') {
1056
+ options.rotateToken = true;
1057
+ continue;
1058
+ }
959
1059
  throw new Error(`Unknown proxy enable option "${arg}".`);
960
1060
  }
961
- if (options.port < 1 || options.port > 65535) {
1061
+ if (options.token && options.rotateToken) {
1062
+ throw new Error('Use either --token or --rotate-token, not both.');
1063
+ }
1064
+ if (options.port !== undefined && (options.port < 1 || options.port > 65535)) {
962
1065
  throw new Error('port must be an integer between 1 and 65535.');
963
1066
  }
964
1067
  return options;
@@ -1049,10 +1152,23 @@ async function handleRemoteCommand(args) {
1049
1152
  try {
1050
1153
  await remoteClient.connect();
1051
1154
  const response = await remoteClient.request({ method: 'health.ping' });
1155
+ if (response.ok) {
1156
+ const health = /** @type {{ extensionConnected?: boolean }} */ (response.result);
1157
+ process.stdout.write(
1158
+ `Remote destination "${name}" is reachable (extension ${
1159
+ health.extensionConnected === true ? 'connected' : 'not connected'
1160
+ }).\n`
1161
+ );
1162
+ } else {
1163
+ process.exitCode = 1;
1164
+ process.stdout.write(`Remote destination "${name}" failed: ${response.error.message}\n`);
1165
+ }
1166
+ } catch (error) {
1167
+ process.exitCode = 1;
1052
1168
  process.stdout.write(
1053
- response.ok
1054
- ? `Remote destination "${name}" is reachable.\n`
1055
- : `Remote destination "${name}" failed: ${response.error.message}\n`
1169
+ `Remote destination "${name}" is not reachable: ${
1170
+ error instanceof Error ? error.message : String(error)
1171
+ }\n`
1056
1172
  );
1057
1173
  } finally {
1058
1174
  await remoteClient.close();
@@ -1214,7 +1330,7 @@ function printCallResponse(response) {
1214
1330
  }
1215
1331
 
1216
1332
  function printUsage() {
1217
- const blocks = ['Usage: bbx <command> [args]'];
1333
+ const blocks = ['Usage: bbx [--remote <name>] <command> [args]'];
1218
1334
  for (const section of CLI_HELP_SECTIONS) {
1219
1335
  blocks.push('', `${section.title}:`);
1220
1336
  blocks.push(...section.lines.map((line) => ` ${line}`));
@@ -1297,6 +1413,28 @@ async function parseCallCommand(args) {
1297
1413
  throw new Error('Usage: call [--tab <tabId>] <method> [paramsJson]');
1298
1414
  }
1299
1415
 
1416
+ /**
1417
+ * Extract the global --remote flag from anywhere in the argument list.
1418
+ * Exits with a usage error when the flag is present without a value.
1419
+ *
1420
+ * @param {string[]} args
1421
+ * @returns {{ remoteId: string | null, explicit: boolean, rest: string[] }}
1422
+ */
1423
+ function extractRemoteFlag(args) {
1424
+ const rest = [...args];
1425
+ const index = rest.indexOf('--remote');
1426
+ if (index === -1) {
1427
+ return { remoteId: null, explicit: false, rest };
1428
+ }
1429
+ const remoteId = rest[index + 1];
1430
+ if (!remoteId || remoteId.startsWith('--')) {
1431
+ process.stderr.write('--remote requires a destination name (see `bbx remote list`).\n');
1432
+ process.exit(1);
1433
+ }
1434
+ rest.splice(index, 2);
1435
+ return { remoteId, explicit: true, rest };
1436
+ }
1437
+
1300
1438
  /**
1301
1439
  * @param {string[]} args
1302
1440
  * @returns {{ tabId: number | null, rest: string[] }}
@@ -1,14 +1,16 @@
1
1
  // @ts-check
2
2
 
3
3
  import { EventEmitter, once } from 'node:events';
4
+ import fs from 'node:fs';
4
5
  import net from 'node:net';
5
6
  import { randomUUID } from 'node:crypto';
6
7
 
7
8
  import {
8
9
  createRequest,
9
10
  DEFAULT_CLIENT_REQUEST_TIMEOUT_MS,
10
- PROTOCOL_VERSION,
11
+ getProtocolVersion,
11
12
  parseJsonLines,
13
+ setProtocolPackageVersion,
12
14
  } from '../../protocol/src/index.js';
13
15
  import {
14
16
  createSocketBridgeTransport,
@@ -26,6 +28,21 @@ import { restartBridgeDaemon } from '../../native-host/src/daemon-process.js';
26
28
  /** @typedef {import('./types.js').PendingRequest} PendingRequest */
27
29
  /** @typedef {import('./types.js').ProtocolHealthResult} ProtocolHealthResult */
28
30
 
31
+ setProtocolPackageVersion(loadPackageVersion());
32
+
33
+ /**
34
+ * @returns {string | null}
35
+ */
36
+ function loadPackageVersion() {
37
+ try {
38
+ const raw = fs.readFileSync(new URL('../../../package.json', import.meta.url), 'utf8');
39
+ const parsed = JSON.parse(raw);
40
+ return parsed && typeof parsed.version === 'string' ? parsed.version : null;
41
+ } catch {
42
+ return null;
43
+ }
44
+ }
45
+
29
46
  /**
30
47
  * @param {string} left
31
48
  * @param {string} right
@@ -57,6 +74,18 @@ function createTimeoutError(method, timeoutMs) {
57
74
  return error;
58
75
  }
59
76
 
77
+ /**
78
+ * npm exec often runs a checkout-local CLI against an already-connected global
79
+ * daemon. In that mode, protocol drift should warn instead of replacing the
80
+ * user's working daemon and temporarily disconnecting the extension.
81
+ *
82
+ * @param {NodeJS.ProcessEnv} [env=process.env]
83
+ * @returns {boolean}
84
+ */
85
+ function shouldAutoRestartDaemonOnVersionMismatch(env = process.env) {
86
+ return env.npm_command !== 'exec';
87
+ }
88
+
60
89
  /**
61
90
  * @param {net.Socket} socket
62
91
  * @param {string} line
@@ -83,7 +112,7 @@ export class BridgeClient extends EventEmitter {
83
112
  clientId = `agent_${randomUUID()}`,
84
113
  defaultTimeoutMs = DEFAULT_CLIENT_REQUEST_TIMEOUT_MS,
85
114
  autoReconnect = false,
86
- restartDaemonOnVersionMismatch = true,
115
+ restartDaemonOnVersionMismatch = shouldAutoRestartDaemonOnVersionMismatch(),
87
116
  restartDaemonFn = restartBridgeDaemon,
88
117
  authToken = undefined,
89
118
  } = {}) {
@@ -390,9 +419,16 @@ export class BridgeClient extends EventEmitter {
390
419
  ? healthResult.supported_versions
391
420
  : [];
392
421
  const latestRemote = remoteVersions[0];
422
+ if (
423
+ healthResult?.extensionConnected === true &&
424
+ Array.isArray(healthResult.supported_versions) &&
425
+ !healthResult.supported_versions.includes(getProtocolVersion())
426
+ ) {
427
+ return false;
428
+ }
393
429
  return (
394
430
  typeof latestRemote === 'string' &&
395
- compareProtocolVersions(latestRemote, PROTOCOL_VERSION) < 0
431
+ compareProtocolVersions(latestRemote, getProtocolVersion()) < 0
396
432
  );
397
433
  }
398
434
 
@@ -436,20 +472,21 @@ export class BridgeClient extends EventEmitter {
436
472
  if (remoteVersions.length === 0) {
437
473
  return {
438
474
  compatible: true,
439
- localVersion: PROTOCOL_VERSION,
475
+ localVersion: getProtocolVersion(),
440
476
  remoteVersions,
441
477
  };
442
478
  }
443
- const compatible = remoteVersions.includes(PROTOCOL_VERSION);
479
+ const localVersion = getProtocolVersion();
480
+ const compatible = remoteVersions.includes(localVersion);
444
481
  return {
445
482
  compatible,
446
- localVersion: PROTOCOL_VERSION,
483
+ localVersion,
447
484
  remoteVersions,
448
485
  ...(!compatible && {
449
486
  warning:
450
487
  typeof healthResult?.migration_hint === 'string' && healthResult.migration_hint
451
488
  ? healthResult.migration_hint
452
- : `Protocol mismatch: client speaks ${PROTOCOL_VERSION} but remote supports [${remoteVersions.join(', ')}]. Update the ${remoteVersions[0] > PROTOCOL_VERSION ? 'client (npm)' : 'extension'} to match.`,
489
+ : `Protocol mismatch: client speaks ${localVersion} but remote supports [${remoteVersions.join(', ')}]. Update the ${remoteVersions[0] > localVersion ? 'client (npm)' : 'extension'} to match.`,
453
490
  }),
454
491
  };
455
492
  }
@@ -257,10 +257,13 @@ export const CLI_HELP_SECTIONS = Object.freeze([
257
257
  'bbx status Check bridge connection',
258
258
  'bbx doctor Diagnose install, daemon, extension, and access readiness',
259
259
  'bbx restart Restart the local Browser Bridge daemon',
260
- 'bbx proxy enable [--port 9223] [--bind-host 0.0.0.0] Enable opt-in TCP proxy mode and print a token',
260
+ 'bbx proxy enable [--port 9223] [--bind-host 0.0.0.0] [--rotate-token] Enable opt-in TCP proxy mode (idempotent: re-running keeps settings and token)',
261
261
  'bbx proxy disable Disable proxy mode and restart the daemon',
262
+ 'bbx proxy status Show proxy config and daemon reachability',
262
263
  'bbx remote add <name> <host:port> --token <token> Add a remote Browser Bridge destination (example: remote-bbx 192.168.56.20:9223)',
263
264
  'bbx remote list List remote destinations',
265
+ 'bbx remote test <name> Ping a remote destination',
266
+ 'bbx remote remove <name> Remove a remote destination',
264
267
  'bbx access-request Request Browser Bridge access for the focused window',
265
268
  'bbx logs Recent bridge logs',
266
269
  'bbx tabs List available tabs',
@@ -277,6 +280,7 @@ export const CLI_HELP_SECTIONS = Object.freeze([
277
280
  'bbx call [--tab <tabId>] <method> [paramsJson|-] Call any bridge method (- reads JSON from stdin)',
278
281
  'bbx <method> [--tab <tabId>] [paramsJson|-] Direct alias for exact bridge methods such as page.get_state',
279
282
  "bbx batch '[{method,params,tabId?},...]' Parallel method calls",
283
+ 'Every bridge command accepts --remote <name> (or BBX_REMOTE env) to target a remote destination from `bbx remote list`.',
280
284
  'Advanced bridge params stay available through `bbx call`, even when shortcuts expose only the common case.',
281
285
  'For open-ended investigation, start with `bbx batch` on `page.get_state`, `dom.query`, and `page.get_text` before any screenshot or CDP call.',
282
286
  '',
@@ -179,6 +179,34 @@ export async function listBridgeDestinations() {
179
179
  ];
180
180
  }
181
181
 
182
+ /**
183
+ * @typedef {{ port?: number, bindHost?: string, token?: string, rotateToken?: boolean }} ProxyEnableOptions
184
+ */
185
+
186
+ /**
187
+ * Merge `bbx proxy enable` options with an existing proxy config so re-running
188
+ * the command is idempotent: the port, bind host, and shared secret are
189
+ * preserved unless explicitly overridden. The token only changes when passed
190
+ * via --token or regenerated via --rotate-token, so already-configured remote
191
+ * clients keep working across repeated enables.
192
+ *
193
+ * @param {{ port: number, bindHost: string, token?: string } | null} existing
194
+ * @param {ProxyEnableOptions} options
195
+ * @param {() => string} generateToken
196
+ * @returns {{ port: number, bindHost: string, token: string, tokenSource: 'explicit' | 'existing' | 'generated' }}
197
+ */
198
+ export function resolveProxyEnableSettings(existing, options, generateToken) {
199
+ const port = options.port ?? existing?.port ?? DEFAULT_REMOTE_PORT;
200
+ const bindHost = options.bindHost ?? existing?.bindHost ?? '0.0.0.0';
201
+ if (options.token) {
202
+ return { port, bindHost, token: options.token, tokenSource: 'explicit' };
203
+ }
204
+ if (!options.rotateToken && existing?.token) {
205
+ return { port, bindHost, token: existing.token, tokenSource: 'existing' };
206
+ }
207
+ return { port, bindHost, token: generateToken(), tokenSource: 'generated' };
208
+ }
209
+
182
210
  /**
183
211
  * @param {string | null | undefined} destinationId
184
212
  * @param {{ defaultTimeoutMs?: number }} [options={}]