@mearl/native-host 2.4.0 → 2.5.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/dist/index.js CHANGED
@@ -46,7 +46,7 @@ function uninstallNativeHostArtifacts(options = {}, dependencies = {}) {
46
46
  writeLine(line);
47
47
  };
48
48
  try {
49
- const existsSync4 = dependencies.existsSync ?? fs11.existsSync;
49
+ const existsSync5 = dependencies.existsSync ?? fs11.existsSync;
50
50
  const platform = dependencies.platform ?? process.platform;
51
51
  const hostDir = dependencies.hostDir ?? resolveNativeHostDirectory({
52
52
  env: dependencies.env,
@@ -62,7 +62,7 @@ function uninstallNativeHostArtifacts(options = {}, dependencies = {}) {
62
62
  path12.join(hostDir, `${hostName}-launcher.${launcherExtension}`)
63
63
  ];
64
64
  for (const artifact of artifacts) {
65
- if (!existsSync4(artifact))
65
+ if (!existsSync5(artifact))
66
66
  continue;
67
67
  unlinkSync(artifact);
68
68
  log2(`✅ Removed: ${artifact}`);
@@ -98,7 +98,7 @@ import fs12 from "node:fs";
98
98
  import path13 from "node:path";
99
99
  import { execFileSync as execFileSync2 } from "node:child_process";
100
100
  import { createRequire } from "node:module";
101
- import { fileURLToPath as fileURLToPath2 } from "node:url";
101
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
102
102
  function resolveHostDir() {
103
103
  return resolveNativeHostDirectory();
104
104
  }
@@ -306,7 +306,7 @@ var init_install = __esm({
306
306
  "use strict";
307
307
  init_nativeHost();
308
308
  DEFAULT_EXTENSION_ID = "aoehhjnofngknnjefamjbplchbolghkm";
309
- __filename = fileURLToPath2(import.meta.url);
309
+ __filename = fileURLToPath3(import.meta.url);
310
310
  __dirname = path13.dirname(__filename);
311
311
  ALL_MODES = ["skills", "mcp"];
312
312
  }
@@ -315,17 +315,16 @@ var init_install = __esm({
315
315
  // src/index.ts
316
316
  import net from "node:net";
317
317
  import fs13 from "node:fs";
318
- import http5 from "node:http";
318
+ import http4 from "node:http";
319
319
  import os7 from "node:os";
320
320
  import path14 from "node:path";
321
- import { fileURLToPath as fileURLToPath3 } from "node:url";
321
+ import { fileURLToPath as fileURLToPath4 } from "node:url";
322
322
 
323
323
  // src/cdp-backend/cdp-session.ts
324
- import http3 from "node:http";
324
+ import http2 from "node:http";
325
325
 
326
326
  // src/cdp-backend/client.ts
327
- import http from "node:http";
328
- import crypto2 from "node:crypto";
327
+ import WebSocket from "ws";
329
328
  var CdpChildSession = class {
330
329
  constructor(parent, sessionId) {
331
330
  this.parent = parent;
@@ -344,199 +343,89 @@ var CdpChildSession = class {
344
343
  }
345
344
  };
346
345
  var CdpClient = class _CdpClient {
347
- socket;
348
- buffer = Buffer.alloc(0);
346
+ constructor(socket) {
347
+ this.socket = socket;
348
+ socket.on("message", (data) => this.consumeMessage(data));
349
+ socket.on("close", () => this.handleClosed());
350
+ socket.on("error", () => this.handleClosed());
351
+ }
349
352
  nextId = 1;
350
353
  pending = /* @__PURE__ */ new Map();
351
354
  eventHandlers = /* @__PURE__ */ new Map();
352
- /** sessionId → method → callbacks (for multiplexed page sessions) */
353
355
  sessionEventHandlers = /* @__PURE__ */ new Map();
354
356
  _closed = false;
355
- constructor(socket) {
356
- this.socket = socket;
357
- socket.on("data", (chunk) => {
358
- this.buffer = Buffer.concat([this.buffer, chunk]);
359
- this.consumeFrames();
360
- });
361
- socket.on("close", () => {
362
- this._closed = true;
363
- for (const [id, pending] of this.pending) {
364
- clearTimeout(pending.timer);
365
- pending.reject(new Error("CDP connection closed"));
366
- this.pending.delete(id);
357
+ static connect(wsUrl) {
358
+ return new Promise((resolve2, reject) => {
359
+ let url2;
360
+ try {
361
+ url2 = new URL(wsUrl);
362
+ } catch {
363
+ reject(new Error("Invalid CDP WebSocket URL"));
364
+ return;
367
365
  }
368
- });
369
- socket.on("error", () => {
370
- this._closed = true;
366
+ if (url2.protocol !== "ws:" && url2.protocol !== "wss:") {
367
+ reject(new Error(`Unsupported CDP WebSocket protocol: ${url2.protocol}`));
368
+ return;
369
+ }
370
+ const socket = new WebSocket(url2, {
371
+ handshakeTimeout: 5e3,
372
+ maxPayload: 64 * 1024 * 1024,
373
+ perMessageDeflate: false
374
+ });
375
+ let settled = false;
376
+ const fail = (error48) => {
377
+ if (settled) return;
378
+ settled = true;
379
+ socket.terminate();
380
+ reject(error48);
381
+ };
382
+ socket.once("open", () => {
383
+ if (settled) return;
384
+ settled = true;
385
+ resolve2(new _CdpClient(socket));
386
+ });
387
+ socket.once("error", fail);
388
+ socket.once("unexpected-response", (_request, response) => {
389
+ response.resume();
390
+ fail(new Error(`CDP WebSocket upgrade failed with status ${response.statusCode ?? 0}`));
391
+ });
371
392
  });
372
393
  }
373
- /** Create a child session client that routes through this connection via sessionId. */
374
394
  createChildSession(sessionId) {
375
395
  return new CdpChildSession(this, sessionId);
376
396
  }
377
- /** Send a command within a specific CDP session (multiplexed on this connection). */
378
397
  sendCommandInSession(sessionId, method, params, timeoutMs = 3e4) {
379
- return new Promise((resolve, reject) => {
380
- if (this._closed) {
381
- reject(new Error("CDP client is closed"));
382
- return;
383
- }
384
- const id = this.nextId++;
385
- const timer = setTimeout(() => {
386
- this.pending.delete(id);
387
- reject(new Error(`CDP command timeout: ${method}`));
388
- }, timeoutMs);
389
- this.pending.set(id, { resolve, reject, timer, sessionId });
390
- this.sendText(JSON.stringify({ id, method, params: params ?? {}, sessionId }));
391
- });
398
+ return this.sendRequest(method, params, timeoutMs, sessionId);
392
399
  }
393
- /** Subscribe to events from a specific CDP session. */
394
400
  onSession(sessionId, method, callback) {
395
401
  if (!this.sessionEventHandlers.has(sessionId)) {
396
402
  this.sessionEventHandlers.set(sessionId, /* @__PURE__ */ new Map());
397
403
  }
398
404
  const sessionMap = this.sessionEventHandlers.get(sessionId);
399
- if (!sessionMap.has(method)) {
400
- sessionMap.set(method, /* @__PURE__ */ new Set());
401
- }
405
+ if (!sessionMap.has(method)) sessionMap.set(method, /* @__PURE__ */ new Set());
402
406
  sessionMap.get(method).add(callback);
403
407
  return () => sessionMap.get(method)?.delete(callback);
404
408
  }
405
- static connect(wsUrl) {
406
- return new Promise((resolve, reject) => {
407
- const url2 = new URL(wsUrl);
408
- const port = parseInt(url2.port || "9222", 10);
409
- const key = crypto2.randomBytes(16).toString("base64");
410
- const expectedAccept = crypto2.createHash("sha1").update(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest("base64");
411
- const req = http.request({
412
- host: url2.hostname,
413
- port,
414
- path: url2.pathname,
415
- headers: {
416
- Connection: "Upgrade",
417
- Upgrade: "websocket",
418
- "Sec-WebSocket-Key": key,
419
- "Sec-WebSocket-Version": "13",
420
- Host: url2.host
421
- // No Origin header — Chrome allows connections without Origin,
422
- // but rejects any Origin not in --remote-allow-origins allowlist.
423
- }
424
- });
425
- let settled = false;
426
- const fail = (err) => {
427
- if (settled) return;
428
- settled = true;
429
- reject(err);
430
- };
431
- req.on("upgrade", (res, socket, head) => {
432
- if (settled) return;
433
- if (res.statusCode !== 101) {
434
- settled = true;
435
- socket.destroy();
436
- reject(new Error(`WebSocket upgrade failed with status ${res.statusCode}`));
437
- return;
438
- }
439
- const acceptHeader = res.headers["sec-websocket-accept"];
440
- if (acceptHeader !== expectedAccept) {
441
- settled = true;
442
- socket.destroy();
443
- reject(new Error("WebSocket handshake key mismatch"));
444
- return;
445
- }
446
- settled = true;
447
- const client2 = new _CdpClient(socket);
448
- if (head && head.length > 0) {
449
- client2.buffer = Buffer.concat([client2.buffer, head]);
450
- client2.consumeFrames();
451
- }
452
- resolve(client2);
453
- });
454
- req.on("error", fail);
455
- req.setTimeout(5e3, () => {
456
- req.destroy();
457
- fail(new Error("CDP WebSocket connect timeout"));
458
- });
459
- req.end();
460
- });
409
+ sendCommand(method, params, timeoutMs = 3e4) {
410
+ return this.sendRequest(method, params, timeoutMs);
461
411
  }
462
- consumeFrames() {
463
- while (true) {
464
- if (this.buffer.length < 2) return;
465
- const byte1 = this.buffer[1];
466
- const masked = (byte1 & 128) !== 0;
467
- let payloadLen = byte1 & 127;
468
- let headerLen = 2;
469
- if (payloadLen === 126) {
470
- if (this.buffer.length < 4) return;
471
- payloadLen = this.buffer.readUInt16BE(2);
472
- headerLen = 4;
473
- } else if (payloadLen === 127) {
474
- if (this.buffer.length < 10) return;
475
- payloadLen = Number(this.buffer.readBigUInt64BE(2));
476
- headerLen = 10;
477
- }
478
- if (masked) headerLen += 4;
479
- if (this.buffer.length < headerLen + payloadLen) return;
480
- const opcode = this.buffer[0] & 15;
481
- let payload;
482
- if (masked) {
483
- const maskKey = this.buffer.subarray(headerLen - 4, headerLen);
484
- payload = Buffer.from(
485
- this.buffer.subarray(headerLen, headerLen + payloadLen).map((b, i) => b ^ maskKey[i % 4])
486
- );
487
- } else {
488
- payload = this.buffer.subarray(headerLen, headerLen + payloadLen);
489
- }
490
- this.buffer = this.buffer.subarray(headerLen + payloadLen);
491
- if (opcode === 1 || opcode === 2) {
492
- try {
493
- const msg = JSON.parse(payload.toString("utf-8"));
494
- this.handleMessage(msg);
495
- } catch {
496
- }
497
- }
498
- }
412
+ on(method, callback) {
413
+ if (!this.eventHandlers.has(method)) this.eventHandlers.set(method, /* @__PURE__ */ new Set());
414
+ this.eventHandlers.get(method).add(callback);
415
+ return () => this.eventHandlers.get(method)?.delete(callback);
499
416
  }
500
- handleMessage(msg) {
501
- if (msg.id !== void 0) {
502
- const pending = this.pending.get(msg.id);
503
- if (pending) {
504
- clearTimeout(pending.timer);
505
- this.pending.delete(msg.id);
506
- if (msg.error) {
507
- pending.reject(new Error(msg.error.message ?? JSON.stringify(msg.error)));
508
- } else {
509
- pending.resolve(msg.result ?? {});
510
- }
511
- }
512
- } else if (msg.method) {
513
- if (msg.sessionId) {
514
- const sessionMap = this.sessionEventHandlers.get(msg.sessionId);
515
- const handlers = sessionMap?.get(msg.method);
516
- if (handlers) {
517
- for (const cb of handlers) {
518
- try {
519
- cb(msg.params);
520
- } catch {
521
- }
522
- }
523
- }
524
- } else {
525
- const handlers = this.eventHandlers.get(msg.method);
526
- if (handlers) {
527
- for (const cb of handlers) {
528
- try {
529
- cb(msg.params);
530
- } catch {
531
- }
532
- }
533
- }
534
- }
535
- }
417
+ close() {
418
+ if (this._closed) return;
419
+ this._closed = true;
420
+ this.socket.terminate();
421
+ this.rejectPending("CDP connection closed");
536
422
  }
537
- sendCommand(method, params, timeoutMs = 3e4) {
538
- return new Promise((resolve, reject) => {
539
- if (this._closed) {
423
+ get closed() {
424
+ return this._closed;
425
+ }
426
+ sendRequest(method, params, timeoutMs, sessionId) {
427
+ return new Promise((resolve2, reject) => {
428
+ if (this._closed || this.socket.readyState !== WebSocket.OPEN) {
540
429
  reject(new Error("CDP client is closed"));
541
430
  return;
542
431
  }
@@ -545,51 +434,61 @@ var CdpClient = class _CdpClient {
545
434
  this.pending.delete(id);
546
435
  reject(new Error(`CDP command timeout: ${method}`));
547
436
  }, timeoutMs);
548
- this.pending.set(id, { resolve, reject, timer });
549
- this.sendText(JSON.stringify({ id, method, params: params ?? {} }));
437
+ this.pending.set(id, { resolve: resolve2, reject, timer, sessionId });
438
+ this.socket.send(
439
+ JSON.stringify({ id, method, params: params ?? {}, ...sessionId ? { sessionId } : {} }),
440
+ (error48) => {
441
+ if (!error48) return;
442
+ const pending = this.pending.get(id);
443
+ if (!pending) return;
444
+ clearTimeout(pending.timer);
445
+ this.pending.delete(id);
446
+ pending.reject(error48);
447
+ }
448
+ );
550
449
  });
551
450
  }
552
- on(method, callback) {
553
- if (!this.eventHandlers.has(method)) {
554
- this.eventHandlers.set(method, /* @__PURE__ */ new Set());
451
+ consumeMessage(data) {
452
+ try {
453
+ const text = Array.isArray(data) ? Buffer.concat(data).toString("utf8") : Buffer.isBuffer(data) ? data.toString("utf8") : Buffer.from(data).toString("utf8");
454
+ this.handleMessage(JSON.parse(text));
455
+ } catch {
555
456
  }
556
- this.eventHandlers.get(method).add(callback);
557
- return () => this.eventHandlers.get(method)?.delete(callback);
558
457
  }
559
- sendText(text) {
560
- if (this._closed) return;
561
- const payload = Buffer.from(text, "utf-8");
562
- const maskKey = crypto2.randomBytes(4);
563
- const masked = Buffer.from(payload.map((b, i) => b ^ maskKey[i % 4]));
564
- let header;
565
- if (payload.length < 126) {
566
- header = Buffer.alloc(6);
567
- header[0] = 129;
568
- header[1] = 128 | payload.length;
569
- maskKey.copy(header, 2);
570
- } else if (payload.length < 65536) {
571
- header = Buffer.alloc(8);
572
- header[0] = 129;
573
- header[1] = 128 | 126;
574
- header.writeUInt16BE(payload.length, 2);
575
- maskKey.copy(header, 4);
576
- } else {
577
- header = Buffer.alloc(14);
578
- header[0] = 129;
579
- header[1] = 128 | 127;
580
- header.writeBigUInt64BE(BigInt(payload.length), 2);
581
- maskKey.copy(header, 10);
458
+ handleMessage(msg) {
459
+ if (msg.id !== void 0) {
460
+ const pending = this.pending.get(msg.id);
461
+ if (!pending) return;
462
+ clearTimeout(pending.timer);
463
+ this.pending.delete(msg.id);
464
+ if (msg.error) {
465
+ pending.reject(new Error(msg.error.message ?? JSON.stringify(msg.error)));
466
+ } else {
467
+ pending.resolve(msg.result ?? {});
468
+ }
469
+ return;
582
470
  }
583
- this.socket.write(Buffer.concat([header, masked]));
584
- }
585
- close() {
586
- if (!this._closed) {
587
- this._closed = true;
588
- this.socket.destroy();
471
+ if (!msg.method) return;
472
+ const handlers = msg.sessionId ? this.sessionEventHandlers.get(msg.sessionId)?.get(msg.method) : this.eventHandlers.get(msg.method);
473
+ if (!handlers) return;
474
+ for (const callback of handlers) {
475
+ try {
476
+ callback(msg.params);
477
+ } catch {
478
+ }
589
479
  }
590
480
  }
591
- get closed() {
592
- return this._closed;
481
+ handleClosed() {
482
+ if (this._closed) return;
483
+ this._closed = true;
484
+ this.rejectPending("CDP connection closed");
485
+ }
486
+ rejectPending(message) {
487
+ for (const [id, pending] of this.pending) {
488
+ clearTimeout(pending.timer);
489
+ pending.reject(new Error(message));
490
+ this.pending.delete(id);
491
+ }
593
492
  }
594
493
  };
595
494
 
@@ -3056,7 +2955,7 @@ function installPageObserverInPage(maxBatches = 50) {
3056
2955
  });
3057
2956
  state.waitSince = (checkpoint, checkpointFocus, quietMs, firstChangeTimeoutMs, timeoutMs) => {
3058
2957
  const startedAt = performance.now();
3059
- return new Promise((resolve) => {
2958
+ return new Promise((resolve2) => {
3060
2959
  let completed = false;
3061
2960
  let quietTimer;
3062
2961
  let firstChangeTimer;
@@ -3121,7 +3020,7 @@ function installPageObserverInPage(maxBatches = 50) {
3121
3020
  if (structuralMutationCount > 0 && unclassifiedStructuralMutationCount > 0) {
3122
3021
  snapshotReasons.push("unclassified-structure");
3123
3022
  }
3124
- resolve({
3023
+ resolve2({
3125
3024
  mode: "delta",
3126
3025
  scope: "main-document",
3127
3026
  checkpoint,
@@ -3360,14 +3259,14 @@ function createNavigationSettlementTracker(options) {
3360
3259
  return Promise.resolve(true);
3361
3260
  if (timeoutMs <= 0 || disposed)
3362
3261
  return Promise.resolve(false);
3363
- return new Promise((resolve) => {
3262
+ return new Promise((resolve2) => {
3364
3263
  const initialDeadline = performance.now() + timeoutMs;
3365
3264
  const hardDeadline = initialDeadline + 4e3;
3366
3265
  let timer;
3367
3266
  const waiter = (didStart) => {
3368
3267
  clearTimeout(timer);
3369
3268
  startWaiters.delete(waiter);
3370
- resolve(didStart);
3269
+ resolve2(didStart);
3371
3270
  };
3372
3271
  const check2 = () => {
3373
3272
  if (started) {
@@ -3389,26 +3288,26 @@ function createNavigationSettlementTracker(options) {
3389
3288
  const waitForResult = (timeoutMs) => {
3390
3289
  if (completed)
3391
3290
  return Promise.resolve(completed);
3392
- return new Promise((resolve) => {
3291
+ return new Promise((resolve2) => {
3393
3292
  const waiter = (result) => {
3394
3293
  clearTimeout(timer);
3395
3294
  resultWaiters.delete(waiter);
3396
- resolve(result);
3295
+ resolve2(result);
3397
3296
  };
3398
3297
  const timer = setTimeout(() => {
3399
3298
  resultWaiters.delete(waiter);
3400
3299
  finish(documentComplete, false, "timeout");
3401
3300
  if (completed)
3402
- resolve(completed);
3301
+ resolve2(completed);
3403
3302
  }, timeoutMs);
3404
3303
  resultWaiters.add(waiter);
3405
3304
  });
3406
3305
  };
3407
3306
  const waitForNetworkIdle = (timeoutMs) => {
3408
3307
  const waitStartedAt = performance.now();
3409
- return new Promise((resolve) => {
3308
+ return new Promise((resolve2) => {
3410
3309
  const finishWait = (ready, settledBy) => {
3411
- resolve({
3310
+ resolve2({
3412
3311
  ready,
3413
3312
  settledBy,
3414
3313
  pendingRequests: pendingRequests2.size,
@@ -4848,7 +4747,7 @@ async function setupBufferListeners(client2, buffers) {
4848
4747
 
4849
4748
  // src/cdp-backend/discover.ts
4850
4749
  import fs from "node:fs";
4851
- import http2 from "node:http";
4750
+ import http from "node:http";
4852
4751
  import os from "node:os";
4853
4752
  import path from "node:path";
4854
4753
  function chromeUserDataDir() {
@@ -4876,13 +4775,13 @@ function readDevToolsActivePort(userDataDir = chromeUserDataDir()) {
4876
4775
  }
4877
4776
  }
4878
4777
  async function discoverTargets(port = 9222) {
4879
- return new Promise((resolve, reject) => {
4880
- const req = http2.get(`http://localhost:${port}/json`, (res) => {
4778
+ return new Promise((resolve2, reject) => {
4779
+ const req = http.get(`http://localhost:${port}/json`, (res) => {
4881
4780
  let data = "";
4882
4781
  res.on("data", (chunk) => data += chunk);
4883
4782
  res.on("end", () => {
4884
4783
  try {
4885
- resolve(JSON.parse(data));
4784
+ resolve2(JSON.parse(data));
4886
4785
  } catch (e) {
4887
4786
  reject(new Error(`Failed to parse CDP targets: ${e}`));
4888
4787
  }
@@ -4909,6 +4808,8 @@ async function isCdpAvailable(port = 9222, userDataDir) {
4909
4808
  var CdpSession = class {
4910
4809
  port;
4911
4810
  userDataDir;
4811
+ endpointUrl;
4812
+ endpointResolver;
4912
4813
  browserClient = null;
4913
4814
  pageSessions = /* @__PURE__ */ new Map();
4914
4815
  // targetId → PageSession
@@ -4922,6 +4823,8 @@ var CdpSession = class {
4922
4823
  }
4923
4824
  this.port = options.port ?? 9222;
4924
4825
  this.userDataDir = options.userDataDir;
4826
+ this.endpointUrl = options.endpointUrl;
4827
+ this.endpointResolver = options.endpointResolver;
4925
4828
  }
4926
4829
  // ── Browser-level ──────────────────────────────────────────────────────────
4927
4830
  async getBrowserClient() {
@@ -4999,7 +4902,13 @@ var CdpSession = class {
4999
4902
  5e3
5000
4903
  );
5001
4904
  client2 = browser.createChildSession(sessionId);
5002
- } catch {
4905
+ } catch (error48) {
4906
+ if (this.isRemote()) {
4907
+ throw new Error(
4908
+ `Failed to attach to remote CDP target ${target.id}: ${error48 instanceof Error ? error48.message : String(error48)}`,
4909
+ { cause: error48 }
4910
+ );
4911
+ }
5003
4912
  client2 = await CdpClient.connect(target.webSocketDebuggerUrl);
5004
4913
  }
5005
4914
  let tabId;
@@ -5029,6 +4938,7 @@ var CdpSession = class {
5029
4938
  return session;
5030
4939
  }
5031
4940
  async findTargetById(targetId) {
4941
+ if (this.isRemote()) return this.findTargetByIdViaCdp(targetId);
5032
4942
  try {
5033
4943
  const targets = await discoverTargets(this.effectivePort());
5034
4944
  return targets.find((t) => t.id === targetId && t.webSocketDebuggerUrl) ?? null;
@@ -5037,6 +4947,7 @@ var CdpSession = class {
5037
4947
  }
5038
4948
  }
5039
4949
  async findDefaultPageTarget() {
4950
+ if (this.isRemote()) return this.findDefaultPageTargetViaCdp();
5040
4951
  try {
5041
4952
  const targets = await discoverTargets(this.effectivePort());
5042
4953
  return targets.find(
@@ -5056,6 +4967,14 @@ var CdpSession = class {
5056
4967
  * Priority: DevToolsActivePort file → HTTP /json/version
5057
4968
  */
5058
4969
  async resolveBrowserWsUrl() {
4970
+ const configuredEndpoint = this.endpointResolver ? await this.endpointResolver() : this.endpointUrl;
4971
+ if (configuredEndpoint) {
4972
+ const url2 = new URL(configuredEndpoint);
4973
+ if (url2.protocol !== "ws:" && url2.protocol !== "wss:") {
4974
+ throw new Error(`Unsupported CDP endpoint protocol: ${url2.protocol}`);
4975
+ }
4976
+ return url2.toString();
4977
+ }
5059
4978
  const fileInfo = readDevToolsActivePort(this.userDataDir);
5060
4979
  if (fileInfo) {
5061
4980
  return `ws://localhost:${fileInfo.port}${fileInfo.browserWsPath}`;
@@ -5075,6 +4994,9 @@ var CdpSession = class {
5075
4994
  const port = this.effectivePort();
5076
4995
  return `ws://localhost:${port}/devtools/page/${targetId}`;
5077
4996
  }
4997
+ isRemote() {
4998
+ return Boolean(this.endpointUrl || this.endpointResolver);
4999
+ }
5078
5000
  async findTargetByIdViaCdp(targetId) {
5079
5001
  try {
5080
5002
  const browser = await this.getBrowserClient();
@@ -5114,13 +5036,13 @@ var CdpSession = class {
5114
5036
  }
5115
5037
  }
5116
5038
  fetchVersionInfo() {
5117
- return new Promise((resolve, reject) => {
5118
- const req = http3.get(`http://localhost:${this.port}/json/version`, (res) => {
5039
+ return new Promise((resolve2, reject) => {
5040
+ const req = http2.get(`http://localhost:${this.port}/json/version`, (res) => {
5119
5041
  let data = "";
5120
5042
  res.on("data", (chunk) => data += chunk);
5121
5043
  res.on("end", () => {
5122
5044
  try {
5123
- resolve(JSON.parse(data));
5045
+ resolve2(JSON.parse(data));
5124
5046
  } catch (e) {
5125
5047
  reject(new Error(`Failed to parse CDP version info: ${e}`));
5126
5048
  }
@@ -5829,15 +5751,15 @@ async function releaseCdpControl(session) {
5829
5751
 
5830
5752
  // src/cdp-backend/cdp-utils.ts
5831
5753
  import https from "node:https";
5832
- import http4 from "node:http";
5754
+ import http3 from "node:http";
5833
5755
  var TYPE_INTO_ELEMENT_SRC = typeIntoElement.toString();
5834
5756
  var CLICK_ELEMENT_VIA_JS_SRC = clickElementViaJS.toString();
5835
5757
  var GET_CLICK_COORDS_FROM_NODE_SRC = getClickCoordsFromNode.toString();
5836
5758
  function nodeFetch(urlStr, options = {}) {
5837
- return new Promise((resolve, reject) => {
5759
+ return new Promise((resolve2, reject) => {
5838
5760
  const url2 = new URL(urlStr);
5839
5761
  const isHttps = url2.protocol === "https:";
5840
- const lib = isHttps ? https : http4;
5762
+ const lib = isHttps ? https : http3;
5841
5763
  const { method = "GET", headers = {}, body, timeoutMs = 3e4 } = options;
5842
5764
  const req = lib.request(
5843
5765
  {
@@ -5857,7 +5779,7 @@ function nodeFetch(urlStr, options = {}) {
5857
5779
  res.on("data", (chunk) => chunks.push(Buffer.from(chunk)));
5858
5780
  res.on("end", () => {
5859
5781
  const bodyBuffer = Buffer.concat(chunks);
5860
- resolve({
5782
+ resolve2({
5861
5783
  status: res.statusCode ?? 0,
5862
5784
  statusText: res.statusMessage ?? "",
5863
5785
  headers: responseHeaders,
@@ -5914,7 +5836,7 @@ ${value}\r
5914
5836
  return { contentType: `multipart/form-data; boundary=${boundary}`, body };
5915
5837
  }
5916
5838
  async function waitForPageLoad(session, timeoutMs) {
5917
- return new Promise((resolve, reject) => {
5839
+ return new Promise((resolve2, reject) => {
5918
5840
  let settled = false;
5919
5841
  let removeLoadListener = () => {
5920
5842
  };
@@ -5924,7 +5846,7 @@ async function waitForPageLoad(session, timeoutMs) {
5924
5846
  clearTimeout(timer);
5925
5847
  removeLoadListener();
5926
5848
  if (error48) reject(error48);
5927
- else resolve();
5849
+ else resolve2();
5928
5850
  };
5929
5851
  const timer = setTimeout(() => {
5930
5852
  finish(new Error(`Page load timeout after ${timeoutMs}ms`));
@@ -7017,12 +6939,12 @@ function resolveCloudConnectorCommand() {
7017
6939
  };
7018
6940
  }
7019
6941
  function startCloudConnector(serverUrl) {
7020
- return new Promise((resolve) => {
6942
+ return new Promise((resolve2) => {
7021
6943
  const existingPid = findLiveConnector(serverUrl);
7022
6944
  if (existingPid) {
7023
6945
  logFn(`Reusing existing cloud connector for ${serverUrl} (pid=${existingPid})`);
7024
6946
  const entry = registerConnector(serverUrl, existingPid);
7025
- resolve({
6947
+ resolve2({
7026
6948
  success: true,
7027
6949
  pid: existingPid,
7028
6950
  ...entry.connectorId ? { connectorId: entry.connectorId } : {},
@@ -7055,14 +6977,14 @@ function startCloudConnector(serverUrl) {
7055
6977
  clearTimeout(startupTimer);
7056
6978
  if (ok && child.pid) {
7057
6979
  const entry = registerConnector(serverUrl, child.pid, child);
7058
- resolve({
6980
+ resolve2({
7059
6981
  success: true,
7060
6982
  pid: child.pid,
7061
6983
  ...entry.connectorId ? { connectorId: entry.connectorId } : {},
7062
6984
  ...entry.name ? { name: entry.name } : {}
7063
6985
  });
7064
6986
  } else {
7065
- resolve({ success: false, error: result.error || "Failed to spawn cloud connector" });
6987
+ resolve2({ success: false, error: result.error || "Failed to spawn cloud connector" });
7066
6988
  }
7067
6989
  };
7068
6990
  child.once("error", (error48) => {
@@ -7674,7 +7596,7 @@ async function cdpWaitForTracknick(session, options = {}) {
7674
7596
  latest = await cdpGetTracknick(session);
7675
7597
  if (expected && latest === expected) return latest;
7676
7598
  if (!expected && latest && latest !== previous) return latest;
7677
- await new Promise((resolve) => setTimeout(resolve, 500));
7599
+ await new Promise((resolve2) => setTimeout(resolve2, 500));
7678
7600
  }
7679
7601
  return latest || await cdpGetTracknick(session);
7680
7602
  }
@@ -7684,7 +7606,7 @@ async function cdpOpenAndCloseTarget(cdpSession2, url2) {
7684
7606
  url: url2,
7685
7607
  background: true
7686
7608
  });
7687
- await new Promise((resolve) => setTimeout(resolve, 3500));
7609
+ await new Promise((resolve2) => setTimeout(resolve2, 3500));
7688
7610
  await browser.sendCommand("Target.closeTarget", { targetId }).catch(() => {
7689
7611
  });
7690
7612
  }
@@ -8434,7 +8356,7 @@ async function cdpPageWait(session, data) {
8434
8356
  const options = resolvePageWaitInput(data);
8435
8357
  const startedAt = Date.now();
8436
8358
  if (options.mode === "time") {
8437
- await new Promise((resolve) => setTimeout(resolve, options.time));
8359
+ await new Promise((resolve2) => setTimeout(resolve2, options.time));
8438
8360
  return { success: true, waited: true, elapsed: options.time, mode: options.mode };
8439
8361
  }
8440
8362
  const expression = options.mode === "selector" ? `!!document.querySelector(${JSON.stringify(options.selector)})` : options.condition;
@@ -8459,7 +8381,7 @@ async function cdpPageWait(session, data) {
8459
8381
  value
8460
8382
  };
8461
8383
  }
8462
- await new Promise((resolve) => setTimeout(resolve, options.interval));
8384
+ await new Promise((resolve2) => setTimeout(resolve2, options.interval));
8463
8385
  }
8464
8386
  if (options.mode === "selector") {
8465
8387
  throw new Error(
@@ -8530,7 +8452,7 @@ async function waitForHistoryIndex(session, targetIndex, timeoutMs) {
8530
8452
  }
8531
8453
  } catch {
8532
8454
  }
8533
- await new Promise((resolve) => setTimeout(resolve, 50));
8455
+ await new Promise((resolve2) => setTimeout(resolve2, 50));
8534
8456
  }
8535
8457
  throw new Error(`Page history navigation timeout after ${timeoutMs}ms`);
8536
8458
  }
@@ -8678,7 +8600,7 @@ async function readCurrentUrl(session, action) {
8678
8600
  return url2;
8679
8601
  }
8680
8602
  function reloadPageAndWait(session, timeoutMs) {
8681
- return new Promise((resolve, reject) => {
8603
+ return new Promise((resolve2, reject) => {
8682
8604
  let settled = false;
8683
8605
  let removeListener = () => {
8684
8606
  };
@@ -8688,7 +8610,7 @@ function reloadPageAndWait(session, timeoutMs) {
8688
8610
  clearTimeout(timer);
8689
8611
  removeListener();
8690
8612
  if (error48) reject(error48);
8691
- else resolve();
8613
+ else resolve2();
8692
8614
  };
8693
8615
  const timer = setTimeout(() => {
8694
8616
  finish(new Error(`Page load timeout after ${timeoutMs}ms`));
@@ -9137,6 +9059,10 @@ async function cdpExportCookies(session, domains) {
9137
9059
  async function cdpImportCookies(session, cookies) {
9138
9060
  if (cookies.length === 0) return 0;
9139
9061
  const browser = await session.getBrowserClient();
9062
+ return cdpImportCookiesWithClient(browser, cookies);
9063
+ }
9064
+ async function cdpImportCookiesWithClient(browser, cookies) {
9065
+ if (cookies.length === 0) return 0;
9140
9066
  await browser.sendCommand("Storage.setCookies", { cookies });
9141
9067
  return cookies.length;
9142
9068
  }
@@ -9263,6 +9189,7 @@ function browserSummary(record2, running) {
9263
9189
  return {
9264
9190
  browserId: record2.browserId,
9265
9191
  name: record2.name,
9192
+ provider: "local",
9266
9193
  headless: record2.headless,
9267
9194
  persistent: record2.persistent,
9268
9195
  cdpPort: record2.cdpPort,
@@ -9329,12 +9256,15 @@ function findManagedRecord(selector) {
9329
9256
  ({ record: record2 }) => record2.browserId.toLowerCase() === normalized || record2.name.toLowerCase() === normalized
9330
9257
  )?.record ?? null;
9331
9258
  }
9259
+ function hasManagedBrowser(selector) {
9260
+ return findManagedRecord(selector) !== null;
9261
+ }
9332
9262
  async function waitForActivePort(userDataDir) {
9333
9263
  const deadline = Date.now() + LAUNCH_TIMEOUT_MS;
9334
9264
  while (Date.now() < deadline) {
9335
9265
  const info = readDevToolsActivePort(userDataDir);
9336
9266
  if (info) return info.port;
9337
- await new Promise((resolve) => setTimeout(resolve, 100));
9267
+ await new Promise((resolve2) => setTimeout(resolve2, 100));
9338
9268
  }
9339
9269
  throw new Error(`Chrome did not expose a CDP port within ${LAUNCH_TIMEOUT_MS}ms`);
9340
9270
  }
@@ -9344,7 +9274,7 @@ async function waitForBrowserRegistration(browserId, expectedPid) {
9344
9274
  while (Date.now() < deadline) {
9345
9275
  const record2 = readRecord(file2);
9346
9276
  if (record2?.pid === expectedPid && isAlive(expectedPid)) return;
9347
- await new Promise((resolve) => setTimeout(resolve, 100));
9277
+ await new Promise((resolve2) => setTimeout(resolve2, 100));
9348
9278
  }
9349
9279
  throw new Error(`Managed browser ${browserId} did not register within ${LAUNCH_TIMEOUT_MS}ms`);
9350
9280
  }
@@ -9366,7 +9296,7 @@ async function stopProcess(pid, commandMarker) {
9366
9296
  signalProcess(pid, "SIGTERM", commandMarker);
9367
9297
  const deadline = Date.now() + 3e3;
9368
9298
  while (Date.now() < deadline && isManagedProcess(pid, commandMarker)) {
9369
- await new Promise((resolve) => setTimeout(resolve, 50));
9299
+ await new Promise((resolve2) => setTimeout(resolve2, 50));
9370
9300
  }
9371
9301
  if (isManagedProcess(pid, commandMarker)) signalProcess(pid, "SIGKILL", commandMarker);
9372
9302
  }
@@ -9435,6 +9365,9 @@ function acquireLaunchLock(browserId) {
9435
9365
  };
9436
9366
  }
9437
9367
  async function launchManagedBrowser(input, deps) {
9368
+ if (input.imageId !== void 0) {
9369
+ throw new Error("imageId is only supported by the AgentBay browser provider");
9370
+ }
9438
9371
  const name = validateName(input.name);
9439
9372
  const browserId = managedBrowserId(name);
9440
9373
  validateTdbankSelector(input);
@@ -9470,9 +9403,9 @@ async function launchManagedBrowser(input, deps) {
9470
9403
  stdio: ["ignore", "ignore", logFd],
9471
9404
  windowsHide: headless
9472
9405
  });
9473
- await new Promise((resolve, reject) => {
9406
+ await new Promise((resolve2, reject) => {
9474
9407
  chrome.once("error", reject);
9475
- chrome.once("spawn", resolve);
9408
+ chrome.once("spawn", resolve2);
9476
9409
  });
9477
9410
  chromePid = chrome.pid ?? 0;
9478
9411
  if (!chromePid) throw new Error("Chrome launched without a process id");
@@ -9511,9 +9444,9 @@ async function launchManagedBrowser(input, deps) {
9511
9444
  ],
9512
9445
  { detached: true, stdio: "ignore", windowsHide: true }
9513
9446
  );
9514
- await new Promise((resolve, reject) => {
9447
+ await new Promise((resolve2, reject) => {
9515
9448
  host.once("error", reject);
9516
- host.once("spawn", resolve);
9449
+ host.once("spawn", resolve2);
9517
9450
  });
9518
9451
  hostPid = host.pid ?? 0;
9519
9452
  if (!hostPid) throw new Error("Native host launched without a process id");
@@ -9566,6 +9499,450 @@ async function closeManagedBrowser(selector, options = {}) {
9566
9499
  return { browserId: record2.browserId, closed: true, profileDeleted: deleteProfile };
9567
9500
  }
9568
9501
 
9502
+ // src/agentbay-browser.ts
9503
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
9504
+ import { spawn as spawn4 } from "node:child_process";
9505
+ import { homedir as homedir2 } from "node:os";
9506
+ import { dirname as dirname3, join as join4, parse, resolve } from "node:path";
9507
+ import { fileURLToPath } from "node:url";
9508
+ var AGENTBAY_BROWSERS_SUBDIR = "agentbay-browsers";
9509
+ var DEFAULT_AGENTBAY_IMAGE_ID = "imgc-0aae4rgl8kvagdv1j";
9510
+ var sdkPromise = null;
9511
+ async function loadSdk() {
9512
+ sdkPromise ??= import("wuying-agentbay-sdk").then((sdk) => {
9513
+ sdk.setupLogger({ level: "ERROR", enableConsole: false });
9514
+ return sdk;
9515
+ });
9516
+ return sdkPromise;
9517
+ }
9518
+ async function createClient(apiKey) {
9519
+ const { AgentBay } = await loadSdk();
9520
+ return new AgentBay({ apiKey });
9521
+ }
9522
+ function agentBayBrowserId(name) {
9523
+ return `managed:agentbay:${name.toLocaleLowerCase("en-US")}`;
9524
+ }
9525
+ function envValue(text, key) {
9526
+ for (const line of text.split(/\r?\n/)) {
9527
+ const match = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)\s*$/.exec(line);
9528
+ if (!match || match[1] !== key) continue;
9529
+ let value = (match[2] ?? "").trim();
9530
+ if (value.length >= 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
9531
+ value = value.slice(1, -1);
9532
+ } else {
9533
+ value = value.replace(/\s+#.*$/, "").trim();
9534
+ }
9535
+ return value || void 0;
9536
+ }
9537
+ return void 0;
9538
+ }
9539
+ function envFilesUpward(start) {
9540
+ const files = [];
9541
+ let current = resolve(start);
9542
+ while (true) {
9543
+ files.push(join4(current, ".env"));
9544
+ const parent = dirname3(current);
9545
+ if (parent === current || current === parse(current).root) break;
9546
+ current = parent;
9547
+ }
9548
+ return files;
9549
+ }
9550
+ function resolveAgentBayApiKey() {
9551
+ const inherited = process.env.AGENTBAY_KEY?.trim() || process.env.AGENTBAY_API_KEY?.trim();
9552
+ if (inherited) return inherited;
9553
+ const moduleDir = dirname3(fileURLToPath(import.meta.url));
9554
+ const candidates = [
9555
+ ...process.env.MEARL_ENV_FILE ? [resolve(process.env.MEARL_ENV_FILE)] : [],
9556
+ ...envFilesUpward(process.cwd()),
9557
+ ...envFilesUpward(moduleDir),
9558
+ join4(homedir2(), ".mearl", ".env")
9559
+ ];
9560
+ for (const file2 of new Set(candidates)) {
9561
+ if (!existsSync4(file2)) continue;
9562
+ try {
9563
+ const text = readFileSync4(file2, "utf8");
9564
+ const value = envValue(text, "AGENTBAY_KEY") ?? envValue(text, "AGENTBAY_API_KEY");
9565
+ if (value) return value;
9566
+ } catch {
9567
+ }
9568
+ }
9569
+ throw new Error(
9570
+ "AgentBay API key not found. Set AGENTBAY_KEY in the environment, project .env, or ~/.mearl/.env."
9571
+ );
9572
+ }
9573
+ function validateAgentBayInput(input) {
9574
+ if (input.headless === true) {
9575
+ throw new Error("headless=true is not supported by the AgentBay browser provider");
9576
+ }
9577
+ if (input.executablePath) {
9578
+ throw new Error("executablePath is only supported by the local browser provider");
9579
+ }
9580
+ if (input.accountId !== void 0 || input.havanaId !== void 0 || input.site !== void 0 || typeof input.query === "string" && input.query.trim()) {
9581
+ throw new Error("TDBank login parameters are only supported by the local browser provider");
9582
+ }
9583
+ }
9584
+ function resolveAgentBayImageId(value) {
9585
+ if (value === void 0) return DEFAULT_AGENTBAY_IMAGE_ID;
9586
+ const imageId = typeof value === "string" ? value.trim() : "";
9587
+ if (!imageId || imageId.length > 128 || !/^[A-Za-z0-9][A-Za-z0-9._:-]*$/.test(imageId)) {
9588
+ throw new Error("imageId must be a valid AgentBay image alias or image ID");
9589
+ }
9590
+ return imageId;
9591
+ }
9592
+ function agentBayContextName(browserId) {
9593
+ return `mearl-browser-${hashKey(browserId)}`;
9594
+ }
9595
+ function normalizeDesktopUserAgent(value) {
9596
+ const userAgent = typeof value === "string" ? value.trim() : "";
9597
+ if (!userAgent) throw new Error("Could not determine the control browser user agent");
9598
+ return userAgent.replace(/\bHeadlessChrome\//g, "Chrome/");
9599
+ }
9600
+ function normalizeAgentBayAccessUrl(value) {
9601
+ const candidate = typeof value === "string" ? value.trim() : "";
9602
+ if (!candidate) return void 0;
9603
+ try {
9604
+ const url2 = new URL(candidate);
9605
+ return url2.protocol === "https:" || url2.protocol === "http:" ? url2.toString() : void 0;
9606
+ } catch {
9607
+ return void 0;
9608
+ }
9609
+ }
9610
+ function browserSummary2(record2, running) {
9611
+ const accessUrl = normalizeAgentBayAccessUrl(record2.accessUrl);
9612
+ return {
9613
+ browserId: record2.browserId,
9614
+ name: record2.name,
9615
+ provider: "agentbay",
9616
+ headless: false,
9617
+ persistent: record2.persistent === true,
9618
+ agentbaySessionId: record2.sessionId,
9619
+ ...record2.imageId ? { agentbayImageId: record2.imageId } : {},
9620
+ ...record2.contextId ? { agentbayContextId: record2.contextId } : {},
9621
+ ...record2.contextName ? { agentbayContextName: record2.contextName } : {},
9622
+ ...accessUrl ? { agentbayAccessUrl: accessUrl } : {},
9623
+ createdAt: record2.createdAt,
9624
+ ...record2.copiedCookies ? { copiedCookies: record2.copiedCookies } : {},
9625
+ running
9626
+ };
9627
+ }
9628
+ function listRecords2() {
9629
+ const records = [];
9630
+ for (const file2 of listRecordFiles(keyedDir(AGENTBAY_BROWSERS_SUBDIR))) {
9631
+ const record2 = readRecord(file2);
9632
+ if (!record2) {
9633
+ removeRecord(file2);
9634
+ continue;
9635
+ }
9636
+ const running = isManagedProcess(record2.pid, `--browser-id ${record2.browserId}`);
9637
+ if (!running) removeRecord(keyedConfigFile("browsers", record2.browserId));
9638
+ records.push({ record: record2, running });
9639
+ }
9640
+ return records;
9641
+ }
9642
+ function findRecord(selector) {
9643
+ const normalized = selector.trim().toLowerCase();
9644
+ return listRecords2().find(
9645
+ ({ record: record2 }) => record2.browserId.toLowerCase() === normalized || record2.name.toLowerCase() === normalized
9646
+ )?.record ?? null;
9647
+ }
9648
+ function hasAgentBayBrowser(selector) {
9649
+ return findRecord(selector) !== null;
9650
+ }
9651
+ function listAgentBayBrowsers() {
9652
+ return listRecords2().map(({ record: record2, running }) => browserSummary2(record2, running));
9653
+ }
9654
+ async function requireSession(sessionId, apiKey) {
9655
+ const agentBay = await createClient(apiKey);
9656
+ return requireSessionFromClient(agentBay, sessionId);
9657
+ }
9658
+ async function requireSessionFromClient(agentBay, sessionId) {
9659
+ const result = await agentBay.get(sessionId);
9660
+ if (!result.success || !result.session) {
9661
+ throw new Error(result.errorMessage || `AgentBay session not found: ${sessionId}`);
9662
+ }
9663
+ return result.session;
9664
+ }
9665
+ async function resolvePersistentContext(agentBay, browserId) {
9666
+ if (!agentBay.context) {
9667
+ throw new Error("The installed AgentBay SDK does not support persistent browser contexts");
9668
+ }
9669
+ const name = agentBayContextName(browserId);
9670
+ const result = await agentBay.context.get(name, true);
9671
+ if (!result.success || !result.context?.id) {
9672
+ throw new Error(result.errorMessage || `Failed to create AgentBay browser context: ${name}`);
9673
+ }
9674
+ return { id: result.context.id, name: result.context.name || name };
9675
+ }
9676
+ async function resolveAgentBayEndpoint(sessionId) {
9677
+ const apiKey = resolveAgentBayApiKey();
9678
+ const { AgentBay, GetCdpLinkRequest } = await loadSdk();
9679
+ const agentBay = new AgentBay({ apiKey });
9680
+ const response = await agentBay.client.getCdpLink(
9681
+ new GetCdpLinkRequest({ authorization: `Bearer ${apiKey}`, sessionId })
9682
+ );
9683
+ const endpoint = response.body?.success ? response.body.data?.url : void 0;
9684
+ if (!endpoint) {
9685
+ throw new Error(response.body?.message || `Failed to get AgentBay CDP link: ${sessionId}`);
9686
+ }
9687
+ const url2 = new URL(endpoint);
9688
+ if (url2.protocol !== "ws:" && url2.protocol !== "wss:") {
9689
+ throw new Error(`AgentBay returned an unsupported CDP endpoint: ${url2.protocol}`);
9690
+ }
9691
+ return url2.toString();
9692
+ }
9693
+ async function keepAgentBaySessionAlive(sessionId) {
9694
+ const session = await requireSession(sessionId, resolveAgentBayApiKey());
9695
+ const result = await session.keepAlive();
9696
+ if (result.success === false) throw new Error(result.errorMessage || "AgentBay keepalive failed");
9697
+ }
9698
+ async function initializeAgentBayBrowserSession(session, input, cookies, options = {}) {
9699
+ const initialized = await session.browser.initializeAsync(
9700
+ options.userAgent ? { userAgent: options.userAgent } : {}
9701
+ );
9702
+ if (!initialized) throw new Error("AgentBay browser initialization failed");
9703
+ const endpoint = await session.browser.getEndpointUrl();
9704
+ const cdp = await (options.connectCdp ?? ((url2) => CdpClient.connect(url2)))(endpoint);
9705
+ try {
9706
+ await cdp.sendCommand("Browser.getVersion", {}, 1e4);
9707
+ const copiedCookieCount = await cdpImportCookiesWithClient(cdp, cookies);
9708
+ if (input.url) {
9709
+ await cdp.sendCommand("Target.createTarget", { url: input.url, background: false });
9710
+ }
9711
+ return copiedCookieCount;
9712
+ } finally {
9713
+ cdp.close();
9714
+ }
9715
+ }
9716
+ async function launchAgentBayBrowser(input, deps) {
9717
+ const name = validateName(input.name);
9718
+ const browserId = agentBayBrowserId(name);
9719
+ validateAgentBayInput(input);
9720
+ const imageId = resolveAgentBayImageId(input.imageId);
9721
+ const persistent = input.persistent === true;
9722
+ const userAgentMode = resolveUserAgentMode(input.userAgentMode);
9723
+ const copyCookieDomains = input.copyCookieDomains === void 0 ? [] : normalizeCookieDomains(input.copyCookieDomains);
9724
+ const apiKey = (deps.resolveApiKey ?? resolveAgentBayApiKey)();
9725
+ const releaseLock = acquireLaunchLock(browserId);
9726
+ const persistBrowserRecord = deps.persistBrowserRecord ?? ((record2) => writeRecord(keyedConfigFile(AGENTBAY_BROWSERS_SUBDIR, record2.browserId), record2));
9727
+ let session = null;
9728
+ let context;
9729
+ let hostPid = 0;
9730
+ let recordWritten = false;
9731
+ try {
9732
+ if (findRecord(browserId)) throw new Error(`AgentBay browser already exists: ${browserId}`);
9733
+ const [cookies, userAgent] = await Promise.all([
9734
+ copyCookieDomains.length > 0 ? deps.exportCookies(copyCookieDomains) : Promise.resolve([]),
9735
+ userAgentMode === "desktop" ? deps.getBrowserUserAgent().then(normalizeDesktopUserAgent) : Promise.resolve(void 0)
9736
+ ]);
9737
+ const agentBay = await (deps.createAgentBayClient ?? createClient)(apiKey);
9738
+ context = persistent ? await resolvePersistentContext(agentBay, browserId) : void 0;
9739
+ const created = await agentBay.create({
9740
+ imageId,
9741
+ framework: "mearl",
9742
+ ...context ? {
9743
+ browserContext: {
9744
+ contextId: context.id,
9745
+ autoUpload: true,
9746
+ syncMode: "standard"
9747
+ }
9748
+ } : {}
9749
+ });
9750
+ if (!created.success || !created.session) {
9751
+ throw new Error(created.errorMessage || "AgentBay session creation failed");
9752
+ }
9753
+ session = created.session;
9754
+ const copiedCookieCount = await initializeAgentBayBrowserSession(session, input, cookies, {
9755
+ userAgent
9756
+ });
9757
+ const host = spawn4(
9758
+ process.execPath,
9759
+ [
9760
+ deps.nativeHostScript,
9761
+ "--daemon",
9762
+ "--agentbay-session-id",
9763
+ session.sessionId,
9764
+ "--browser-id",
9765
+ browserId,
9766
+ "--browser-name",
9767
+ name
9768
+ ],
9769
+ {
9770
+ detached: true,
9771
+ stdio: "ignore",
9772
+ windowsHide: true
9773
+ }
9774
+ );
9775
+ await new Promise((resolveSpawn, rejectSpawn) => {
9776
+ host.once("error", rejectSpawn);
9777
+ host.once("spawn", resolveSpawn);
9778
+ });
9779
+ hostPid = host.pid ?? 0;
9780
+ if (!hostPid) throw new Error("AgentBay native host launched without a process id");
9781
+ host.unref();
9782
+ const accessUrl = normalizeAgentBayAccessUrl(session.resourceUrl);
9783
+ const record2 = {
9784
+ pid: hostPid,
9785
+ browserId,
9786
+ name,
9787
+ sessionId: session.sessionId,
9788
+ imageId,
9789
+ persistent,
9790
+ ...context ? { contextId: context.id, contextName: context.name } : {},
9791
+ ...accessUrl ? { accessUrl } : {},
9792
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
9793
+ ...copyCookieDomains.length > 0 ? { copiedCookies: { domains: copyCookieDomains, count: copiedCookieCount } } : {}
9794
+ };
9795
+ persistBrowserRecord(record2);
9796
+ recordWritten = true;
9797
+ await waitForBrowserRegistration(browserId, hostPid);
9798
+ deps.log(`AgentBay browser launched: ${browserId}, host=${hostPid}`);
9799
+ return browserSummary2(record2, true);
9800
+ } catch (error48) {
9801
+ if (hostPid) await stopProcess(hostPid, `--browser-id ${browserId}`);
9802
+ if (recordWritten) {
9803
+ removeRecord(keyedConfigFile(AGENTBAY_BROWSERS_SUBDIR, browserId));
9804
+ removeRecord(keyedConfigFile("browsers", browserId));
9805
+ }
9806
+ if (session) {
9807
+ let cleanupError;
9808
+ try {
9809
+ const deleted = await session.delete(false);
9810
+ if (!deleted.success) {
9811
+ cleanupError = new Error(deleted.errorMessage || "AgentBay session deletion failed");
9812
+ }
9813
+ } catch (deleteError) {
9814
+ cleanupError = deleteError;
9815
+ }
9816
+ if (cleanupError) {
9817
+ const accessUrl = normalizeAgentBayAccessUrl(session.resourceUrl);
9818
+ const recoveryRecord = {
9819
+ pid: hostPid,
9820
+ browserId,
9821
+ name,
9822
+ sessionId: session.sessionId,
9823
+ imageId,
9824
+ persistent,
9825
+ ...context ? { contextId: context.id, contextName: context.name } : {},
9826
+ ...accessUrl ? { accessUrl } : {},
9827
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
9828
+ };
9829
+ let recoverySaved = false;
9830
+ let recoveryError;
9831
+ try {
9832
+ persistBrowserRecord(recoveryRecord);
9833
+ recoverySaved = true;
9834
+ } catch (writeError) {
9835
+ recoveryError = writeError;
9836
+ }
9837
+ deps.log(
9838
+ `AgentBay launch cleanup failed for ${session.sessionId}:`,
9839
+ cleanupError instanceof Error ? cleanupError.message : String(cleanupError)
9840
+ );
9841
+ if (recoveryError) {
9842
+ deps.log(
9843
+ `AgentBay recovery metadata could not be saved for ${session.sessionId}:`,
9844
+ recoveryError instanceof Error ? recoveryError.message : String(recoveryError)
9845
+ );
9846
+ }
9847
+ throw new AggregateError(
9848
+ recoveryError ? [error48, cleanupError, recoveryError] : [error48, cleanupError],
9849
+ `${error48 instanceof Error ? error48.message : String(error48)}; ` + (recoverySaved ? "AgentBay session cleanup failed and recovery metadata was retained for browser_close" : `AgentBay session cleanup failed; session ${session.sessionId} may require manual deletion`),
9850
+ { cause: error48 }
9851
+ );
9852
+ }
9853
+ }
9854
+ throw error48;
9855
+ } finally {
9856
+ releaseLock();
9857
+ }
9858
+ }
9859
+ function isMissingSessionError(error48) {
9860
+ const message = error48 instanceof Error ? error48.message : String(error48);
9861
+ return /not[ -]?found|does not exist|already deleted|invalid session/i.test(message);
9862
+ }
9863
+ async function releaseAgentBayBrowserResources(record2, agentBay, options = {}) {
9864
+ const deleteContext = record2.persistent === true && options.deleteProfile === true;
9865
+ const syncContext = record2.persistent === true && !deleteContext;
9866
+ let contextSynced = Boolean(record2.contextSyncedAt);
9867
+ let session;
9868
+ try {
9869
+ session = await requireSessionFromClient(agentBay, record2.sessionId);
9870
+ } catch (error48) {
9871
+ if (!isMissingSessionError(error48)) throw error48;
9872
+ }
9873
+ if (session) {
9874
+ if (syncContext && !contextSynced) {
9875
+ if (!session.callMcpTool) {
9876
+ throw new Error("The installed AgentBay SDK cannot stop the browser before context sync");
9877
+ }
9878
+ const stopped = await session.callMcpTool("stopChrome", {}, false);
9879
+ if (stopped.success === false) {
9880
+ throw new Error(stopped.errorMessage || "AgentBay browser shutdown failed");
9881
+ }
9882
+ if (!session.context) {
9883
+ throw new Error("The installed AgentBay SDK cannot synchronize browser context state");
9884
+ }
9885
+ const synced = await session.context.sync();
9886
+ if (!synced.success) {
9887
+ throw new Error(synced.errorMessage || "AgentBay browser context sync failed");
9888
+ }
9889
+ contextSynced = true;
9890
+ record2.contextSyncedAt = (/* @__PURE__ */ new Date()).toISOString();
9891
+ options.persistRecord?.(record2);
9892
+ }
9893
+ try {
9894
+ const result = await session.delete(false);
9895
+ if (!result.success) {
9896
+ throw new Error(result.errorMessage || "AgentBay session deletion failed");
9897
+ }
9898
+ } catch (error48) {
9899
+ if (!isMissingSessionError(error48)) throw error48;
9900
+ }
9901
+ }
9902
+ if (deleteContext) {
9903
+ if (!record2.contextId || !record2.contextName || !agentBay.context) {
9904
+ throw new Error("AgentBay browser context metadata is unavailable for profile deletion");
9905
+ }
9906
+ const result = await agentBay.context.delete({
9907
+ id: record2.contextId,
9908
+ name: record2.contextName
9909
+ });
9910
+ if (!result.success) throw new Error(result.errorMessage || "AgentBay context deletion failed");
9911
+ }
9912
+ return {
9913
+ sessionDeleted: true,
9914
+ ...syncContext ? { contextSynced } : {},
9915
+ ...deleteContext ? { contextDeleted: true } : {}
9916
+ };
9917
+ }
9918
+ async function closeAgentBayBrowser(selector, options = {}) {
9919
+ const record2 = findRecord(selector);
9920
+ if (!record2) throw new Error(`AgentBay browser not found: ${selector}`);
9921
+ const agentBay = await createClient(resolveAgentBayApiKey());
9922
+ const released = await releaseAgentBayBrowserResources(record2, agentBay, {
9923
+ ...options,
9924
+ persistRecord: (updatedRecord) => writeRecord(
9925
+ keyedConfigFile(AGENTBAY_BROWSERS_SUBDIR, updatedRecord.browserId),
9926
+ updatedRecord
9927
+ )
9928
+ });
9929
+ const closingOwnHost = record2.pid === process.pid;
9930
+ if (!closingOwnHost) await stopProcess(record2.pid, `--browser-id ${record2.browserId}`);
9931
+ removeRecord(keyedConfigFile(AGENTBAY_BROWSERS_SUBDIR, record2.browserId));
9932
+ removeRecord(keyedConfigFile("browsers", record2.browserId));
9933
+ if (closingOwnHost) {
9934
+ const timer = setTimeout(() => process.kill(process.pid, "SIGTERM"), 250);
9935
+ timer.unref();
9936
+ }
9937
+ return {
9938
+ browserId: record2.browserId,
9939
+ provider: "agentbay",
9940
+ closed: true,
9941
+ ...released,
9942
+ ...record2.persistent === true ? { profileDeleted: options.deleteProfile === true } : { profileDeleted: true }
9943
+ };
9944
+ }
9945
+
9569
9946
  // src/browser-registry.ts
9570
9947
  var BROWSERS_SUBDIR = "browsers";
9571
9948
  var STALE_RECORD_TTL_MS = 5 * 6e4;
@@ -9715,10 +10092,16 @@ function mergeBrowserInventory(connected, managed) {
9715
10092
  ...connectionFields(record2),
9716
10093
  headless: managedBrowser.headless,
9717
10094
  persistent: managedBrowser.persistent,
9718
- cdpPort: managedBrowser.cdpPort,
10095
+ provider: managedBrowser.provider,
10096
+ ..."cdpPort" in managedBrowser ? { cdpPort: managedBrowser.cdpPort } : {},
10097
+ ..."agentbaySessionId" in managedBrowser ? { agentbaySessionId: managedBrowser.agentbaySessionId } : {},
10098
+ ..."agentbayImageId" in managedBrowser && managedBrowser.agentbayImageId ? { agentbayImageId: managedBrowser.agentbayImageId } : {},
10099
+ ..."agentbayContextId" in managedBrowser && managedBrowser.agentbayContextId ? { agentbayContextId: managedBrowser.agentbayContextId } : {},
10100
+ ..."agentbayContextName" in managedBrowser && managedBrowser.agentbayContextName ? { agentbayContextName: managedBrowser.agentbayContextName } : {},
10101
+ ..."agentbayAccessUrl" in managedBrowser && managedBrowser.agentbayAccessUrl ? { agentbayAccessUrl: managedBrowser.agentbayAccessUrl } : {},
9719
10102
  createdAt: managedBrowser.createdAt,
9720
- account: managedBrowser.account,
9721
- copiedCookies: managedBrowser.copiedCookies
10103
+ ..."account" in managedBrowser ? { account: managedBrowser.account } : {},
10104
+ ..."copiedCookies" in managedBrowser ? { copiedCookies: managedBrowser.copiedCookies } : {}
9722
10105
  };
9723
10106
  });
9724
10107
  const disconnected = [...managedById.values()].sort((a, b) => b.createdAt.localeCompare(a.createdAt)).map((browser) => ({
@@ -9728,10 +10111,16 @@ function mergeBrowserInventory(connected, managed) {
9728
10111
  status: browser.running ? "running_disconnected" : "stopped",
9729
10112
  headless: browser.headless,
9730
10113
  persistent: browser.persistent,
9731
- cdpPort: browser.cdpPort,
10114
+ provider: browser.provider,
10115
+ ..."cdpPort" in browser ? { cdpPort: browser.cdpPort } : {},
10116
+ ..."agentbaySessionId" in browser ? { agentbaySessionId: browser.agentbaySessionId } : {},
10117
+ ..."agentbayImageId" in browser && browser.agentbayImageId ? { agentbayImageId: browser.agentbayImageId } : {},
10118
+ ..."agentbayContextId" in browser && browser.agentbayContextId ? { agentbayContextId: browser.agentbayContextId } : {},
10119
+ ..."agentbayContextName" in browser && browser.agentbayContextName ? { agentbayContextName: browser.agentbayContextName } : {},
10120
+ ..."agentbayAccessUrl" in browser && browser.agentbayAccessUrl ? { agentbayAccessUrl: browser.agentbayAccessUrl } : {},
9732
10121
  createdAt: browser.createdAt,
9733
- account: browser.account,
9734
- copiedCookies: browser.copiedCookies
10122
+ ..."account" in browser ? { account: browser.account } : {},
10123
+ ..."copiedCookies" in browser ? { copiedCookies: browser.copiedCookies } : {}
9735
10124
  }));
9736
10125
  browsers.push(...disconnected);
9737
10126
  return {
@@ -9741,7 +10130,7 @@ function mergeBrowserInventory(connected, managed) {
9741
10130
  };
9742
10131
  }
9743
10132
  function listBrowserInventory() {
9744
- const managed = listManagedBrowsers();
10133
+ const managed = [...listManagedBrowsers(), ...listAgentBayBrowsers()];
9745
10134
  return mergeBrowserInventory(listRegisteredBrowsers(), managed);
9746
10135
  }
9747
10136
 
@@ -9752,7 +10141,7 @@ import os2 from "node:os";
9752
10141
  import { randomUUID as randomUUID4 } from "node:crypto";
9753
10142
 
9754
10143
  // src/version.ts
9755
- var NATIVE_HOST_VERSION = true ? "2.4.0" : "unknown";
10144
+ var NATIVE_HOST_VERSION = true ? "2.5.1" : "unknown";
9756
10145
 
9757
10146
  // src/analytics.ts
9758
10147
  var MEASUREMENT_ID = "G-W84LJDZ6PH";
@@ -9936,14 +10325,14 @@ function selectedPath(stdout) {
9936
10325
  const value = stdout.replace(/[\r\n]+$/, "");
9937
10326
  return value || null;
9938
10327
  }
9939
- var runDirectoryPickerCommand = (command, args) => new Promise((resolve, reject) => {
10328
+ var runDirectoryPickerCommand = (command, args) => new Promise((resolve2, reject) => {
9940
10329
  execFile(command, args, { encoding: "utf8", windowsHide: true }, (error48, stdout) => {
9941
10330
  if (!error48) {
9942
- resolve(selectedPath(stdout));
10331
+ resolve2(selectedPath(stdout));
9943
10332
  return;
9944
10333
  }
9945
10334
  if (error48.code === 1) {
9946
- resolve(null);
10335
+ resolve2(null);
9947
10336
  return;
9948
10337
  }
9949
10338
  reject(error48);
@@ -10614,17 +11003,17 @@ var AgentChatManager = class {
10614
11003
  // src/agent/codex-provider.ts
10615
11004
  import os3 from "node:os";
10616
11005
  import readline from "node:readline";
10617
- import { spawn as spawn6 } from "node:child_process";
11006
+ import { spawn as spawn7 } from "node:child_process";
10618
11007
 
10619
11008
  // src/agent/executable-resolver.ts
10620
11009
  import fs6 from "node:fs";
10621
- import { spawn as spawn5 } from "node:child_process";
11010
+ import { spawn as spawn6 } from "node:child_process";
10622
11011
  import path7 from "node:path";
10623
11012
 
10624
11013
  // ../setup/dist/process.js
10625
11014
  import fs5 from "node:fs";
10626
11015
  import path6 from "node:path";
10627
- import { spawn as spawn4 } from "node:child_process";
11016
+ import { spawn as spawn5 } from "node:child_process";
10628
11017
  var MAX_CAPTURE_LENGTH = 1e6;
10629
11018
  function isExecutable(filePath, platform = process.platform) {
10630
11019
  try {
@@ -10686,7 +11075,7 @@ function prependPathDirectory(directory, env = process.env) {
10686
11075
  function findExecutable(name, options = {}) {
10687
11076
  const env = options.env ?? process.env;
10688
11077
  const platform = options.platform ?? process.platform;
10689
- const existsSync4 = options.existsSync ?? ((candidate) => isExecutable(candidate, platform));
11078
+ const existsSync5 = options.existsSync ?? ((candidate) => isExecutable(candidate, platform));
10690
11079
  const pathKey = Object.keys(env).find((key) => key.toLowerCase() === "path") || "PATH";
10691
11080
  const directories = [
10692
11081
  ...options.extraDirectories ?? [],
@@ -10695,16 +11084,16 @@ function findExecutable(name, options = {}) {
10695
11084
  for (const directory of directories) {
10696
11085
  for (const executableName of executableNames(name, platform)) {
10697
11086
  const candidate = path6.join(directory, executableName);
10698
- if (existsSync4(candidate))
11087
+ if (existsSync5(candidate))
10699
11088
  return candidate;
10700
11089
  }
10701
11090
  }
10702
11091
  return null;
10703
11092
  }
10704
- var runCommand = (command, args, options = {}) => new Promise((resolve, reject) => {
11093
+ var runCommand = (command, args, options = {}) => new Promise((resolve2, reject) => {
10705
11094
  const capture = options.capture === true;
10706
11095
  const launch = resolveSpawnCommand(command, args);
10707
- const child = spawn4(launch.command, launch.args, {
11096
+ const child = spawn5(launch.command, launch.args, {
10708
11097
  cwd: options.cwd,
10709
11098
  env: options.env,
10710
11099
  stdio: capture ? ["ignore", "pipe", "pipe"] : "inherit",
@@ -10723,7 +11112,7 @@ var runCommand = (command, args, options = {}) => new Promise((resolve, reject)
10723
11112
  if (error48)
10724
11113
  reject(error48);
10725
11114
  else
10726
- resolve({ code, stdout, stderr });
11115
+ resolve2({ code, stdout, stderr });
10727
11116
  };
10728
11117
  const timer = options.timeoutMs === void 0 ? void 0 : setTimeout(() => {
10729
11118
  child.kill("SIGTERM");
@@ -10845,11 +11234,11 @@ function executableFileCandidates(candidates) {
10845
11234
  return executableCandidates;
10846
11235
  }
10847
11236
  function probeAgentExecutable(executable, options) {
10848
- return new Promise((resolve) => {
11237
+ return new Promise((resolve2) => {
10849
11238
  let settled = false;
10850
11239
  let stderr = "";
10851
11240
  const launch = resolveSpawnCommand(executable, options.args);
10852
- const child = spawn5(launch.command, launch.args, {
11241
+ const child = spawn6(launch.command, launch.args, {
10853
11242
  stdio: ["ignore", "ignore", "pipe"],
10854
11243
  windowsHide: true,
10855
11244
  env: prependNodePath()
@@ -10858,7 +11247,7 @@ function probeAgentExecutable(executable, options) {
10858
11247
  if (settled) return;
10859
11248
  settled = true;
10860
11249
  clearTimeout(timer);
10861
- resolve(error48);
11250
+ resolve2(error48);
10862
11251
  };
10863
11252
  const timer = setTimeout(() => {
10864
11253
  child.kill("SIGTERM");
@@ -10989,13 +11378,13 @@ function externalOpenCommand(url2, platform = process.platform) {
10989
11378
  }
10990
11379
  function openExternalUrl(url2) {
10991
11380
  const { command, args } = externalOpenCommand(url2);
10992
- return new Promise((resolve, reject) => {
10993
- const child = spawn6(command, args, { stdio: "ignore", windowsHide: true });
11381
+ return new Promise((resolve2, reject) => {
11382
+ const child = spawn7(command, args, { stdio: "ignore", windowsHide: true });
10994
11383
  child.once("error", (error48) => {
10995
11384
  reject(new Error(`Unable to open Codex App: ${error48.message}`));
10996
11385
  });
10997
11386
  child.once("close", (code) => {
10998
- if (code === 0) resolve();
11387
+ if (code === 0) resolve2();
10999
11388
  else reject(new Error("Unable to open Codex App. Make sure it is installed."));
11000
11389
  });
11001
11390
  });
@@ -11162,7 +11551,7 @@ var CodexAgentProvider = class {
11162
11551
  url: `data:${image.mimeType};base64,${image.data}`
11163
11552
  }))
11164
11553
  ],
11165
- summary: "concise"
11554
+ summary: "detailed"
11166
11555
  });
11167
11556
  const turnId = result?.turn?.id;
11168
11557
  if (typeof turnId !== "string" || !turnId) {
@@ -11224,7 +11613,7 @@ var CodexAgentProvider = class {
11224
11613
  const launch = resolveSpawnCommand(executable, ["app-server", "--listen", "stdio://"]);
11225
11614
  const childEnv = prependNodePath();
11226
11615
  if (this.mearlSocketPath) childEnv.MEARL_SOCKET_PATH = this.mearlSocketPath;
11227
- const child = spawn6(launch.command, launch.args, {
11616
+ const child = spawn7(launch.command, launch.args, {
11228
11617
  stdio: ["pipe", "pipe", "pipe"],
11229
11618
  windowsHide: true,
11230
11619
  env: childEnv
@@ -11316,12 +11705,12 @@ var CodexAgentProvider = class {
11316
11705
  }
11317
11706
  request(method, params) {
11318
11707
  const id = this.nextRequestId++;
11319
- return new Promise((resolve, reject) => {
11708
+ return new Promise((resolve2, reject) => {
11320
11709
  const timer = setTimeout(() => {
11321
11710
  this.pendingRequests.delete(id);
11322
11711
  reject(new Error(`Codex request timed out: ${method}`));
11323
11712
  }, REQUEST_TIMEOUT_MS);
11324
- this.pendingRequests.set(id, { resolve, reject, timer });
11713
+ this.pendingRequests.set(id, { resolve: resolve2, reject, timer });
11325
11714
  try {
11326
11715
  this.write({ id, method, params });
11327
11716
  } catch (error48) {
@@ -11395,7 +11784,7 @@ var CodexAgentProvider = class {
11395
11784
  import fs7 from "node:fs";
11396
11785
  import os4 from "node:os";
11397
11786
  import path8 from "node:path";
11398
- import { spawn as spawn7 } from "node:child_process";
11787
+ import { spawn as spawn8 } from "node:child_process";
11399
11788
  import { Readable, Writable } from "node:stream";
11400
11789
 
11401
11790
  // ../../node_modules/.pnpm/@agentclientprotocol+sdk@1.2.1_zod@4.3.6/node_modules/@agentclientprotocol/sdk/dist/schema/index.js
@@ -11631,7 +12020,7 @@ __export(external_exports, {
11631
12020
  object: () => object,
11632
12021
  optional: () => optional,
11633
12022
  overwrite: () => _overwrite,
11634
- parse: () => parse2,
12023
+ parse: () => parse3,
11635
12024
  parseAsync: () => parseAsync2,
11636
12025
  partialRecord: () => partialRecord,
11637
12026
  pipe: () => pipe,
@@ -11950,7 +12339,7 @@ __export(core_exports2, {
11950
12339
  isValidJWT: () => isValidJWT,
11951
12340
  locales: () => locales_exports,
11952
12341
  meta: () => meta,
11953
- parse: () => parse,
12342
+ parse: () => parse2,
11954
12343
  parseAsync: () => parseAsync,
11955
12344
  prettifyError: () => prettifyError,
11956
12345
  process: () => process2,
@@ -12873,7 +13262,7 @@ var _parse = (_Err) => (schema, value, _ctx, _params) => {
12873
13262
  }
12874
13263
  return result.value;
12875
13264
  };
12876
- var parse = /* @__PURE__ */ _parse($ZodRealError);
13265
+ var parse2 = /* @__PURE__ */ _parse($ZodRealError);
12877
13266
  var _parseAsync = (_Err) => async (schema, value, _ctx, params) => {
12878
13267
  const ctx = _ctx ? Object.assign(_ctx, { async: true }) : { async: true };
12879
13268
  let result = schema._zod.run({ value, issues: [] }, ctx);
@@ -15546,10 +15935,10 @@ var $ZodFunction = /* @__PURE__ */ $constructor("$ZodFunction", (inst, def) => {
15546
15935
  throw new Error("implement() must be called with a function");
15547
15936
  }
15548
15937
  return function(...args) {
15549
- const parsedArgs = inst._def.input ? parse(inst._def.input, args) : args;
15938
+ const parsedArgs = inst._def.input ? parse2(inst._def.input, args) : args;
15550
15939
  const result = Reflect.apply(func, this, parsedArgs);
15551
15940
  if (inst._def.output) {
15552
- return parse(inst._def.output, result);
15941
+ return parse2(inst._def.output, result);
15553
15942
  }
15554
15943
  return result;
15555
15944
  };
@@ -23598,7 +23987,7 @@ var ZodRealError = $constructor("ZodError", initializer2, {
23598
23987
  });
23599
23988
 
23600
23989
  // ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/classic/parse.js
23601
- var parse2 = /* @__PURE__ */ _parse(ZodRealError);
23990
+ var parse3 = /* @__PURE__ */ _parse(ZodRealError);
23602
23991
  var parseAsync2 = /* @__PURE__ */ _parseAsync(ZodRealError);
23603
23992
  var safeParse2 = /* @__PURE__ */ _safeParse(ZodRealError);
23604
23993
  var safeParseAsync2 = /* @__PURE__ */ _safeParseAsync(ZodRealError);
@@ -23641,7 +24030,7 @@ var ZodType = /* @__PURE__ */ $constructor("ZodType", (inst, def) => {
23641
24030
  reg.add(inst, meta3);
23642
24031
  return inst;
23643
24032
  });
23644
- inst.parse = (data, params) => parse2(inst, data, params, { callee: inst.parse });
24033
+ inst.parse = (data, params) => parse3(inst, data, params, { callee: inst.parse });
23645
24034
  inst.safeParse = (data, params) => safeParse2(inst, data, params);
23646
24035
  inst.parseAsync = async (data, params) => parseAsync2(inst, data, params, { callee: inst.parseAsync });
23647
24036
  inst.safeParseAsync = async (data, params) => safeParseAsync2(inst, data, params);
@@ -27200,12 +27589,12 @@ var Connection = class {
27200
27589
  const id = this.nextRequestId++;
27201
27590
  let cancel = () => {
27202
27591
  };
27203
- const responsePromise = new Promise((resolve, reject) => {
27592
+ const responsePromise = new Promise((resolve2, reject) => {
27204
27593
  const pendingResponse = {
27205
27594
  resolve: (response) => {
27206
27595
  try {
27207
27596
  const value = mapResponse ? mapResponse(response) : response;
27208
- resolve(value);
27597
+ resolve2(value);
27209
27598
  } catch (error48) {
27210
27599
  reject(error48);
27211
27600
  }
@@ -27283,8 +27672,8 @@ var Connection = class {
27283
27672
  initialize(stream, handlers) {
27284
27673
  this.stream = stream;
27285
27674
  this.staticHandlers = handlers;
27286
- this.closedPromise = new Promise((resolve) => {
27287
- this.abortController.signal.addEventListener("abort", () => resolve());
27675
+ this.closedPromise = new Promise((resolve2) => {
27676
+ this.abortController.signal.addEventListener("abort", () => resolve2());
27288
27677
  });
27289
27678
  void this.receive();
27290
27679
  }
@@ -27509,13 +27898,13 @@ var ConnectionBuilder = class {
27509
27898
  /**
27510
27899
  * Adds a typed request handler for one method.
27511
27900
  */
27512
- onReceiveRequest(method, parse3, handler) {
27901
+ onReceiveRequest(method, parse4, handler) {
27513
27902
  return this.withHandler({
27514
27903
  handleMessage: async (message, cx) => {
27515
27904
  if (message.kind !== "request" || message.method !== method) {
27516
27905
  return Handled.no(message);
27517
27906
  }
27518
- const request = parse3(message.params);
27907
+ const request = parse4(message.params);
27519
27908
  return await handler(request, message.responder, cx) ?? Handled.yes();
27520
27909
  },
27521
27910
  describe: () => `${this.connectionName ?? "request"}:${method}`
@@ -27524,13 +27913,13 @@ var ConnectionBuilder = class {
27524
27913
  /**
27525
27914
  * Adds a typed notification handler for one method.
27526
27915
  */
27527
- onReceiveNotification(method, parse3, handler) {
27916
+ onReceiveNotification(method, parse4, handler) {
27528
27917
  return this.withHandler({
27529
27918
  handleMessage: async (message, cx) => {
27530
27919
  if (message.kind !== "notification" || message.method !== method) {
27531
27920
  return Handled.no(message);
27532
27921
  }
27533
- const notification = parse3(message.params);
27922
+ const notification = parse4(message.params);
27534
27923
  return await handler(notification, cx) ?? Handled.yes();
27535
27924
  },
27536
27925
  describe: () => `${this.connectionName ?? "notification"}:${method}`
@@ -28072,8 +28461,8 @@ var AsyncQueue = class {
28072
28461
  if (this.failed) {
28073
28462
  return Promise.reject(this.failure);
28074
28463
  }
28075
- return new Promise((resolve, reject) => {
28076
- this.waiters.push({ resolve, reject });
28464
+ return new Promise((resolve2, reject) => {
28465
+ this.waiters.push({ resolve: resolve2, reject });
28077
28466
  });
28078
28467
  }
28079
28468
  };
@@ -29184,7 +29573,7 @@ ${message}` : message;
29184
29573
  }
29185
29574
  const { executable } = resolution;
29186
29575
  const launch = resolveSpawnCommand(executable, ["--acp"]);
29187
- const child = spawn7(launch.command, launch.args, {
29576
+ const child = spawn8(launch.command, launch.args, {
29188
29577
  stdio: ["pipe", "pipe", "pipe"],
29189
29578
  windowsHide: true,
29190
29579
  env: prependNodePath()
@@ -29452,10 +29841,10 @@ ${message}` : message;
29452
29841
  if (requestId === null) {
29453
29842
  return Promise.resolve({ outcome: { outcome: "cancelled" } });
29454
29843
  }
29455
- return new Promise((resolve) => {
29844
+ return new Promise((resolve2) => {
29456
29845
  this.pendingPermissions.set(requestId, {
29457
29846
  providerSessionId: request.sessionId,
29458
- resolve
29847
+ resolve: resolve2
29459
29848
  });
29460
29849
  this.emit({
29461
29850
  providerSessionId: request.sessionId,
@@ -29502,7 +29891,7 @@ ${message}` : message;
29502
29891
  }
29503
29892
  }
29504
29893
  withTimeout(promise2, operation) {
29505
- return new Promise((resolve, reject) => {
29894
+ return new Promise((resolve2, reject) => {
29506
29895
  const timer = setTimeout(
29507
29896
  () => reject(new Error(`${operation} timed out`)),
29508
29897
  REQUEST_TIMEOUT_MS2
@@ -29510,7 +29899,7 @@ ${message}` : message;
29510
29899
  promise2.then(
29511
29900
  (result) => {
29512
29901
  clearTimeout(timer);
29513
- resolve(result);
29902
+ resolve2(result);
29514
29903
  },
29515
29904
  (error48) => {
29516
29905
  clearTimeout(timer);
@@ -29529,9 +29918,9 @@ ${message}` : message;
29529
29918
  });
29530
29919
  }
29531
29920
  runSkillsList(executable, cwd) {
29532
- return new Promise((resolve, reject) => {
29921
+ return new Promise((resolve2, reject) => {
29533
29922
  const launch = resolveSpawnCommand(executable, ["skill", "list", "--all"]);
29534
- const child = spawn7(launch.command, launch.args, {
29923
+ const child = spawn8(launch.command, launch.args, {
29535
29924
  cwd,
29536
29925
  stdio: ["ignore", "pipe", "pipe"],
29537
29926
  windowsHide: true,
@@ -29545,7 +29934,7 @@ ${message}` : message;
29545
29934
  settled = true;
29546
29935
  clearTimeout(timer);
29547
29936
  if (error48) reject(error48);
29548
- else resolve(stdout);
29937
+ else resolve2(stdout);
29549
29938
  };
29550
29939
  const timer = setTimeout(() => {
29551
29940
  child.kill("SIGTERM");
@@ -29580,12 +29969,12 @@ ${message}` : message;
29580
29969
  import fs8 from "node:fs";
29581
29970
  import os5 from "node:os";
29582
29971
  import path9 from "node:path";
29583
- import { spawn as spawn8 } from "node:child_process";
29972
+ import { spawn as spawn9 } from "node:child_process";
29584
29973
  var COMMAND_TIMEOUT_MS = 12e4;
29585
29974
  var MAX_OUTPUT_LENGTH = 1e6;
29586
29975
  var ANSI_ESCAPE_PATTERN = new RegExp(String.raw`\u001B\[[0-?]*[ -/]*[@-~]`, "g");
29587
- function existingExecutable(candidates, existsSync4) {
29588
- return candidates.find((candidate) => candidate && existsSync4(candidate)) ?? null;
29976
+ function existingExecutable(candidates, existsSync5) {
29977
+ return candidates.find((candidate) => candidate && existsSync5(candidate)) ?? null;
29589
29978
  }
29590
29979
  function stripAnsi(value) {
29591
29980
  return value.replace(ANSI_ESCAPE_PATTERN, "");
@@ -29594,7 +29983,7 @@ function resolveAliSkillsExecutable(options = {}) {
29594
29983
  const env = options.env ?? process.env;
29595
29984
  const home = (options.homedir ?? os5.homedir)();
29596
29985
  const platform = options.platform ?? process.platform;
29597
- const existsSync4 = options.existsSync ?? ((candidate) => isExecutable(candidate, platform));
29986
+ const existsSync5 = options.existsSync ?? ((candidate) => isExecutable(candidate, platform));
29598
29987
  const processExecPath = options.processExecPath ?? process.execPath;
29599
29988
  const readdirSync2 = options.readdirSync ?? fs8.readdirSync;
29600
29989
  const names = executableNames("ali-skills", platform);
@@ -29616,11 +30005,11 @@ function resolveAliSkillsExecutable(options = {}) {
29616
30005
  ...platform === "win32" ? [] : ["/opt/homebrew/bin/ali-skills", "/usr/local/bin/ali-skills", "/usr/bin/ali-skills"],
29617
30006
  ...pathCandidates
29618
30007
  ];
29619
- return existingExecutable(candidates, existsSync4);
30008
+ return existingExecutable(candidates, existsSync5);
29620
30009
  }
29621
- var runAliSkillsCli = (executable, args, cwd) => new Promise((resolve, reject) => {
30010
+ var runAliSkillsCli = (executable, args, cwd) => new Promise((resolve2, reject) => {
29622
30011
  const launch = resolveSpawnCommand(executable, args);
29623
- const child = spawn8(launch.command, launch.args, {
30012
+ const child = spawn9(launch.command, launch.args, {
29624
30013
  cwd,
29625
30014
  env: prependNodePath(prependCommandPath(executable)),
29626
30015
  stdio: ["ignore", "pipe", "pipe"],
@@ -29638,7 +30027,7 @@ var runAliSkillsCli = (executable, args, cwd) => new Promise((resolve, reject) =
29638
30027
  if (error48)
29639
30028
  reject(error48);
29640
30029
  else
29641
- resolve({ stdout, stderr });
30030
+ resolve2({ stdout, stderr });
29642
30031
  };
29643
30032
  const timer = setTimeout(() => {
29644
30033
  child.kill("SIGTERM");
@@ -29826,7 +30215,7 @@ function createExtensionRequestMessage(id, action, data, trustedInternal = false
29826
30215
  // src/host-updater.ts
29827
30216
  import fs10 from "node:fs";
29828
30217
  import path11 from "node:path";
29829
- import { fileURLToPath } from "node:url";
30218
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
29830
30219
 
29831
30220
  // ../setup/dist/packageManager.js
29832
30221
  import fs9 from "node:fs";
@@ -29834,8 +30223,8 @@ import path10 from "node:path";
29834
30223
  function normalizePath(filePath) {
29835
30224
  return filePath.replaceAll("\\", "/").replace(/\/+$/, "");
29836
30225
  }
29837
- function firstExisting(candidates, existsSync4) {
29838
- return candidates.find((candidate) => existsSync4(candidate));
30226
+ function firstExisting(candidates, existsSync5) {
30227
+ return candidates.find((candidate) => existsSync5(candidate));
29839
30228
  }
29840
30229
  function fallbackCommand(name, platform) {
29841
30230
  if (platform !== "win32")
@@ -29847,7 +30236,7 @@ function fallbackCommand(name, platform) {
29847
30236
  function resolvePackageManagerInstallation(packageRoot, packageName, options = {}) {
29848
30237
  const env = options.env ?? process.env;
29849
30238
  const platform = options.platform ?? process.platform;
29850
- const existsSync4 = options.existsSync ?? ((candidate) => isExecutable(candidate, platform));
30239
+ const existsSync5 = options.existsSync ?? ((candidate) => isExecutable(candidate, platform));
29851
30240
  const processExecPath = options.processExecPath ?? process.execPath;
29852
30241
  const normalizedRoot = normalizePath(path10.resolve(packageRoot));
29853
30242
  const lowerRoot = normalizedRoot.toLowerCase();
@@ -29858,13 +30247,13 @@ function resolvePackageManagerInstallation(packageRoot, packageName, options = {
29858
30247
  const command = firstExisting([
29859
30248
  ...executableNames("pnpm", platform).map((name) => path10.join(pnpmHome, name)),
29860
30249
  ...executableNames("pnpm", platform).map((name) => path10.join(path10.dirname(processExecPath), name))
29861
- ], existsSync4) ?? fallbackCommand("pnpm", platform);
30250
+ ], existsSync5) ?? fallbackCommand("pnpm", platform);
29862
30251
  return { manager: "pnpm", command, binDirectory: pnpmHome };
29863
30252
  }
29864
30253
  const bunMarker = `/.bun/install/global/node_modules/${lowerPackageName}`;
29865
30254
  if (lowerRoot.endsWith(bunMarker)) {
29866
30255
  const bunHome = normalizedRoot.slice(0, lowerRoot.indexOf("/.bun/"));
29867
- const command = firstExisting(executableNames("bun", platform).map((name) => path10.join(bunHome, ".bun", "bin", name)), existsSync4) ?? fallbackCommand("bun", platform);
30256
+ const command = firstExisting(executableNames("bun", platform).map((name) => path10.join(bunHome, ".bun", "bin", name)), existsSync5) ?? fallbackCommand("bun", platform);
29868
30257
  return {
29869
30258
  manager: "bun",
29870
30259
  command,
@@ -29883,12 +30272,12 @@ function resolvePackageManagerInstallation(packageRoot, packageName, options = {
29883
30272
  const command = firstExisting([
29884
30273
  ...executableNames("yarn", platform).map((name) => path10.join(binDirectory, name)),
29885
30274
  ...executableNames("yarn", platform).map((name) => path10.join(path10.dirname(processExecPath), name))
29886
- ], existsSync4) ?? findExecutable("yarn", { env, existsSync: existsSync4, platform }) ?? fallbackCommand("yarn", platform);
30275
+ ], existsSync5) ?? findExecutable("yarn", { env, existsSync: existsSync5, platform }) ?? fallbackCommand("yarn", platform);
29887
30276
  return { manager: "yarn", command, binDirectory };
29888
30277
  }
29889
30278
  if (lowerRoot.includes("/.volta/tools/image/packages/") && lowerRoot.endsWith(`/lib/node_modules/${lowerPackageName}`)) {
29890
30279
  const voltaHome = normalizedRoot.slice(0, lowerRoot.indexOf("/.volta/"));
29891
- const command = firstExisting(executableNames("volta", platform).map((name) => path10.join(voltaHome, ".volta", "bin", name)), existsSync4) ?? fallbackCommand("volta", platform);
30280
+ const command = firstExisting(executableNames("volta", platform).map((name) => path10.join(voltaHome, ".volta", "bin", name)), existsSync5) ?? fallbackCommand("volta", platform);
29892
30281
  return {
29893
30282
  manager: "volta",
29894
30283
  command,
@@ -29901,7 +30290,7 @@ function resolvePackageManagerInstallation(packageRoot, packageName, options = {
29901
30290
  const command = firstExisting([
29902
30291
  ...executableNames("npm", platform).map((name) => path10.join(prefix, "bin", name)),
29903
30292
  ...executableNames("npm", platform).map((name) => path10.join(path10.dirname(processExecPath), name))
29904
- ], existsSync4) ?? fallbackCommand("npm", platform);
30293
+ ], existsSync5) ?? fallbackCommand("npm", platform);
29905
30294
  return { manager: "npm", command, binDirectory: path10.join(prefix, "bin") };
29906
30295
  }
29907
30296
  if (platform === "win32" && env.APPDATA) {
@@ -29911,7 +30300,7 @@ function resolvePackageManagerInstallation(packageRoot, packageName, options = {
29911
30300
  const command = firstExisting([
29912
30301
  ...executableNames("npm", platform).map((name) => path10.join(binDirectory, name)),
29913
30302
  ...executableNames("npm", platform).map((name) => path10.join(path10.dirname(processExecPath), name))
29914
- ], existsSync4) ?? fallbackCommand("npm", platform);
30303
+ ], existsSync5) ?? fallbackCommand("npm", platform);
29915
30304
  return { manager: "npm", command, binDirectory };
29916
30305
  }
29917
30306
  }
@@ -30002,7 +30391,7 @@ var NativeHostUpdateFailure = class extends Error {
30002
30391
  }
30003
30392
  };
30004
30393
  function wait(delayMs) {
30005
- return new Promise((resolve) => setTimeout(resolve, delayMs));
30394
+ return new Promise((resolve2) => setTimeout(resolve2, delayMs));
30006
30395
  }
30007
30396
  function readLockState(lockPath) {
30008
30397
  try {
@@ -30115,7 +30504,7 @@ function currentNativeHostPackageRoot(entryPath) {
30115
30504
  return null;
30116
30505
  }
30117
30506
  function currentPackageManagerInstallation(environment, nodePath, platform) {
30118
- const packageRoot = currentNativeHostPackageRoot(fileURLToPath(import.meta.url));
30507
+ const packageRoot = currentNativeHostPackageRoot(fileURLToPath2(import.meta.url));
30119
30508
  if (!packageRoot) return null;
30120
30509
  return resolvePackageManagerInstallation(packageRoot, "@mearl/native-host", {
30121
30510
  env: environment,
@@ -30245,6 +30634,7 @@ function positivePort(value, fallback) {
30245
30634
  }
30246
30635
  var CDP_PORT = positivePort(cliOption("--cdp-port"), 9222);
30247
30636
  var CDP_USER_DATA_DIR = cliOption("--cdp-user-data-dir");
30637
+ var AGENTBAY_SESSION_ID = cliOption("--agentbay-session-id");
30248
30638
  var CDP_BROWSER_ID = cliOption("--browser-id") ?? "cdp";
30249
30639
  var CDP_BROWSER_NAME = cliOption("--browser-name") ?? "CDP";
30250
30640
  if (cliArg === "--init" || cliArg === "--uninstall") {
@@ -30271,6 +30661,7 @@ Options:
30271
30661
  --daemon Start as standalone CDP daemon (no extension needed)
30272
30662
  --cdp-port <port> CDP port for daemon mode (default 9222)
30273
30663
  --cdp-user-data-dir <dir> Read DevToolsActivePort from this Chrome profile
30664
+ --agentbay-session-id <id> Connect to an AgentBay browser session over CDP
30274
30665
  --browser-id <id> Registry id for this CDP browser
30275
30666
  --browser-name <name> Human-readable registry name
30276
30667
  --version, -v Show version
@@ -30314,28 +30705,53 @@ function extractChromeVersion(input) {
30314
30705
  const parts = match[1].split(".");
30315
30706
  return parts.slice(0, 3).join(".");
30316
30707
  }
30317
- function fetchCdpBrowserVersion() {
30708
+ async function fetchCdpBrowserVersion() {
30709
+ if (AGENTBAY_SESSION_ID) {
30710
+ try {
30711
+ const browser = await getCdpSession().getBrowserClient();
30712
+ const result = await browser.sendCommand("Browser.getVersion", {}, 5e3);
30713
+ return result.product ?? null;
30714
+ } catch {
30715
+ return null;
30716
+ }
30717
+ }
30318
30718
  const port = readDevToolsActivePort(CDP_USER_DATA_DIR)?.port ?? CDP_PORT;
30319
- return new Promise((resolve) => {
30320
- const req = http5.get(`http://localhost:${port}/json/version`, (res) => {
30719
+ return new Promise((resolve2) => {
30720
+ const req = http4.get(`http://localhost:${port}/json/version`, (res) => {
30321
30721
  let data = "";
30322
30722
  res.on("data", (chunk) => data += chunk);
30323
30723
  res.on("end", () => {
30324
30724
  try {
30325
30725
  const info = JSON.parse(data);
30326
- resolve(info.Browser ?? null);
30726
+ resolve2(info.Browser ?? null);
30327
30727
  } catch {
30328
- resolve(null);
30728
+ resolve2(null);
30329
30729
  }
30330
30730
  });
30331
30731
  });
30332
- req.on("error", () => resolve(null));
30732
+ req.on("error", () => resolve2(null));
30333
30733
  req.setTimeout(2e3, () => {
30334
30734
  req.destroy();
30335
- resolve(null);
30735
+ resolve2(null);
30336
30736
  });
30337
30737
  });
30338
30738
  }
30739
+ async function getCurrentBrowserUserAgent() {
30740
+ if (extensionVersion !== null) {
30741
+ try {
30742
+ const result = await sendRequestToExtension("get_browser_info", {}, 3e3);
30743
+ if (result.userAgent?.trim()) return result.userAgent;
30744
+ } catch {
30745
+ }
30746
+ }
30747
+ try {
30748
+ const browser = await getCdpSession().getBrowserClient();
30749
+ const result = await browser.sendCommand("Browser.getVersion", {}, 5e3);
30750
+ if (result.userAgent?.trim()) return result.userAgent;
30751
+ } catch {
30752
+ }
30753
+ throw new Error("Could not determine the control browser user agent for userAgentMode=desktop");
30754
+ }
30339
30755
  async function collectVersions() {
30340
30756
  const info = {
30341
30757
  nativeHost: NATIVE_HOST_VERSION,
@@ -30387,7 +30803,9 @@ function scheduleInventoryExit() {
30387
30803
  var cdpSession = null;
30388
30804
  function getCdpSession() {
30389
30805
  if (!cdpSession) {
30390
- cdpSession = new CdpSession({ port: CDP_PORT, userDataDir: CDP_USER_DATA_DIR });
30806
+ cdpSession = AGENTBAY_SESSION_ID ? new CdpSession({
30807
+ endpointResolver: () => resolveAgentBayEndpoint(AGENTBAY_SESSION_ID)
30808
+ }) : new CdpSession({ port: CDP_PORT, userDataDir: CDP_USER_DATA_DIR });
30391
30809
  }
30392
30810
  return cdpSession;
30393
30811
  }
@@ -30591,12 +31009,12 @@ function setupNativeMessageInput() {
30591
31009
  }
30592
31010
  function sendRequestToExtension(action, data, timeoutMs, trustedInternal = false, controlSource = "local") {
30593
31011
  const id = `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
30594
- return new Promise((resolve, reject) => {
31012
+ return new Promise((resolve2, reject) => {
30595
31013
  const timer = setTimeout(() => {
30596
31014
  pendingRequests.delete(id);
30597
31015
  reject(new Error(`Request timeout after ${timeoutMs}ms`));
30598
31016
  }, timeoutMs);
30599
- pendingRequests.set(id, { resolve, reject, timer });
31017
+ pendingRequests.set(id, { resolve: resolve2, reject, timer });
30600
31018
  sendToExtension(
30601
31019
  createExtensionRequestMessage(id, action, data, trustedInternal, controlSource)
30602
31020
  );
@@ -30706,7 +31124,14 @@ async function handleSocketLine(socket, line) {
30706
31124
  try {
30707
31125
  const selector = String(data?.browser ?? data?.browserId ?? data?.name ?? "").trim();
30708
31126
  if (!selector) throw new Error("browser is required");
30709
- const result = await closeManagedBrowser(selector, { deleteProfile: data?.deleteProfile });
31127
+ const localExists = hasManagedBrowser(selector);
31128
+ const agentBayExists = hasAgentBayBrowser(selector);
31129
+ if (localExists && agentBayExists) {
31130
+ throw new Error(
31131
+ `Managed browser name is ambiguous: ${selector}. Use the full browserId from browser_list.`
31132
+ );
31133
+ }
31134
+ const result = agentBayExists ? await closeAgentBayBrowser(selector, { deleteProfile: data?.deleteProfile }) : await closeManagedBrowser(selector, { deleteProfile: data?.deleteProfile });
30710
31135
  socket.write(JSON.stringify({ id, success: true, data: result }) + "\n");
30711
31136
  } catch (error48) {
30712
31137
  socket.write(
@@ -30798,7 +31223,6 @@ async function handleSocketLine(socket, line) {
30798
31223
  }
30799
31224
  if (action === "browser_launch") {
30800
31225
  try {
30801
- const prepareTdbankSso = extensionVersion !== null ? (payload) => sendRequestToExtension("__tdbank_prepare_sso", payload, 2e4, true) : (payload) => cdpPrepareTdbankSso(getCdpSession(), payload);
30802
31226
  const exportCookies = extensionVersion !== null ? async (domains) => {
30803
31227
  const result2 = await sendRequestToExtension(
30804
31228
  "__export_cookies",
@@ -30808,8 +31232,34 @@ async function handleSocketLine(socket, line) {
30808
31232
  );
30809
31233
  return result2.cookies ?? [];
30810
31234
  } : (domains) => cdpExportCookies(getCdpSession(), domains);
31235
+ if (data?.provider === "agentbay") {
31236
+ const name2 = typeof data?.name === "string" ? data.name.trim() : "";
31237
+ if (name2 && hasManagedBrowser(name2)) {
31238
+ throw new Error(
31239
+ `Managed browser name is already used by the local provider: ${name2}. Choose a unique name.`
31240
+ );
31241
+ }
31242
+ const result2 = await launchAgentBayBrowser(data, {
31243
+ nativeHostScript: fileURLToPath4(import.meta.url),
31244
+ exportCookies,
31245
+ getBrowserUserAgent: getCurrentBrowserUserAgent,
31246
+ log
31247
+ });
31248
+ socket.write(JSON.stringify({ id, success: true, data: result2 }) + "\n");
31249
+ return;
31250
+ }
31251
+ if (data?.provider !== void 0 && data.provider !== "local") {
31252
+ throw new Error('provider must be "local" or "agentbay"');
31253
+ }
31254
+ const name = typeof data?.name === "string" ? data.name.trim() : "";
31255
+ if (name && hasAgentBayBrowser(name)) {
31256
+ throw new Error(
31257
+ `Managed browser name is already used by the AgentBay provider: ${name}. Choose a unique name.`
31258
+ );
31259
+ }
31260
+ const prepareTdbankSso = extensionVersion !== null ? (payload) => sendRequestToExtension("__tdbank_prepare_sso", payload, 2e4, true) : (payload) => cdpPrepareTdbankSso(getCdpSession(), payload);
30811
31261
  const result = await launchManagedBrowser(data, {
30812
- nativeHostScript: fileURLToPath3(import.meta.url),
31262
+ nativeHostScript: fileURLToPath4(import.meta.url),
30813
31263
  prepareTdbankSso,
30814
31264
  exportCookies,
30815
31265
  log
@@ -30901,8 +31351,21 @@ async function handleViaCdp(action, data, _timeoutMs, controlSource) {
30901
31351
  `${action} requires the Mearl browser extension. Install it from https://chromewebstore.google.com/detail/mearl/aoehhjnofngknnjefamjbplchbolghkm`
30902
31352
  );
30903
31353
  }
30904
- const available = await isCdpAvailable(CDP_PORT, CDP_USER_DATA_DIR);
31354
+ if (AGENTBAY_SESSION_ID && action === "page_upload") {
31355
+ throw new Error(
31356
+ "page_upload is not available for AgentBay CDP sessions because file paths resolve on the remote browser host."
31357
+ );
31358
+ }
31359
+ const available = AGENTBAY_SESSION_ID ? await getCdpSession().getBrowserClient().then(
31360
+ () => true,
31361
+ () => false
31362
+ ) : await isCdpAvailable(CDP_PORT, CDP_USER_DATA_DIR);
30905
31363
  if (!available) {
31364
+ if (AGENTBAY_SESSION_ID) {
31365
+ throw new Error(
31366
+ `AgentBay CDP endpoint is not available for session ${AGENTBAY_SESSION_ID}. Check the session status and AgentBay plan permissions.`
31367
+ );
31368
+ }
30906
31369
  throw new Error(
30907
31370
  `Chrome DevTools remote debugging is not available on port ${CDP_PORT}. Enable it via chrome://inspect/#remote-debugging (Chrome 145+) or install the Mearl extension.`
30908
31371
  );
@@ -30937,16 +31400,16 @@ function onSocketConnection(socket) {
30937
31400
  });
30938
31401
  }
30939
31402
  function isPathAlive(socketPath) {
30940
- return new Promise((resolve) => {
31403
+ return new Promise((resolve2) => {
30941
31404
  if (process.platform !== "win32" && !fs13.existsSync(socketPath)) {
30942
- resolve(false);
31405
+ resolve2(false);
30943
31406
  return;
30944
31407
  }
30945
31408
  const client2 = net.createConnection(socketPath);
30946
31409
  const done = (alive) => {
30947
31410
  client2.removeAllListeners();
30948
31411
  client2.destroy();
30949
- resolve(alive);
31412
+ resolve2(alive);
30950
31413
  };
30951
31414
  const timer = setTimeout(() => done(false), 500);
30952
31415
  client2.on("connect", () => {
@@ -30960,7 +31423,7 @@ function isPathAlive(socketPath) {
30960
31423
  });
30961
31424
  }
30962
31425
  function listenOn(socketPath) {
30963
- return new Promise((resolve, reject) => {
31426
+ return new Promise((resolve2, reject) => {
30964
31427
  if (process.platform !== "win32" && fs13.existsSync(socketPath)) {
30965
31428
  try {
30966
31429
  fs13.unlinkSync(socketPath);
@@ -30986,7 +31449,7 @@ function listenOn(socketPath) {
30986
31449
  server.removeListener("error", reject);
30987
31450
  server.on("error", (error48) => log("socket server error:", String(error48)));
30988
31451
  log(`listening on ${socketPath}`);
30989
- resolve(server);
31452
+ resolve2(server);
30990
31453
  });
30991
31454
  });
30992
31455
  }
@@ -31087,6 +31550,17 @@ async function main() {
31087
31550
  lastFocusedAt: CDP_BROWSER_ID.startsWith("managed:") ? 0 : void 0
31088
31551
  });
31089
31552
  }
31553
+ if (AGENTBAY_SESSION_ID) {
31554
+ const keepAliveTimer = setInterval(() => {
31555
+ keepAgentBaySessionAlive(AGENTBAY_SESSION_ID).catch((error48) => {
31556
+ log(
31557
+ `AgentBay keepalive failed for ${AGENTBAY_SESSION_ID}:`,
31558
+ error48 instanceof Error ? error48.message : String(error48)
31559
+ );
31560
+ });
31561
+ }, 6e4);
31562
+ keepAliveTimer.unref();
31563
+ }
31090
31564
  process.on("SIGINT", () => cleanupAndExit(0));
31091
31565
  process.on("SIGTERM", () => cleanupAndExit(0));
31092
31566
  process.on("uncaughtException", (error48) => {