@mearl/native-host 2.4.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,360 @@ 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.persistent === true) {
9578
+ throw new Error("persistent is not supported by the AgentBay CDP provider yet");
9579
+ }
9580
+ if (input.executablePath) {
9581
+ throw new Error("executablePath is only supported by the local browser provider");
9582
+ }
9583
+ if (input.accountId !== void 0 || input.havanaId !== void 0 || input.site !== void 0 || typeof input.query === "string" && input.query.trim()) {
9584
+ throw new Error("TDBank login parameters are only supported by the local browser provider");
9585
+ }
9586
+ }
9587
+ function resolveAgentBayImageId(value) {
9588
+ if (value === void 0) return DEFAULT_AGENTBAY_IMAGE_ID;
9589
+ const imageId = typeof value === "string" ? value.trim() : "";
9590
+ if (!imageId || imageId.length > 128 || !/^[A-Za-z0-9][A-Za-z0-9._:-]*$/.test(imageId)) {
9591
+ throw new Error("imageId must be a valid AgentBay image alias or image ID");
9592
+ }
9593
+ return imageId;
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: false,
9618
+ agentbaySessionId: record2.sessionId,
9619
+ ...record2.imageId ? { agentbayImageId: record2.imageId } : {},
9620
+ ...accessUrl ? { agentbayAccessUrl: accessUrl } : {},
9621
+ createdAt: record2.createdAt,
9622
+ ...record2.copiedCookies ? { copiedCookies: record2.copiedCookies } : {},
9623
+ running
9624
+ };
9625
+ }
9626
+ function listRecords2() {
9627
+ const records = [];
9628
+ for (const file2 of listRecordFiles(keyedDir(AGENTBAY_BROWSERS_SUBDIR))) {
9629
+ const record2 = readRecord(file2);
9630
+ if (!record2) {
9631
+ removeRecord(file2);
9632
+ continue;
9633
+ }
9634
+ const running = isManagedProcess(record2.pid, `--browser-id ${record2.browserId}`);
9635
+ if (!running) removeRecord(keyedConfigFile("browsers", record2.browserId));
9636
+ records.push({ record: record2, running });
9637
+ }
9638
+ return records;
9639
+ }
9640
+ function findRecord(selector) {
9641
+ const normalized = selector.trim().toLowerCase();
9642
+ return listRecords2().find(
9643
+ ({ record: record2 }) => record2.browserId.toLowerCase() === normalized || record2.name.toLowerCase() === normalized
9644
+ )?.record ?? null;
9645
+ }
9646
+ function hasAgentBayBrowser(selector) {
9647
+ return findRecord(selector) !== null;
9648
+ }
9649
+ function listAgentBayBrowsers() {
9650
+ return listRecords2().map(({ record: record2, running }) => browserSummary2(record2, running));
9651
+ }
9652
+ async function requireSession(sessionId, apiKey) {
9653
+ const agentBay = await createClient(apiKey);
9654
+ const result = await agentBay.get(sessionId);
9655
+ if (!result.success || !result.session) {
9656
+ throw new Error(result.errorMessage || `AgentBay session not found: ${sessionId}`);
9657
+ }
9658
+ return result.session;
9659
+ }
9660
+ async function resolveAgentBayEndpoint(sessionId) {
9661
+ const apiKey = resolveAgentBayApiKey();
9662
+ const { AgentBay, GetCdpLinkRequest } = await loadSdk();
9663
+ const agentBay = new AgentBay({ apiKey });
9664
+ const response = await agentBay.client.getCdpLink(
9665
+ new GetCdpLinkRequest({ authorization: `Bearer ${apiKey}`, sessionId })
9666
+ );
9667
+ const endpoint = response.body?.success ? response.body.data?.url : void 0;
9668
+ if (!endpoint) {
9669
+ throw new Error(response.body?.message || `Failed to get AgentBay CDP link: ${sessionId}`);
9670
+ }
9671
+ const url2 = new URL(endpoint);
9672
+ if (url2.protocol !== "ws:" && url2.protocol !== "wss:") {
9673
+ throw new Error(`AgentBay returned an unsupported CDP endpoint: ${url2.protocol}`);
9674
+ }
9675
+ return url2.toString();
9676
+ }
9677
+ async function keepAgentBaySessionAlive(sessionId) {
9678
+ const session = await requireSession(sessionId, resolveAgentBayApiKey());
9679
+ const result = await session.keepAlive();
9680
+ if (result.success === false) throw new Error(result.errorMessage || "AgentBay keepalive failed");
9681
+ }
9682
+ async function initializeAgentBayBrowserSession(session, input, cookies, options = {}) {
9683
+ const initialized = await session.browser.initializeAsync(
9684
+ options.userAgent ? { userAgent: options.userAgent } : {}
9685
+ );
9686
+ if (!initialized) throw new Error("AgentBay browser initialization failed");
9687
+ const endpoint = await session.browser.getEndpointUrl();
9688
+ const cdp = await (options.connectCdp ?? ((url2) => CdpClient.connect(url2)))(endpoint);
9689
+ try {
9690
+ await cdp.sendCommand("Browser.getVersion", {}, 1e4);
9691
+ const copiedCookieCount = await cdpImportCookiesWithClient(cdp, cookies);
9692
+ if (input.url) {
9693
+ await cdp.sendCommand("Target.createTarget", { url: input.url, background: false });
9694
+ }
9695
+ return copiedCookieCount;
9696
+ } finally {
9697
+ cdp.close();
9698
+ }
9699
+ }
9700
+ async function launchAgentBayBrowser(input, deps) {
9701
+ const name = validateName(input.name);
9702
+ const browserId = agentBayBrowserId(name);
9703
+ validateAgentBayInput(input);
9704
+ const imageId = resolveAgentBayImageId(input.imageId);
9705
+ const userAgentMode = resolveUserAgentMode(input.userAgentMode);
9706
+ const copyCookieDomains = input.copyCookieDomains === void 0 ? [] : normalizeCookieDomains(input.copyCookieDomains);
9707
+ const apiKey = (deps.resolveApiKey ?? resolveAgentBayApiKey)();
9708
+ const releaseLock = acquireLaunchLock(browserId);
9709
+ const persistBrowserRecord = deps.persistBrowserRecord ?? ((record2) => writeRecord(keyedConfigFile(AGENTBAY_BROWSERS_SUBDIR, record2.browserId), record2));
9710
+ let session = null;
9711
+ let hostPid = 0;
9712
+ let recordWritten = false;
9713
+ try {
9714
+ if (findRecord(browserId)) throw new Error(`AgentBay browser already exists: ${browserId}`);
9715
+ const [cookies, userAgent] = await Promise.all([
9716
+ copyCookieDomains.length > 0 ? deps.exportCookies(copyCookieDomains) : Promise.resolve([]),
9717
+ userAgentMode === "desktop" ? deps.getBrowserUserAgent().then(normalizeDesktopUserAgent) : Promise.resolve(void 0)
9718
+ ]);
9719
+ const agentBay = await (deps.createAgentBayClient ?? createClient)(apiKey);
9720
+ const created = await agentBay.create({ imageId, framework: "mearl" });
9721
+ if (!created.success || !created.session) {
9722
+ throw new Error(created.errorMessage || "AgentBay session creation failed");
9723
+ }
9724
+ session = created.session;
9725
+ const copiedCookieCount = await initializeAgentBayBrowserSession(session, input, cookies, {
9726
+ userAgent
9727
+ });
9728
+ const host = spawn4(
9729
+ process.execPath,
9730
+ [
9731
+ deps.nativeHostScript,
9732
+ "--daemon",
9733
+ "--agentbay-session-id",
9734
+ session.sessionId,
9735
+ "--browser-id",
9736
+ browserId,
9737
+ "--browser-name",
9738
+ name
9739
+ ],
9740
+ {
9741
+ detached: true,
9742
+ stdio: "ignore",
9743
+ windowsHide: true
9744
+ }
9745
+ );
9746
+ await new Promise((resolveSpawn, rejectSpawn) => {
9747
+ host.once("error", rejectSpawn);
9748
+ host.once("spawn", resolveSpawn);
9749
+ });
9750
+ hostPid = host.pid ?? 0;
9751
+ if (!hostPid) throw new Error("AgentBay native host launched without a process id");
9752
+ host.unref();
9753
+ const accessUrl = normalizeAgentBayAccessUrl(session.resourceUrl);
9754
+ const record2 = {
9755
+ pid: hostPid,
9756
+ browserId,
9757
+ name,
9758
+ sessionId: session.sessionId,
9759
+ imageId,
9760
+ ...accessUrl ? { accessUrl } : {},
9761
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
9762
+ ...copyCookieDomains.length > 0 ? { copiedCookies: { domains: copyCookieDomains, count: copiedCookieCount } } : {}
9763
+ };
9764
+ persistBrowserRecord(record2);
9765
+ recordWritten = true;
9766
+ await waitForBrowserRegistration(browserId, hostPid);
9767
+ deps.log(`AgentBay browser launched: ${browserId}, host=${hostPid}`);
9768
+ return browserSummary2(record2, true);
9769
+ } catch (error48) {
9770
+ if (hostPid) await stopProcess(hostPid, `--browser-id ${browserId}`);
9771
+ if (recordWritten) {
9772
+ removeRecord(keyedConfigFile(AGENTBAY_BROWSERS_SUBDIR, browserId));
9773
+ removeRecord(keyedConfigFile("browsers", browserId));
9774
+ }
9775
+ if (session) {
9776
+ let cleanupError;
9777
+ try {
9778
+ const deleted = await session.delete(false);
9779
+ if (!deleted.success) {
9780
+ cleanupError = new Error(deleted.errorMessage || "AgentBay session deletion failed");
9781
+ }
9782
+ } catch (deleteError) {
9783
+ cleanupError = deleteError;
9784
+ }
9785
+ if (cleanupError) {
9786
+ const accessUrl = normalizeAgentBayAccessUrl(session.resourceUrl);
9787
+ const recoveryRecord = {
9788
+ pid: hostPid,
9789
+ browserId,
9790
+ name,
9791
+ sessionId: session.sessionId,
9792
+ imageId,
9793
+ ...accessUrl ? { accessUrl } : {},
9794
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
9795
+ };
9796
+ let recoverySaved = false;
9797
+ let recoveryError;
9798
+ try {
9799
+ persistBrowserRecord(recoveryRecord);
9800
+ recoverySaved = true;
9801
+ } catch (writeError) {
9802
+ recoveryError = writeError;
9803
+ }
9804
+ deps.log(
9805
+ `AgentBay launch cleanup failed for ${session.sessionId}:`,
9806
+ cleanupError instanceof Error ? cleanupError.message : String(cleanupError)
9807
+ );
9808
+ if (recoveryError) {
9809
+ deps.log(
9810
+ `AgentBay recovery metadata could not be saved for ${session.sessionId}:`,
9811
+ recoveryError instanceof Error ? recoveryError.message : String(recoveryError)
9812
+ );
9813
+ }
9814
+ throw new AggregateError(
9815
+ recoveryError ? [error48, cleanupError, recoveryError] : [error48, cleanupError],
9816
+ `${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`),
9817
+ { cause: error48 }
9818
+ );
9819
+ }
9820
+ }
9821
+ throw error48;
9822
+ } finally {
9823
+ releaseLock();
9824
+ }
9825
+ }
9826
+ function isMissingSessionError(error48) {
9827
+ const message = error48 instanceof Error ? error48.message : String(error48);
9828
+ return /not[ -]?found|does not exist|already deleted|invalid session/i.test(message);
9829
+ }
9830
+ async function closeAgentBayBrowser(selector) {
9831
+ const record2 = findRecord(selector);
9832
+ if (!record2) throw new Error(`AgentBay browser not found: ${selector}`);
9833
+ try {
9834
+ const session = await requireSession(record2.sessionId, resolveAgentBayApiKey());
9835
+ const result = await session.delete(false);
9836
+ if (!result.success) throw new Error(result.errorMessage || "AgentBay session deletion failed");
9837
+ } catch (error48) {
9838
+ if (!isMissingSessionError(error48)) throw error48;
9839
+ }
9840
+ const closingOwnHost = record2.pid === process.pid;
9841
+ if (!closingOwnHost) await stopProcess(record2.pid, `--browser-id ${record2.browserId}`);
9842
+ removeRecord(keyedConfigFile(AGENTBAY_BROWSERS_SUBDIR, record2.browserId));
9843
+ removeRecord(keyedConfigFile("browsers", record2.browserId));
9844
+ if (closingOwnHost) {
9845
+ const timer = setTimeout(() => process.kill(process.pid, "SIGTERM"), 250);
9846
+ timer.unref();
9847
+ }
9848
+ return {
9849
+ browserId: record2.browserId,
9850
+ provider: "agentbay",
9851
+ closed: true,
9852
+ sessionDeleted: true
9853
+ };
9854
+ }
9855
+
9569
9856
  // src/browser-registry.ts
9570
9857
  var BROWSERS_SUBDIR = "browsers";
9571
9858
  var STALE_RECORD_TTL_MS = 5 * 6e4;
@@ -9715,10 +10002,14 @@ function mergeBrowserInventory(connected, managed) {
9715
10002
  ...connectionFields(record2),
9716
10003
  headless: managedBrowser.headless,
9717
10004
  persistent: managedBrowser.persistent,
9718
- cdpPort: managedBrowser.cdpPort,
10005
+ provider: managedBrowser.provider,
10006
+ ..."cdpPort" in managedBrowser ? { cdpPort: managedBrowser.cdpPort } : {},
10007
+ ..."agentbaySessionId" in managedBrowser ? { agentbaySessionId: managedBrowser.agentbaySessionId } : {},
10008
+ ..."agentbayImageId" in managedBrowser && managedBrowser.agentbayImageId ? { agentbayImageId: managedBrowser.agentbayImageId } : {},
10009
+ ..."agentbayAccessUrl" in managedBrowser && managedBrowser.agentbayAccessUrl ? { agentbayAccessUrl: managedBrowser.agentbayAccessUrl } : {},
9719
10010
  createdAt: managedBrowser.createdAt,
9720
- account: managedBrowser.account,
9721
- copiedCookies: managedBrowser.copiedCookies
10011
+ ..."account" in managedBrowser ? { account: managedBrowser.account } : {},
10012
+ ..."copiedCookies" in managedBrowser ? { copiedCookies: managedBrowser.copiedCookies } : {}
9722
10013
  };
9723
10014
  });
9724
10015
  const disconnected = [...managedById.values()].sort((a, b) => b.createdAt.localeCompare(a.createdAt)).map((browser) => ({
@@ -9728,10 +10019,14 @@ function mergeBrowserInventory(connected, managed) {
9728
10019
  status: browser.running ? "running_disconnected" : "stopped",
9729
10020
  headless: browser.headless,
9730
10021
  persistent: browser.persistent,
9731
- cdpPort: browser.cdpPort,
10022
+ provider: browser.provider,
10023
+ ..."cdpPort" in browser ? { cdpPort: browser.cdpPort } : {},
10024
+ ..."agentbaySessionId" in browser ? { agentbaySessionId: browser.agentbaySessionId } : {},
10025
+ ..."agentbayImageId" in browser && browser.agentbayImageId ? { agentbayImageId: browser.agentbayImageId } : {},
10026
+ ..."agentbayAccessUrl" in browser && browser.agentbayAccessUrl ? { agentbayAccessUrl: browser.agentbayAccessUrl } : {},
9732
10027
  createdAt: browser.createdAt,
9733
- account: browser.account,
9734
- copiedCookies: browser.copiedCookies
10028
+ ..."account" in browser ? { account: browser.account } : {},
10029
+ ..."copiedCookies" in browser ? { copiedCookies: browser.copiedCookies } : {}
9735
10030
  }));
9736
10031
  browsers.push(...disconnected);
9737
10032
  return {
@@ -9741,7 +10036,7 @@ function mergeBrowserInventory(connected, managed) {
9741
10036
  };
9742
10037
  }
9743
10038
  function listBrowserInventory() {
9744
- const managed = listManagedBrowsers();
10039
+ const managed = [...listManagedBrowsers(), ...listAgentBayBrowsers()];
9745
10040
  return mergeBrowserInventory(listRegisteredBrowsers(), managed);
9746
10041
  }
9747
10042
 
@@ -9752,7 +10047,7 @@ import os2 from "node:os";
9752
10047
  import { randomUUID as randomUUID4 } from "node:crypto";
9753
10048
 
9754
10049
  // src/version.ts
9755
- var NATIVE_HOST_VERSION = true ? "2.4.0" : "unknown";
10050
+ var NATIVE_HOST_VERSION = true ? "2.5.0" : "unknown";
9756
10051
 
9757
10052
  // src/analytics.ts
9758
10053
  var MEASUREMENT_ID = "G-W84LJDZ6PH";
@@ -9936,14 +10231,14 @@ function selectedPath(stdout) {
9936
10231
  const value = stdout.replace(/[\r\n]+$/, "");
9937
10232
  return value || null;
9938
10233
  }
9939
- var runDirectoryPickerCommand = (command, args) => new Promise((resolve, reject) => {
10234
+ var runDirectoryPickerCommand = (command, args) => new Promise((resolve2, reject) => {
9940
10235
  execFile(command, args, { encoding: "utf8", windowsHide: true }, (error48, stdout) => {
9941
10236
  if (!error48) {
9942
- resolve(selectedPath(stdout));
10237
+ resolve2(selectedPath(stdout));
9943
10238
  return;
9944
10239
  }
9945
10240
  if (error48.code === 1) {
9946
- resolve(null);
10241
+ resolve2(null);
9947
10242
  return;
9948
10243
  }
9949
10244
  reject(error48);
@@ -10614,17 +10909,17 @@ var AgentChatManager = class {
10614
10909
  // src/agent/codex-provider.ts
10615
10910
  import os3 from "node:os";
10616
10911
  import readline from "node:readline";
10617
- import { spawn as spawn6 } from "node:child_process";
10912
+ import { spawn as spawn7 } from "node:child_process";
10618
10913
 
10619
10914
  // src/agent/executable-resolver.ts
10620
10915
  import fs6 from "node:fs";
10621
- import { spawn as spawn5 } from "node:child_process";
10916
+ import { spawn as spawn6 } from "node:child_process";
10622
10917
  import path7 from "node:path";
10623
10918
 
10624
10919
  // ../setup/dist/process.js
10625
10920
  import fs5 from "node:fs";
10626
10921
  import path6 from "node:path";
10627
- import { spawn as spawn4 } from "node:child_process";
10922
+ import { spawn as spawn5 } from "node:child_process";
10628
10923
  var MAX_CAPTURE_LENGTH = 1e6;
10629
10924
  function isExecutable(filePath, platform = process.platform) {
10630
10925
  try {
@@ -10686,7 +10981,7 @@ function prependPathDirectory(directory, env = process.env) {
10686
10981
  function findExecutable(name, options = {}) {
10687
10982
  const env = options.env ?? process.env;
10688
10983
  const platform = options.platform ?? process.platform;
10689
- const existsSync4 = options.existsSync ?? ((candidate) => isExecutable(candidate, platform));
10984
+ const existsSync5 = options.existsSync ?? ((candidate) => isExecutable(candidate, platform));
10690
10985
  const pathKey = Object.keys(env).find((key) => key.toLowerCase() === "path") || "PATH";
10691
10986
  const directories = [
10692
10987
  ...options.extraDirectories ?? [],
@@ -10695,16 +10990,16 @@ function findExecutable(name, options = {}) {
10695
10990
  for (const directory of directories) {
10696
10991
  for (const executableName of executableNames(name, platform)) {
10697
10992
  const candidate = path6.join(directory, executableName);
10698
- if (existsSync4(candidate))
10993
+ if (existsSync5(candidate))
10699
10994
  return candidate;
10700
10995
  }
10701
10996
  }
10702
10997
  return null;
10703
10998
  }
10704
- var runCommand = (command, args, options = {}) => new Promise((resolve, reject) => {
10999
+ var runCommand = (command, args, options = {}) => new Promise((resolve2, reject) => {
10705
11000
  const capture = options.capture === true;
10706
11001
  const launch = resolveSpawnCommand(command, args);
10707
- const child = spawn4(launch.command, launch.args, {
11002
+ const child = spawn5(launch.command, launch.args, {
10708
11003
  cwd: options.cwd,
10709
11004
  env: options.env,
10710
11005
  stdio: capture ? ["ignore", "pipe", "pipe"] : "inherit",
@@ -10723,7 +11018,7 @@ var runCommand = (command, args, options = {}) => new Promise((resolve, reject)
10723
11018
  if (error48)
10724
11019
  reject(error48);
10725
11020
  else
10726
- resolve({ code, stdout, stderr });
11021
+ resolve2({ code, stdout, stderr });
10727
11022
  };
10728
11023
  const timer = options.timeoutMs === void 0 ? void 0 : setTimeout(() => {
10729
11024
  child.kill("SIGTERM");
@@ -10845,11 +11140,11 @@ function executableFileCandidates(candidates) {
10845
11140
  return executableCandidates;
10846
11141
  }
10847
11142
  function probeAgentExecutable(executable, options) {
10848
- return new Promise((resolve) => {
11143
+ return new Promise((resolve2) => {
10849
11144
  let settled = false;
10850
11145
  let stderr = "";
10851
11146
  const launch = resolveSpawnCommand(executable, options.args);
10852
- const child = spawn5(launch.command, launch.args, {
11147
+ const child = spawn6(launch.command, launch.args, {
10853
11148
  stdio: ["ignore", "ignore", "pipe"],
10854
11149
  windowsHide: true,
10855
11150
  env: prependNodePath()
@@ -10858,7 +11153,7 @@ function probeAgentExecutable(executable, options) {
10858
11153
  if (settled) return;
10859
11154
  settled = true;
10860
11155
  clearTimeout(timer);
10861
- resolve(error48);
11156
+ resolve2(error48);
10862
11157
  };
10863
11158
  const timer = setTimeout(() => {
10864
11159
  child.kill("SIGTERM");
@@ -10989,13 +11284,13 @@ function externalOpenCommand(url2, platform = process.platform) {
10989
11284
  }
10990
11285
  function openExternalUrl(url2) {
10991
11286
  const { command, args } = externalOpenCommand(url2);
10992
- return new Promise((resolve, reject) => {
10993
- const child = spawn6(command, args, { stdio: "ignore", windowsHide: true });
11287
+ return new Promise((resolve2, reject) => {
11288
+ const child = spawn7(command, args, { stdio: "ignore", windowsHide: true });
10994
11289
  child.once("error", (error48) => {
10995
11290
  reject(new Error(`Unable to open Codex App: ${error48.message}`));
10996
11291
  });
10997
11292
  child.once("close", (code) => {
10998
- if (code === 0) resolve();
11293
+ if (code === 0) resolve2();
10999
11294
  else reject(new Error("Unable to open Codex App. Make sure it is installed."));
11000
11295
  });
11001
11296
  });
@@ -11162,7 +11457,7 @@ var CodexAgentProvider = class {
11162
11457
  url: `data:${image.mimeType};base64,${image.data}`
11163
11458
  }))
11164
11459
  ],
11165
- summary: "concise"
11460
+ summary: "detailed"
11166
11461
  });
11167
11462
  const turnId = result?.turn?.id;
11168
11463
  if (typeof turnId !== "string" || !turnId) {
@@ -11224,7 +11519,7 @@ var CodexAgentProvider = class {
11224
11519
  const launch = resolveSpawnCommand(executable, ["app-server", "--listen", "stdio://"]);
11225
11520
  const childEnv = prependNodePath();
11226
11521
  if (this.mearlSocketPath) childEnv.MEARL_SOCKET_PATH = this.mearlSocketPath;
11227
- const child = spawn6(launch.command, launch.args, {
11522
+ const child = spawn7(launch.command, launch.args, {
11228
11523
  stdio: ["pipe", "pipe", "pipe"],
11229
11524
  windowsHide: true,
11230
11525
  env: childEnv
@@ -11316,12 +11611,12 @@ var CodexAgentProvider = class {
11316
11611
  }
11317
11612
  request(method, params) {
11318
11613
  const id = this.nextRequestId++;
11319
- return new Promise((resolve, reject) => {
11614
+ return new Promise((resolve2, reject) => {
11320
11615
  const timer = setTimeout(() => {
11321
11616
  this.pendingRequests.delete(id);
11322
11617
  reject(new Error(`Codex request timed out: ${method}`));
11323
11618
  }, REQUEST_TIMEOUT_MS);
11324
- this.pendingRequests.set(id, { resolve, reject, timer });
11619
+ this.pendingRequests.set(id, { resolve: resolve2, reject, timer });
11325
11620
  try {
11326
11621
  this.write({ id, method, params });
11327
11622
  } catch (error48) {
@@ -11395,7 +11690,7 @@ var CodexAgentProvider = class {
11395
11690
  import fs7 from "node:fs";
11396
11691
  import os4 from "node:os";
11397
11692
  import path8 from "node:path";
11398
- import { spawn as spawn7 } from "node:child_process";
11693
+ import { spawn as spawn8 } from "node:child_process";
11399
11694
  import { Readable, Writable } from "node:stream";
11400
11695
 
11401
11696
  // ../../node_modules/.pnpm/@agentclientprotocol+sdk@1.2.1_zod@4.3.6/node_modules/@agentclientprotocol/sdk/dist/schema/index.js
@@ -11631,7 +11926,7 @@ __export(external_exports, {
11631
11926
  object: () => object,
11632
11927
  optional: () => optional,
11633
11928
  overwrite: () => _overwrite,
11634
- parse: () => parse2,
11929
+ parse: () => parse3,
11635
11930
  parseAsync: () => parseAsync2,
11636
11931
  partialRecord: () => partialRecord,
11637
11932
  pipe: () => pipe,
@@ -11950,7 +12245,7 @@ __export(core_exports2, {
11950
12245
  isValidJWT: () => isValidJWT,
11951
12246
  locales: () => locales_exports,
11952
12247
  meta: () => meta,
11953
- parse: () => parse,
12248
+ parse: () => parse2,
11954
12249
  parseAsync: () => parseAsync,
11955
12250
  prettifyError: () => prettifyError,
11956
12251
  process: () => process2,
@@ -12873,7 +13168,7 @@ var _parse = (_Err) => (schema, value, _ctx, _params) => {
12873
13168
  }
12874
13169
  return result.value;
12875
13170
  };
12876
- var parse = /* @__PURE__ */ _parse($ZodRealError);
13171
+ var parse2 = /* @__PURE__ */ _parse($ZodRealError);
12877
13172
  var _parseAsync = (_Err) => async (schema, value, _ctx, params) => {
12878
13173
  const ctx = _ctx ? Object.assign(_ctx, { async: true }) : { async: true };
12879
13174
  let result = schema._zod.run({ value, issues: [] }, ctx);
@@ -15546,10 +15841,10 @@ var $ZodFunction = /* @__PURE__ */ $constructor("$ZodFunction", (inst, def) => {
15546
15841
  throw new Error("implement() must be called with a function");
15547
15842
  }
15548
15843
  return function(...args) {
15549
- const parsedArgs = inst._def.input ? parse(inst._def.input, args) : args;
15844
+ const parsedArgs = inst._def.input ? parse2(inst._def.input, args) : args;
15550
15845
  const result = Reflect.apply(func, this, parsedArgs);
15551
15846
  if (inst._def.output) {
15552
- return parse(inst._def.output, result);
15847
+ return parse2(inst._def.output, result);
15553
15848
  }
15554
15849
  return result;
15555
15850
  };
@@ -23598,7 +23893,7 @@ var ZodRealError = $constructor("ZodError", initializer2, {
23598
23893
  });
23599
23894
 
23600
23895
  // ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/classic/parse.js
23601
- var parse2 = /* @__PURE__ */ _parse(ZodRealError);
23896
+ var parse3 = /* @__PURE__ */ _parse(ZodRealError);
23602
23897
  var parseAsync2 = /* @__PURE__ */ _parseAsync(ZodRealError);
23603
23898
  var safeParse2 = /* @__PURE__ */ _safeParse(ZodRealError);
23604
23899
  var safeParseAsync2 = /* @__PURE__ */ _safeParseAsync(ZodRealError);
@@ -23641,7 +23936,7 @@ var ZodType = /* @__PURE__ */ $constructor("ZodType", (inst, def) => {
23641
23936
  reg.add(inst, meta3);
23642
23937
  return inst;
23643
23938
  });
23644
- inst.parse = (data, params) => parse2(inst, data, params, { callee: inst.parse });
23939
+ inst.parse = (data, params) => parse3(inst, data, params, { callee: inst.parse });
23645
23940
  inst.safeParse = (data, params) => safeParse2(inst, data, params);
23646
23941
  inst.parseAsync = async (data, params) => parseAsync2(inst, data, params, { callee: inst.parseAsync });
23647
23942
  inst.safeParseAsync = async (data, params) => safeParseAsync2(inst, data, params);
@@ -27200,12 +27495,12 @@ var Connection = class {
27200
27495
  const id = this.nextRequestId++;
27201
27496
  let cancel = () => {
27202
27497
  };
27203
- const responsePromise = new Promise((resolve, reject) => {
27498
+ const responsePromise = new Promise((resolve2, reject) => {
27204
27499
  const pendingResponse = {
27205
27500
  resolve: (response) => {
27206
27501
  try {
27207
27502
  const value = mapResponse ? mapResponse(response) : response;
27208
- resolve(value);
27503
+ resolve2(value);
27209
27504
  } catch (error48) {
27210
27505
  reject(error48);
27211
27506
  }
@@ -27283,8 +27578,8 @@ var Connection = class {
27283
27578
  initialize(stream, handlers) {
27284
27579
  this.stream = stream;
27285
27580
  this.staticHandlers = handlers;
27286
- this.closedPromise = new Promise((resolve) => {
27287
- this.abortController.signal.addEventListener("abort", () => resolve());
27581
+ this.closedPromise = new Promise((resolve2) => {
27582
+ this.abortController.signal.addEventListener("abort", () => resolve2());
27288
27583
  });
27289
27584
  void this.receive();
27290
27585
  }
@@ -27509,13 +27804,13 @@ var ConnectionBuilder = class {
27509
27804
  /**
27510
27805
  * Adds a typed request handler for one method.
27511
27806
  */
27512
- onReceiveRequest(method, parse3, handler) {
27807
+ onReceiveRequest(method, parse4, handler) {
27513
27808
  return this.withHandler({
27514
27809
  handleMessage: async (message, cx) => {
27515
27810
  if (message.kind !== "request" || message.method !== method) {
27516
27811
  return Handled.no(message);
27517
27812
  }
27518
- const request = parse3(message.params);
27813
+ const request = parse4(message.params);
27519
27814
  return await handler(request, message.responder, cx) ?? Handled.yes();
27520
27815
  },
27521
27816
  describe: () => `${this.connectionName ?? "request"}:${method}`
@@ -27524,13 +27819,13 @@ var ConnectionBuilder = class {
27524
27819
  /**
27525
27820
  * Adds a typed notification handler for one method.
27526
27821
  */
27527
- onReceiveNotification(method, parse3, handler) {
27822
+ onReceiveNotification(method, parse4, handler) {
27528
27823
  return this.withHandler({
27529
27824
  handleMessage: async (message, cx) => {
27530
27825
  if (message.kind !== "notification" || message.method !== method) {
27531
27826
  return Handled.no(message);
27532
27827
  }
27533
- const notification = parse3(message.params);
27828
+ const notification = parse4(message.params);
27534
27829
  return await handler(notification, cx) ?? Handled.yes();
27535
27830
  },
27536
27831
  describe: () => `${this.connectionName ?? "notification"}:${method}`
@@ -28072,8 +28367,8 @@ var AsyncQueue = class {
28072
28367
  if (this.failed) {
28073
28368
  return Promise.reject(this.failure);
28074
28369
  }
28075
- return new Promise((resolve, reject) => {
28076
- this.waiters.push({ resolve, reject });
28370
+ return new Promise((resolve2, reject) => {
28371
+ this.waiters.push({ resolve: resolve2, reject });
28077
28372
  });
28078
28373
  }
28079
28374
  };
@@ -29184,7 +29479,7 @@ ${message}` : message;
29184
29479
  }
29185
29480
  const { executable } = resolution;
29186
29481
  const launch = resolveSpawnCommand(executable, ["--acp"]);
29187
- const child = spawn7(launch.command, launch.args, {
29482
+ const child = spawn8(launch.command, launch.args, {
29188
29483
  stdio: ["pipe", "pipe", "pipe"],
29189
29484
  windowsHide: true,
29190
29485
  env: prependNodePath()
@@ -29452,10 +29747,10 @@ ${message}` : message;
29452
29747
  if (requestId === null) {
29453
29748
  return Promise.resolve({ outcome: { outcome: "cancelled" } });
29454
29749
  }
29455
- return new Promise((resolve) => {
29750
+ return new Promise((resolve2) => {
29456
29751
  this.pendingPermissions.set(requestId, {
29457
29752
  providerSessionId: request.sessionId,
29458
- resolve
29753
+ resolve: resolve2
29459
29754
  });
29460
29755
  this.emit({
29461
29756
  providerSessionId: request.sessionId,
@@ -29502,7 +29797,7 @@ ${message}` : message;
29502
29797
  }
29503
29798
  }
29504
29799
  withTimeout(promise2, operation) {
29505
- return new Promise((resolve, reject) => {
29800
+ return new Promise((resolve2, reject) => {
29506
29801
  const timer = setTimeout(
29507
29802
  () => reject(new Error(`${operation} timed out`)),
29508
29803
  REQUEST_TIMEOUT_MS2
@@ -29510,7 +29805,7 @@ ${message}` : message;
29510
29805
  promise2.then(
29511
29806
  (result) => {
29512
29807
  clearTimeout(timer);
29513
- resolve(result);
29808
+ resolve2(result);
29514
29809
  },
29515
29810
  (error48) => {
29516
29811
  clearTimeout(timer);
@@ -29529,9 +29824,9 @@ ${message}` : message;
29529
29824
  });
29530
29825
  }
29531
29826
  runSkillsList(executable, cwd) {
29532
- return new Promise((resolve, reject) => {
29827
+ return new Promise((resolve2, reject) => {
29533
29828
  const launch = resolveSpawnCommand(executable, ["skill", "list", "--all"]);
29534
- const child = spawn7(launch.command, launch.args, {
29829
+ const child = spawn8(launch.command, launch.args, {
29535
29830
  cwd,
29536
29831
  stdio: ["ignore", "pipe", "pipe"],
29537
29832
  windowsHide: true,
@@ -29545,7 +29840,7 @@ ${message}` : message;
29545
29840
  settled = true;
29546
29841
  clearTimeout(timer);
29547
29842
  if (error48) reject(error48);
29548
- else resolve(stdout);
29843
+ else resolve2(stdout);
29549
29844
  };
29550
29845
  const timer = setTimeout(() => {
29551
29846
  child.kill("SIGTERM");
@@ -29580,12 +29875,12 @@ ${message}` : message;
29580
29875
  import fs8 from "node:fs";
29581
29876
  import os5 from "node:os";
29582
29877
  import path9 from "node:path";
29583
- import { spawn as spawn8 } from "node:child_process";
29878
+ import { spawn as spawn9 } from "node:child_process";
29584
29879
  var COMMAND_TIMEOUT_MS = 12e4;
29585
29880
  var MAX_OUTPUT_LENGTH = 1e6;
29586
29881
  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;
29882
+ function existingExecutable(candidates, existsSync5) {
29883
+ return candidates.find((candidate) => candidate && existsSync5(candidate)) ?? null;
29589
29884
  }
29590
29885
  function stripAnsi(value) {
29591
29886
  return value.replace(ANSI_ESCAPE_PATTERN, "");
@@ -29594,7 +29889,7 @@ function resolveAliSkillsExecutable(options = {}) {
29594
29889
  const env = options.env ?? process.env;
29595
29890
  const home = (options.homedir ?? os5.homedir)();
29596
29891
  const platform = options.platform ?? process.platform;
29597
- const existsSync4 = options.existsSync ?? ((candidate) => isExecutable(candidate, platform));
29892
+ const existsSync5 = options.existsSync ?? ((candidate) => isExecutable(candidate, platform));
29598
29893
  const processExecPath = options.processExecPath ?? process.execPath;
29599
29894
  const readdirSync2 = options.readdirSync ?? fs8.readdirSync;
29600
29895
  const names = executableNames("ali-skills", platform);
@@ -29616,11 +29911,11 @@ function resolveAliSkillsExecutable(options = {}) {
29616
29911
  ...platform === "win32" ? [] : ["/opt/homebrew/bin/ali-skills", "/usr/local/bin/ali-skills", "/usr/bin/ali-skills"],
29617
29912
  ...pathCandidates
29618
29913
  ];
29619
- return existingExecutable(candidates, existsSync4);
29914
+ return existingExecutable(candidates, existsSync5);
29620
29915
  }
29621
- var runAliSkillsCli = (executable, args, cwd) => new Promise((resolve, reject) => {
29916
+ var runAliSkillsCli = (executable, args, cwd) => new Promise((resolve2, reject) => {
29622
29917
  const launch = resolveSpawnCommand(executable, args);
29623
- const child = spawn8(launch.command, launch.args, {
29918
+ const child = spawn9(launch.command, launch.args, {
29624
29919
  cwd,
29625
29920
  env: prependNodePath(prependCommandPath(executable)),
29626
29921
  stdio: ["ignore", "pipe", "pipe"],
@@ -29638,7 +29933,7 @@ var runAliSkillsCli = (executable, args, cwd) => new Promise((resolve, reject) =
29638
29933
  if (error48)
29639
29934
  reject(error48);
29640
29935
  else
29641
- resolve({ stdout, stderr });
29936
+ resolve2({ stdout, stderr });
29642
29937
  };
29643
29938
  const timer = setTimeout(() => {
29644
29939
  child.kill("SIGTERM");
@@ -29826,7 +30121,7 @@ function createExtensionRequestMessage(id, action, data, trustedInternal = false
29826
30121
  // src/host-updater.ts
29827
30122
  import fs10 from "node:fs";
29828
30123
  import path11 from "node:path";
29829
- import { fileURLToPath } from "node:url";
30124
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
29830
30125
 
29831
30126
  // ../setup/dist/packageManager.js
29832
30127
  import fs9 from "node:fs";
@@ -29834,8 +30129,8 @@ import path10 from "node:path";
29834
30129
  function normalizePath(filePath) {
29835
30130
  return filePath.replaceAll("\\", "/").replace(/\/+$/, "");
29836
30131
  }
29837
- function firstExisting(candidates, existsSync4) {
29838
- return candidates.find((candidate) => existsSync4(candidate));
30132
+ function firstExisting(candidates, existsSync5) {
30133
+ return candidates.find((candidate) => existsSync5(candidate));
29839
30134
  }
29840
30135
  function fallbackCommand(name, platform) {
29841
30136
  if (platform !== "win32")
@@ -29847,7 +30142,7 @@ function fallbackCommand(name, platform) {
29847
30142
  function resolvePackageManagerInstallation(packageRoot, packageName, options = {}) {
29848
30143
  const env = options.env ?? process.env;
29849
30144
  const platform = options.platform ?? process.platform;
29850
- const existsSync4 = options.existsSync ?? ((candidate) => isExecutable(candidate, platform));
30145
+ const existsSync5 = options.existsSync ?? ((candidate) => isExecutable(candidate, platform));
29851
30146
  const processExecPath = options.processExecPath ?? process.execPath;
29852
30147
  const normalizedRoot = normalizePath(path10.resolve(packageRoot));
29853
30148
  const lowerRoot = normalizedRoot.toLowerCase();
@@ -29858,13 +30153,13 @@ function resolvePackageManagerInstallation(packageRoot, packageName, options = {
29858
30153
  const command = firstExisting([
29859
30154
  ...executableNames("pnpm", platform).map((name) => path10.join(pnpmHome, name)),
29860
30155
  ...executableNames("pnpm", platform).map((name) => path10.join(path10.dirname(processExecPath), name))
29861
- ], existsSync4) ?? fallbackCommand("pnpm", platform);
30156
+ ], existsSync5) ?? fallbackCommand("pnpm", platform);
29862
30157
  return { manager: "pnpm", command, binDirectory: pnpmHome };
29863
30158
  }
29864
30159
  const bunMarker = `/.bun/install/global/node_modules/${lowerPackageName}`;
29865
30160
  if (lowerRoot.endsWith(bunMarker)) {
29866
30161
  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);
30162
+ const command = firstExisting(executableNames("bun", platform).map((name) => path10.join(bunHome, ".bun", "bin", name)), existsSync5) ?? fallbackCommand("bun", platform);
29868
30163
  return {
29869
30164
  manager: "bun",
29870
30165
  command,
@@ -29883,12 +30178,12 @@ function resolvePackageManagerInstallation(packageRoot, packageName, options = {
29883
30178
  const command = firstExisting([
29884
30179
  ...executableNames("yarn", platform).map((name) => path10.join(binDirectory, name)),
29885
30180
  ...executableNames("yarn", platform).map((name) => path10.join(path10.dirname(processExecPath), name))
29886
- ], existsSync4) ?? findExecutable("yarn", { env, existsSync: existsSync4, platform }) ?? fallbackCommand("yarn", platform);
30181
+ ], existsSync5) ?? findExecutable("yarn", { env, existsSync: existsSync5, platform }) ?? fallbackCommand("yarn", platform);
29887
30182
  return { manager: "yarn", command, binDirectory };
29888
30183
  }
29889
30184
  if (lowerRoot.includes("/.volta/tools/image/packages/") && lowerRoot.endsWith(`/lib/node_modules/${lowerPackageName}`)) {
29890
30185
  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);
30186
+ const command = firstExisting(executableNames("volta", platform).map((name) => path10.join(voltaHome, ".volta", "bin", name)), existsSync5) ?? fallbackCommand("volta", platform);
29892
30187
  return {
29893
30188
  manager: "volta",
29894
30189
  command,
@@ -29901,7 +30196,7 @@ function resolvePackageManagerInstallation(packageRoot, packageName, options = {
29901
30196
  const command = firstExisting([
29902
30197
  ...executableNames("npm", platform).map((name) => path10.join(prefix, "bin", name)),
29903
30198
  ...executableNames("npm", platform).map((name) => path10.join(path10.dirname(processExecPath), name))
29904
- ], existsSync4) ?? fallbackCommand("npm", platform);
30199
+ ], existsSync5) ?? fallbackCommand("npm", platform);
29905
30200
  return { manager: "npm", command, binDirectory: path10.join(prefix, "bin") };
29906
30201
  }
29907
30202
  if (platform === "win32" && env.APPDATA) {
@@ -29911,7 +30206,7 @@ function resolvePackageManagerInstallation(packageRoot, packageName, options = {
29911
30206
  const command = firstExisting([
29912
30207
  ...executableNames("npm", platform).map((name) => path10.join(binDirectory, name)),
29913
30208
  ...executableNames("npm", platform).map((name) => path10.join(path10.dirname(processExecPath), name))
29914
- ], existsSync4) ?? fallbackCommand("npm", platform);
30209
+ ], existsSync5) ?? fallbackCommand("npm", platform);
29915
30210
  return { manager: "npm", command, binDirectory };
29916
30211
  }
29917
30212
  }
@@ -30002,7 +30297,7 @@ var NativeHostUpdateFailure = class extends Error {
30002
30297
  }
30003
30298
  };
30004
30299
  function wait(delayMs) {
30005
- return new Promise((resolve) => setTimeout(resolve, delayMs));
30300
+ return new Promise((resolve2) => setTimeout(resolve2, delayMs));
30006
30301
  }
30007
30302
  function readLockState(lockPath) {
30008
30303
  try {
@@ -30115,7 +30410,7 @@ function currentNativeHostPackageRoot(entryPath) {
30115
30410
  return null;
30116
30411
  }
30117
30412
  function currentPackageManagerInstallation(environment, nodePath, platform) {
30118
- const packageRoot = currentNativeHostPackageRoot(fileURLToPath(import.meta.url));
30413
+ const packageRoot = currentNativeHostPackageRoot(fileURLToPath2(import.meta.url));
30119
30414
  if (!packageRoot) return null;
30120
30415
  return resolvePackageManagerInstallation(packageRoot, "@mearl/native-host", {
30121
30416
  env: environment,
@@ -30245,6 +30540,7 @@ function positivePort(value, fallback) {
30245
30540
  }
30246
30541
  var CDP_PORT = positivePort(cliOption("--cdp-port"), 9222);
30247
30542
  var CDP_USER_DATA_DIR = cliOption("--cdp-user-data-dir");
30543
+ var AGENTBAY_SESSION_ID = cliOption("--agentbay-session-id");
30248
30544
  var CDP_BROWSER_ID = cliOption("--browser-id") ?? "cdp";
30249
30545
  var CDP_BROWSER_NAME = cliOption("--browser-name") ?? "CDP";
30250
30546
  if (cliArg === "--init" || cliArg === "--uninstall") {
@@ -30271,6 +30567,7 @@ Options:
30271
30567
  --daemon Start as standalone CDP daemon (no extension needed)
30272
30568
  --cdp-port <port> CDP port for daemon mode (default 9222)
30273
30569
  --cdp-user-data-dir <dir> Read DevToolsActivePort from this Chrome profile
30570
+ --agentbay-session-id <id> Connect to an AgentBay browser session over CDP
30274
30571
  --browser-id <id> Registry id for this CDP browser
30275
30572
  --browser-name <name> Human-readable registry name
30276
30573
  --version, -v Show version
@@ -30314,28 +30611,53 @@ function extractChromeVersion(input) {
30314
30611
  const parts = match[1].split(".");
30315
30612
  return parts.slice(0, 3).join(".");
30316
30613
  }
30317
- function fetchCdpBrowserVersion() {
30614
+ async function fetchCdpBrowserVersion() {
30615
+ if (AGENTBAY_SESSION_ID) {
30616
+ try {
30617
+ const browser = await getCdpSession().getBrowserClient();
30618
+ const result = await browser.sendCommand("Browser.getVersion", {}, 5e3);
30619
+ return result.product ?? null;
30620
+ } catch {
30621
+ return null;
30622
+ }
30623
+ }
30318
30624
  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) => {
30625
+ return new Promise((resolve2) => {
30626
+ const req = http4.get(`http://localhost:${port}/json/version`, (res) => {
30321
30627
  let data = "";
30322
30628
  res.on("data", (chunk) => data += chunk);
30323
30629
  res.on("end", () => {
30324
30630
  try {
30325
30631
  const info = JSON.parse(data);
30326
- resolve(info.Browser ?? null);
30632
+ resolve2(info.Browser ?? null);
30327
30633
  } catch {
30328
- resolve(null);
30634
+ resolve2(null);
30329
30635
  }
30330
30636
  });
30331
30637
  });
30332
- req.on("error", () => resolve(null));
30638
+ req.on("error", () => resolve2(null));
30333
30639
  req.setTimeout(2e3, () => {
30334
30640
  req.destroy();
30335
- resolve(null);
30641
+ resolve2(null);
30336
30642
  });
30337
30643
  });
30338
30644
  }
30645
+ async function getCurrentBrowserUserAgent() {
30646
+ if (extensionVersion !== null) {
30647
+ try {
30648
+ const result = await sendRequestToExtension("get_browser_info", {}, 3e3);
30649
+ if (result.userAgent?.trim()) return result.userAgent;
30650
+ } catch {
30651
+ }
30652
+ }
30653
+ try {
30654
+ const browser = await getCdpSession().getBrowserClient();
30655
+ const result = await browser.sendCommand("Browser.getVersion", {}, 5e3);
30656
+ if (result.userAgent?.trim()) return result.userAgent;
30657
+ } catch {
30658
+ }
30659
+ throw new Error("Could not determine the control browser user agent for userAgentMode=desktop");
30660
+ }
30339
30661
  async function collectVersions() {
30340
30662
  const info = {
30341
30663
  nativeHost: NATIVE_HOST_VERSION,
@@ -30387,7 +30709,9 @@ function scheduleInventoryExit() {
30387
30709
  var cdpSession = null;
30388
30710
  function getCdpSession() {
30389
30711
  if (!cdpSession) {
30390
- cdpSession = new CdpSession({ port: CDP_PORT, userDataDir: CDP_USER_DATA_DIR });
30712
+ cdpSession = AGENTBAY_SESSION_ID ? new CdpSession({
30713
+ endpointResolver: () => resolveAgentBayEndpoint(AGENTBAY_SESSION_ID)
30714
+ }) : new CdpSession({ port: CDP_PORT, userDataDir: CDP_USER_DATA_DIR });
30391
30715
  }
30392
30716
  return cdpSession;
30393
30717
  }
@@ -30591,12 +30915,12 @@ function setupNativeMessageInput() {
30591
30915
  }
30592
30916
  function sendRequestToExtension(action, data, timeoutMs, trustedInternal = false, controlSource = "local") {
30593
30917
  const id = `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
30594
- return new Promise((resolve, reject) => {
30918
+ return new Promise((resolve2, reject) => {
30595
30919
  const timer = setTimeout(() => {
30596
30920
  pendingRequests.delete(id);
30597
30921
  reject(new Error(`Request timeout after ${timeoutMs}ms`));
30598
30922
  }, timeoutMs);
30599
- pendingRequests.set(id, { resolve, reject, timer });
30923
+ pendingRequests.set(id, { resolve: resolve2, reject, timer });
30600
30924
  sendToExtension(
30601
30925
  createExtensionRequestMessage(id, action, data, trustedInternal, controlSource)
30602
30926
  );
@@ -30706,7 +31030,14 @@ async function handleSocketLine(socket, line) {
30706
31030
  try {
30707
31031
  const selector = String(data?.browser ?? data?.browserId ?? data?.name ?? "").trim();
30708
31032
  if (!selector) throw new Error("browser is required");
30709
- const result = await closeManagedBrowser(selector, { deleteProfile: data?.deleteProfile });
31033
+ const localExists = hasManagedBrowser(selector);
31034
+ const agentBayExists = hasAgentBayBrowser(selector);
31035
+ if (localExists && agentBayExists) {
31036
+ throw new Error(
31037
+ `Managed browser name is ambiguous: ${selector}. Use the full browserId from browser_list.`
31038
+ );
31039
+ }
31040
+ const result = agentBayExists ? await closeAgentBayBrowser(selector) : await closeManagedBrowser(selector, { deleteProfile: data?.deleteProfile });
30710
31041
  socket.write(JSON.stringify({ id, success: true, data: result }) + "\n");
30711
31042
  } catch (error48) {
30712
31043
  socket.write(
@@ -30798,7 +31129,6 @@ async function handleSocketLine(socket, line) {
30798
31129
  }
30799
31130
  if (action === "browser_launch") {
30800
31131
  try {
30801
- const prepareTdbankSso = extensionVersion !== null ? (payload) => sendRequestToExtension("__tdbank_prepare_sso", payload, 2e4, true) : (payload) => cdpPrepareTdbankSso(getCdpSession(), payload);
30802
31132
  const exportCookies = extensionVersion !== null ? async (domains) => {
30803
31133
  const result2 = await sendRequestToExtension(
30804
31134
  "__export_cookies",
@@ -30808,8 +31138,34 @@ async function handleSocketLine(socket, line) {
30808
31138
  );
30809
31139
  return result2.cookies ?? [];
30810
31140
  } : (domains) => cdpExportCookies(getCdpSession(), domains);
31141
+ if (data?.provider === "agentbay") {
31142
+ const name2 = typeof data?.name === "string" ? data.name.trim() : "";
31143
+ if (name2 && hasManagedBrowser(name2)) {
31144
+ throw new Error(
31145
+ `Managed browser name is already used by the local provider: ${name2}. Choose a unique name.`
31146
+ );
31147
+ }
31148
+ const result2 = await launchAgentBayBrowser(data, {
31149
+ nativeHostScript: fileURLToPath4(import.meta.url),
31150
+ exportCookies,
31151
+ getBrowserUserAgent: getCurrentBrowserUserAgent,
31152
+ log
31153
+ });
31154
+ socket.write(JSON.stringify({ id, success: true, data: result2 }) + "\n");
31155
+ return;
31156
+ }
31157
+ if (data?.provider !== void 0 && data.provider !== "local") {
31158
+ throw new Error('provider must be "local" or "agentbay"');
31159
+ }
31160
+ const name = typeof data?.name === "string" ? data.name.trim() : "";
31161
+ if (name && hasAgentBayBrowser(name)) {
31162
+ throw new Error(
31163
+ `Managed browser name is already used by the AgentBay provider: ${name}. Choose a unique name.`
31164
+ );
31165
+ }
31166
+ const prepareTdbankSso = extensionVersion !== null ? (payload) => sendRequestToExtension("__tdbank_prepare_sso", payload, 2e4, true) : (payload) => cdpPrepareTdbankSso(getCdpSession(), payload);
30811
31167
  const result = await launchManagedBrowser(data, {
30812
- nativeHostScript: fileURLToPath3(import.meta.url),
31168
+ nativeHostScript: fileURLToPath4(import.meta.url),
30813
31169
  prepareTdbankSso,
30814
31170
  exportCookies,
30815
31171
  log
@@ -30901,8 +31257,21 @@ async function handleViaCdp(action, data, _timeoutMs, controlSource) {
30901
31257
  `${action} requires the Mearl browser extension. Install it from https://chromewebstore.google.com/detail/mearl/aoehhjnofngknnjefamjbplchbolghkm`
30902
31258
  );
30903
31259
  }
30904
- const available = await isCdpAvailable(CDP_PORT, CDP_USER_DATA_DIR);
31260
+ if (AGENTBAY_SESSION_ID && action === "page_upload") {
31261
+ throw new Error(
31262
+ "page_upload is not available for AgentBay CDP sessions because file paths resolve on the remote browser host."
31263
+ );
31264
+ }
31265
+ const available = AGENTBAY_SESSION_ID ? await getCdpSession().getBrowserClient().then(
31266
+ () => true,
31267
+ () => false
31268
+ ) : await isCdpAvailable(CDP_PORT, CDP_USER_DATA_DIR);
30905
31269
  if (!available) {
31270
+ if (AGENTBAY_SESSION_ID) {
31271
+ throw new Error(
31272
+ `AgentBay CDP endpoint is not available for session ${AGENTBAY_SESSION_ID}. Check the session status and AgentBay plan permissions.`
31273
+ );
31274
+ }
30906
31275
  throw new Error(
30907
31276
  `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
31277
  );
@@ -30937,16 +31306,16 @@ function onSocketConnection(socket) {
30937
31306
  });
30938
31307
  }
30939
31308
  function isPathAlive(socketPath) {
30940
- return new Promise((resolve) => {
31309
+ return new Promise((resolve2) => {
30941
31310
  if (process.platform !== "win32" && !fs13.existsSync(socketPath)) {
30942
- resolve(false);
31311
+ resolve2(false);
30943
31312
  return;
30944
31313
  }
30945
31314
  const client2 = net.createConnection(socketPath);
30946
31315
  const done = (alive) => {
30947
31316
  client2.removeAllListeners();
30948
31317
  client2.destroy();
30949
- resolve(alive);
31318
+ resolve2(alive);
30950
31319
  };
30951
31320
  const timer = setTimeout(() => done(false), 500);
30952
31321
  client2.on("connect", () => {
@@ -30960,7 +31329,7 @@ function isPathAlive(socketPath) {
30960
31329
  });
30961
31330
  }
30962
31331
  function listenOn(socketPath) {
30963
- return new Promise((resolve, reject) => {
31332
+ return new Promise((resolve2, reject) => {
30964
31333
  if (process.platform !== "win32" && fs13.existsSync(socketPath)) {
30965
31334
  try {
30966
31335
  fs13.unlinkSync(socketPath);
@@ -30986,7 +31355,7 @@ function listenOn(socketPath) {
30986
31355
  server.removeListener("error", reject);
30987
31356
  server.on("error", (error48) => log("socket server error:", String(error48)));
30988
31357
  log(`listening on ${socketPath}`);
30989
- resolve(server);
31358
+ resolve2(server);
30990
31359
  });
30991
31360
  });
30992
31361
  }
@@ -31087,6 +31456,17 @@ async function main() {
31087
31456
  lastFocusedAt: CDP_BROWSER_ID.startsWith("managed:") ? 0 : void 0
31088
31457
  });
31089
31458
  }
31459
+ if (AGENTBAY_SESSION_ID) {
31460
+ const keepAliveTimer = setInterval(() => {
31461
+ keepAgentBaySessionAlive(AGENTBAY_SESSION_ID).catch((error48) => {
31462
+ log(
31463
+ `AgentBay keepalive failed for ${AGENTBAY_SESSION_ID}:`,
31464
+ error48 instanceof Error ? error48.message : String(error48)
31465
+ );
31466
+ });
31467
+ }, 6e4);
31468
+ keepAliveTimer.unref();
31469
+ }
31090
31470
  process.on("SIGINT", () => cleanupAndExit(0));
31091
31471
  process.on("SIGTERM", () => cleanupAndExit(0));
31092
31472
  process.on("uncaughtException", (error48) => {