@avasapp/agent-bridge 0.4.1 → 0.5.0

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.
@@ -76,11 +76,11 @@ async function listCdpTargets(metro, signal) {
76
76
  );
77
77
  return await res.json();
78
78
  }
79
- function pickOne(items, filter, label) {
79
+ function pickOne(items, filter, label2) {
80
80
  const matching = filter ? items.filter(
81
- (item) => label(item).toLowerCase().includes(filter.toLowerCase())
81
+ (item) => label2(item).toLowerCase().includes(filter.toLowerCase())
82
82
  ) : items;
83
- const connected = items.map(label).join("; ") || "none";
83
+ const connected = items.map(label2).join("; ") || "none";
84
84
  if (matching.length === 0) {
85
85
  throw new Error(
86
86
  filter ? `No app matches "${filter}". Connected: ${connected}` : "No app is connected to Metro."
@@ -91,6 +91,15 @@ function pickOne(items, filter, label) {
91
91
  `${matching.length} apps are connected; pick one with --device. Connected: ${connected}`
92
92
  );
93
93
  }
94
+ if (matching.length > 1) {
95
+ const exact = matching.filter(
96
+ (item) => label2(item).toLowerCase() === filter?.toLowerCase()
97
+ );
98
+ if (exact.length === 1) return exact[0];
99
+ throw new Error(
100
+ `${matching.length} apps match "${filter}"; narrow --device. Matching: ${matching.map(label2).join("; ")}`
101
+ );
102
+ }
94
103
  return matching[0];
95
104
  }
96
105
 
@@ -107,6 +116,7 @@ function toAsciiJson(value) {
107
116
 
108
117
  // src/client/connection.ts
109
118
  var import_ws = __toESM(require("ws"), 1);
119
+ var describePin = (pin) => `${pin.name} (${pin.platform}${pin.target ? `, ${pin.target}` : ""})`;
110
120
  var OPEN_SOCKET_MS = 5e3;
111
121
  var OpenGaveUp = class extends Error {
112
122
  };
@@ -232,7 +242,7 @@ async function listExpoDevices(metro, discoveryMs = 600) {
232
242
  ws.close();
233
243
  return [...devices.values()];
234
244
  }
235
- async function connectExpo(metro, device, discoveryMs = 600, signal) {
245
+ async function connectExpo(metro, device, discoveryMs = 600, signal, pin) {
236
246
  const { ws, devices, pending, send, announced } = await openBroadcast(
237
247
  metro,
238
248
  discoveryMs,
@@ -240,7 +250,13 @@ async function connectExpo(metro, device, discoveryMs = 600, signal) {
240
250
  );
241
251
  let info;
242
252
  try {
243
- info = pickOne([...devices.values()], device, deviceLabel);
253
+ const all = [...devices.values()];
254
+ const candidates = pin ? all.filter((d) => d.name === pin.name && d.platform === pin.platform) : all;
255
+ if (pin && !candidates.length)
256
+ throw new Error(
257
+ `${describePin(pin)} is not connected; connected: ${all.map(deviceLabel).join("; ") || "none"}`
258
+ );
259
+ info = pickOne(candidates, device, deviceLabel);
244
260
  } catch (error) {
245
261
  ws.close();
246
262
  throw error;
@@ -255,6 +271,7 @@ async function connectExpo(metro, device, discoveryMs = 600, signal) {
255
271
  return {
256
272
  transport: "expo",
257
273
  device: info,
274
+ pin: { name: info.name, platform: info.platform },
258
275
  call(tool, args, timeoutMs) {
259
276
  if (superseded)
260
277
  return Promise.resolve({
@@ -278,17 +295,219 @@ async function connectExpo(metro, device, discoveryMs = 600, signal) {
278
295
  };
279
296
  }
280
297
 
298
+ // src/client/session/state.ts
299
+ var import_node_crypto = require("crypto");
300
+ var import_node_fs = require("fs");
301
+ var import_node_os = require("os");
302
+ var import_node_path = require("path");
303
+ function projectRoot(from = process.cwd()) {
304
+ for (let dir = (0, import_node_path.resolve)(from); ; dir = (0, import_node_path.dirname)(dir)) {
305
+ if ((0, import_node_fs.existsSync)((0, import_node_path.join)(dir, "package.json"))) return dir;
306
+ if ((0, import_node_path.dirname)(dir) === dir) return (0, import_node_path.resolve)(from);
307
+ }
308
+ }
309
+ var uid = () => process.getuid?.() ?? (0, import_node_os.userInfo)().username;
310
+ function privateDir(dir) {
311
+ (0, import_node_fs.mkdirSync)(dir, { recursive: true, mode: 448 });
312
+ const stat = (0, import_node_fs.lstatSync)(dir);
313
+ if (!stat.isDirectory())
314
+ throw new Error(`${dir} is not a directory; remove it and retry`);
315
+ if (process.getuid && stat.uid !== process.getuid())
316
+ throw new Error(`${dir} belongs to another user; refusing to use it`);
317
+ if (process.platform !== "win32" && (stat.mode & 63) !== 0)
318
+ (0, import_node_fs.chmodSync)(dir, 448);
319
+ return dir;
320
+ }
321
+ function stateDir() {
322
+ return privateDir(
323
+ process.env.AGENT_BRIDGE_STATE_DIR ?? (process.env.XDG_RUNTIME_DIR ? (0, import_node_path.join)(process.env.XDG_RUNTIME_DIR, "agent-bridge") : (0, import_node_path.join)((0, import_node_os.tmpdir)(), `agent-bridge-${uid()}`))
324
+ );
325
+ }
326
+ var shortHash = (text) => (0, import_node_crypto.createHash)("sha256").update(text).digest("hex").slice(0, 16);
327
+ function socketPath(name, dir) {
328
+ if (process.platform === "win32")
329
+ return `\\\\.\\pipe\\agent-bridge-${shortHash(dir)}-${name}`;
330
+ const path = (0, import_node_path.join)(dir, `${name}.sock`);
331
+ if (Buffer.byteLength(path) <= 100) return path;
332
+ const short = privateDir(`/tmp/agent-bridge-${uid()}`);
333
+ return (0, import_node_path.join)(short, `${shortHash(path)}.sock`);
334
+ }
335
+ var sessionFiles = (name, dir = stateDir()) => ({
336
+ state: (0, import_node_path.join)(dir, `${name}.json`),
337
+ error: (0, import_node_path.join)(dir, `${name}.error`),
338
+ log: (0, import_node_path.join)(dir, `${name}.log`),
339
+ socket: socketPath(name, dir)
340
+ });
341
+ function checkName(name) {
342
+ if (!/^[\w.-]{1,40}$/.test(name))
343
+ throw new Error(
344
+ `Session name "${name}" must be 1-40 letters, digits, ".", "_" or "-"`
345
+ );
346
+ return name;
347
+ }
348
+ function isAlive(pid) {
349
+ try {
350
+ process.kill(pid, 0);
351
+ return true;
352
+ } catch (error) {
353
+ return error.code === "EPERM";
354
+ }
355
+ }
356
+ function removeSessionFiles(name, dir = stateDir()) {
357
+ const files = sessionFiles(name, dir);
358
+ (0, import_node_fs.rmSync)(files.state, { force: true });
359
+ if (process.platform !== "win32") (0, import_node_fs.rmSync)(files.socket, { force: true });
360
+ }
361
+ function readState(file) {
362
+ try {
363
+ return JSON.parse((0, import_node_fs.readFileSync)(file, "utf8"));
364
+ } catch {
365
+ return null;
366
+ }
367
+ }
368
+ function listSessions(dir = stateDir()) {
369
+ const live = [];
370
+ for (const entry of (0, import_node_fs.readdirSync)(dir)) {
371
+ if (!entry.endsWith(".json")) continue;
372
+ const state = readState((0, import_node_path.join)(dir, entry));
373
+ if (!state) continue;
374
+ if (isAlive(state.pid)) live.push(state);
375
+ else removeSessionFiles(state.name, dir);
376
+ }
377
+ return live.sort((a, b) => a.startedAt - b.startedAt);
378
+ }
379
+ function readSession(name, dir = stateDir()) {
380
+ return listSessions(dir).find((s) => s.name === name) ?? null;
381
+ }
382
+ function pickSession(filter) {
383
+ if (filter.name) {
384
+ const named = readSession(checkName(filter.name));
385
+ if (!named) throw new Error(`No session named "${filter.name}" is running`);
386
+ return named;
387
+ }
388
+ const device = filter.device?.toLowerCase();
389
+ const explicit = !!(filter.metro || device || filter.transport && filter.transport !== "auto");
390
+ const project = projectRoot();
391
+ const matching = listSessions().filter(
392
+ (s) => (explicit || s.project === project) && (!filter.metro || s.metro === filter.metro) && (!device || s.device.name.toLowerCase().includes(device) || s.device.deviceId.toLowerCase().includes(device)) && (!filter.transport || filter.transport === "auto" || filter.transport === s.transport)
393
+ );
394
+ if (matching.length > 1)
395
+ throw new Error(
396
+ `Several sessions match (${matching.map((s) => `${s.name}: ${s.device.name} on ${s.metro}`).join("; ")}); pick one by name (--session, or --name for session stop/status), or narrow with --metro/--device`
397
+ );
398
+ return matching[0] ?? null;
399
+ }
400
+
401
+ // src/client/metro-hint.ts
402
+ var range = (from, to) => Array.from({ length: to - from + 1 }, (_, i) => from + i);
403
+ var probe = {
404
+ ports: [...range(8081, 8090), ...range(8100, 8199)],
405
+ eachMs: 300,
406
+ totalMs: 1e3
407
+ };
408
+ var hostOf = (metro) => {
409
+ const colon = metro.lastIndexOf(":");
410
+ return colon > 0 && !metro.endsWith("]") ? metro.slice(0, colon) : metro;
411
+ };
412
+ async function answers(metro, signal) {
413
+ try {
414
+ const res = await fetch(`http://${metro}/status`, { signal });
415
+ return (await res.text()).includes("packager-status:running");
416
+ } catch {
417
+ return false;
418
+ }
419
+ }
420
+ async function appsOn(metro, signal) {
421
+ try {
422
+ const res = await fetch(`http://${metro}/json/list`, { signal });
423
+ const targets = await res.json();
424
+ const names = targets.map(
425
+ (t) => `${t.title ?? "?"}${t.deviceName ? ` [${t.deviceName}]` : ""}`
426
+ );
427
+ return [...new Set(names)];
428
+ } catch {
429
+ return [];
430
+ }
431
+ }
432
+ async function findMetros(metro) {
433
+ const host = hostOf(metro);
434
+ const candidates = new Set(probe.ports.map((port) => `${host}:${port}`));
435
+ try {
436
+ for (const s of listSessions()) candidates.add(s.metro);
437
+ } catch {
438
+ }
439
+ candidates.delete(metro);
440
+ const deadline = AbortSignal.timeout(probe.totalMs);
441
+ const within2 = () => AbortSignal.any([deadline, AbortSignal.timeout(probe.eachMs)]);
442
+ const running = (await Promise.all(
443
+ [...candidates].map(async (m) => await answers(m, within2()) ? m : null)
444
+ )).filter((m) => m !== null);
445
+ return Promise.all(
446
+ running.map(async (m) => ({ metro: m, apps: await appsOn(m, within2()) }))
447
+ );
448
+ }
449
+ var label = (found) => `${found.metro} (${found.apps.length ? `${found.apps.length === 1 ? "app" : "apps"}: ${found.apps.join(", ")}` : "no app connected"})`;
450
+ var howToSet = (metro) => `Pass --metro ${metro} or set AGENT_BRIDGE_METRO=${metro}.`;
451
+ function elsewhere(found) {
452
+ const [only] = found;
453
+ if (found.length === 1 && only)
454
+ return `Metro is running on ${label(only)}. ${howToSet(only.metro)}`;
455
+ return `Metro is running on ${found.map(label).join("; ")}. ${howToSet("<host:port>")}`;
456
+ }
457
+ var errorText = (error) => {
458
+ if (!(error instanceof Error)) return String(error);
459
+ const code = error.code ?? "";
460
+ return `${error.message} ${code} ${error.cause ? errorText(error.cause) : ""}`;
461
+ };
462
+ var isRefused = (error) => /ECONNREFUSED|ConnectionRefused/.test(errorText(error));
463
+ var NO_APP = /No app is connected to Metro|No app matches/;
464
+ async function refusedMessage(metro) {
465
+ const found = await findMetros(metro);
466
+ if (found.length)
467
+ return `Nothing is listening on ${metro}. ${elsewhere(found)}`;
468
+ const host = hostOf(metro);
469
+ return `Nothing is listening on ${metro}, and no Metro answered on ${host} ports 8081-8090 or 8100-8199. Start Metro, or if it runs elsewhere, pass --metro <host:port> or set AGENT_BRIDGE_METRO=<host:port>.`;
470
+ }
471
+ async function noAppHint(metro, device) {
472
+ const causes = `Likely: the app isn't open or hasn't reloaded since the bridge was mounted, it uses another Metro${device ? `, or --device "${device}" doesn't match` : ""}.`;
473
+ const withApps = (await findMetros(metro)).filter((f) => f.apps.length);
474
+ return withApps.length ? `${causes} ${elsewhere(withApps)}` : causes;
475
+ }
476
+ async function refused(metro) {
477
+ try {
478
+ await fetch(`http://${metro}/status`, {
479
+ signal: AbortSignal.timeout(probe.eachMs)
480
+ });
481
+ return false;
482
+ } catch (error) {
483
+ return isRefused(error);
484
+ }
485
+ }
486
+ async function explainConnectError(error, metro, device) {
487
+ const message = error instanceof Error ? error.message : String(error);
488
+ if (NO_APP.test(message))
489
+ return new Error(`${message}
490
+ ${await noAppHint(metro, device)}`, {
491
+ cause: error
492
+ });
493
+ if (await refused(metro))
494
+ return new Error(await refusedMessage(metro), { cause: error });
495
+ return error;
496
+ }
497
+
281
498
  // src/client/cdp.ts
282
499
  var import_ws2 = __toESM(require("ws"), 1);
283
500
  var CannotEvaluate = class extends Error {
284
501
  };
285
502
  var targetLabel = (t) => `${t.title}${t.deviceName ? ` [${t.deviceName}]` : ""}`;
286
- async function connectCdp(metro, device, signal) {
287
- const target = pickOne(
288
- await listCdpTargets(metro, signal),
289
- device,
290
- targetLabel
291
- );
503
+ async function connectCdp(metro, device, signal, pin) {
504
+ const targets = await listCdpTargets(metro, signal);
505
+ const candidates = pin?.target ? targets.filter((t) => targetLabel(t) === pin.target) : targets;
506
+ if (pin && !candidates.length)
507
+ throw new Error(
508
+ `${describePin(pin)} is not connected; connected: ${targets.map(targetLabel).join("; ") || "none"}`
509
+ );
510
+ const target = pickOne(candidates, device, targetLabel);
292
511
  const hostUri = await expoHostUri(metro, signal);
293
512
  const port = metro.split(":")[1] ?? "8081";
294
513
  const origin = hostUri ? `http://${hostUri}` : `http://localhost:${port}`;
@@ -366,27 +585,34 @@ async function connectCdp(metro, device, signal) {
366
585
  );
367
586
  }
368
587
  signal?.removeEventListener("abort", dropOnAbort);
588
+ if (pin && (info.name !== pin.name || info.platform !== pin.platform)) {
589
+ ws.terminate();
590
+ throw new Error(
591
+ `${describePin(pin)} is not connected; connected: ${targetLabel(target)} runs ${info.name} (${info.platform})`
592
+ );
593
+ }
369
594
  return {
370
595
  transport: "cdp",
596
+ pin: {
597
+ name: info.name,
598
+ platform: info.platform,
599
+ target: targetLabel(target)
600
+ },
371
601
  device: { ...info, name: `${info.name} (${targetLabel(target)})` },
372
- async call(tool, args, timeoutMs) {
602
+ call(tool, args, timeoutMs) {
373
603
  const call = { id: newCallId(), tool, args };
374
604
  const reply = pending.wait(call.id, tool, timeoutMs);
375
- reply.catch(() => {
376
- });
377
- try {
378
- await evaluate(
379
- `${CDP_GLOBAL}.dispatch(${JSON.stringify(toAsciiJson(call))})`
380
- );
381
- } catch (error) {
382
- pending.settle({
605
+ evaluate(
606
+ `${CDP_GLOBAL}.dispatch(${JSON.stringify(toAsciiJson(call))})`
607
+ ).catch(
608
+ (error) => pending.settle({
383
609
  id: call.id,
384
610
  from: info.deviceId,
385
611
  ok: false,
386
612
  error: String(error),
387
613
  ms: 0
388
- });
389
- }
614
+ })
615
+ );
390
616
  return reply;
391
617
  },
392
618
  close: () => ws.close()
@@ -429,7 +655,7 @@ async function openConnection(options = {}) {
429
655
  try {
430
656
  return await phase(
431
657
  options,
432
- (signal) => connectExpo(metro, options.device, void 0, signal)
658
+ (signal) => connectExpo(metro, options.device, void 0, signal, options.pin)
433
659
  );
434
660
  } catch (error) {
435
661
  if (want === "expo" || options.signal?.aborted) throw error;
@@ -439,7 +665,7 @@ async function openConnection(options = {}) {
439
665
  try {
440
666
  return await phase(
441
667
  options,
442
- (signal) => connectCdp(metro, options.device, signal)
668
+ (signal) => connectCdp(metro, options.device, signal, options.pin)
443
669
  );
444
670
  } catch (error) {
445
671
  const expoNote = expoError ? ` (Expo socket: ${String(expoError)})` : "";
@@ -503,7 +729,7 @@ async function runFlow(bridge, flow, options = {}) {
503
729
  }
504
730
  };
505
731
  const call = async (tool, ...args) => (await timed(tool, ...args)).value;
506
- const step = async (label, tool, ...given) => {
732
+ const step = async (label2, tool, ...given) => {
507
733
  const args = given.length === 1 && Array.isArray(given[0]) ? given[0] : given;
508
734
  const { value, ms, logs, notice } = await bridge.timed(tool, ...args).catch((error) => {
509
735
  report(failedLogs(error), failedNotice(error));
@@ -511,7 +737,7 @@ async function runFlow(bridge, flow, options = {}) {
511
737
  });
512
738
  total += ms;
513
739
  print(
514
- `${String(++n).padStart(2, "0")} ${label.padEnd(30)} ${ms.toFixed(1).padStart(7)} ms`
740
+ `${String(++n).padStart(2, "0")} ${label2.padEnd(30)} ${ms.toFixed(1).padStart(7)} ms`
515
741
  );
516
742
  report(logs, notice);
517
743
  return value;
@@ -563,109 +789,6 @@ var import_node_readline = require("readline");
563
789
  var encodeLine = (message) => `${JSON.stringify(message).replace(/[\u2028\u2029]/g, (c) => `\\u${c.charCodeAt(0).toString(16)}`)}
564
790
  `;
565
791
 
566
- // src/client/session/state.ts
567
- var import_node_crypto = require("crypto");
568
- var import_node_fs = require("fs");
569
- var import_node_os = require("os");
570
- var import_node_path = require("path");
571
- function projectRoot(from = process.cwd()) {
572
- for (let dir = (0, import_node_path.resolve)(from); ; dir = (0, import_node_path.dirname)(dir)) {
573
- if ((0, import_node_fs.existsSync)((0, import_node_path.join)(dir, "package.json"))) return dir;
574
- if ((0, import_node_path.dirname)(dir) === dir) return (0, import_node_path.resolve)(from);
575
- }
576
- }
577
- var uid = () => process.getuid?.() ?? (0, import_node_os.userInfo)().username;
578
- function privateDir(dir) {
579
- (0, import_node_fs.mkdirSync)(dir, { recursive: true, mode: 448 });
580
- const stat = (0, import_node_fs.lstatSync)(dir);
581
- if (!stat.isDirectory())
582
- throw new Error(`${dir} is not a directory; remove it and retry`);
583
- if (process.getuid && stat.uid !== process.getuid())
584
- throw new Error(`${dir} belongs to another user; refusing to use it`);
585
- if (process.platform !== "win32" && (stat.mode & 63) !== 0)
586
- (0, import_node_fs.chmodSync)(dir, 448);
587
- return dir;
588
- }
589
- function stateDir() {
590
- return privateDir(
591
- process.env.AGENT_BRIDGE_STATE_DIR ?? (process.env.XDG_RUNTIME_DIR ? (0, import_node_path.join)(process.env.XDG_RUNTIME_DIR, "agent-bridge") : (0, import_node_path.join)((0, import_node_os.tmpdir)(), `agent-bridge-${uid()}`))
592
- );
593
- }
594
- var shortHash = (text) => (0, import_node_crypto.createHash)("sha256").update(text).digest("hex").slice(0, 16);
595
- function socketPath(name, dir) {
596
- if (process.platform === "win32")
597
- return `\\\\.\\pipe\\agent-bridge-${shortHash(dir)}-${name}`;
598
- const path = (0, import_node_path.join)(dir, `${name}.sock`);
599
- if (Buffer.byteLength(path) <= 100) return path;
600
- const short = privateDir(`/tmp/agent-bridge-${uid()}`);
601
- return (0, import_node_path.join)(short, `${shortHash(path)}.sock`);
602
- }
603
- var sessionFiles = (name, dir = stateDir()) => ({
604
- state: (0, import_node_path.join)(dir, `${name}.json`),
605
- error: (0, import_node_path.join)(dir, `${name}.error`),
606
- log: (0, import_node_path.join)(dir, `${name}.log`),
607
- socket: socketPath(name, dir)
608
- });
609
- function checkName(name) {
610
- if (!/^[\w.-]{1,40}$/.test(name))
611
- throw new Error(
612
- `Session name "${name}" must be 1-40 letters, digits, ".", "_" or "-"`
613
- );
614
- return name;
615
- }
616
- function isAlive(pid) {
617
- try {
618
- process.kill(pid, 0);
619
- return true;
620
- } catch (error) {
621
- return error.code === "EPERM";
622
- }
623
- }
624
- function removeSessionFiles(name, dir = stateDir()) {
625
- const files = sessionFiles(name, dir);
626
- (0, import_node_fs.rmSync)(files.state, { force: true });
627
- if (process.platform !== "win32") (0, import_node_fs.rmSync)(files.socket, { force: true });
628
- }
629
- function readState(file) {
630
- try {
631
- return JSON.parse((0, import_node_fs.readFileSync)(file, "utf8"));
632
- } catch {
633
- return null;
634
- }
635
- }
636
- function listSessions(dir = stateDir()) {
637
- const live = [];
638
- for (const entry of (0, import_node_fs.readdirSync)(dir)) {
639
- if (!entry.endsWith(".json")) continue;
640
- const state = readState((0, import_node_path.join)(dir, entry));
641
- if (!state) continue;
642
- if (isAlive(state.pid)) live.push(state);
643
- else removeSessionFiles(state.name, dir);
644
- }
645
- return live.sort((a, b) => a.startedAt - b.startedAt);
646
- }
647
- function readSession(name, dir = stateDir()) {
648
- return listSessions(dir).find((s) => s.name === name) ?? null;
649
- }
650
- function pickSession(filter) {
651
- if (filter.name) {
652
- const named = readSession(checkName(filter.name));
653
- if (!named) throw new Error(`No session named "${filter.name}" is running`);
654
- return named;
655
- }
656
- const device = filter.device?.toLowerCase();
657
- const explicit = !!(filter.metro || device || filter.transport && filter.transport !== "auto");
658
- const project = projectRoot();
659
- const matching = listSessions().filter(
660
- (s) => (explicit || s.project === project) && (!filter.metro || s.metro === filter.metro) && (!device || s.device.name.toLowerCase().includes(device) || s.device.deviceId.toLowerCase().includes(device)) && (!filter.transport || filter.transport === "auto" || filter.transport === s.transport)
661
- );
662
- if (matching.length > 1)
663
- throw new Error(
664
- `Several sessions match (${matching.map((s) => `${s.name}: ${s.device.name} on ${s.metro}`).join("; ")}); pick one by name (--session, or --name for session stop/status), or narrow with --metro/--device`
665
- );
666
- return matching[0] ?? null;
667
- }
668
-
669
792
  // src/client/session/client.ts
670
793
  function openSessionLink(state) {
671
794
  return new Promise((resolve2, reject) => {
@@ -787,7 +910,9 @@ Warning: ${notice}` : ""}`);
787
910
  };
788
911
  async function connect(options = {}) {
789
912
  const timeoutMs = options.timeoutMs ?? 1e4;
790
- const conn = await openConnection(options);
913
+ const conn = await openConnection(options).catch(async (error) => {
914
+ throw await explainConnectError(error, metroHost(options.metro), options.device);
915
+ });
791
916
  const timed = async (tool, ...args) => {
792
917
  const t0 = performance.now();
793
918
  const result = await conn.call(tool, args, timeoutMs);
@@ -5,7 +5,7 @@ import {
5
5
  listDevices,
6
6
  listSessions,
7
7
  runFlow
8
- } from "../chunk-AJD3XTNT.js";
8
+ } from "../chunk-3QIG7A5W.js";
9
9
  export {
10
10
  AgentBridgeCallError,
11
11
  connect,