@mearl/native-host 2.5.0 → 2.6.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.
@@ -9,7 +9,11 @@ export interface AgentBayBrowserRecord extends DaemonRecord {
9
9
  name: string;
10
10
  sessionId: string;
11
11
  imageId?: string;
12
- accessUrl?: string;
12
+ persistent?: boolean;
13
+ contextId?: string;
14
+ contextName?: string;
15
+ /** Successful context sync checkpoint for retrying deletion of the current session. */
16
+ contextSyncedAt?: string;
13
17
  createdAt: string;
14
18
  copiedCookies?: {
15
19
  domains: string[];
@@ -18,11 +22,25 @@ export interface AgentBayBrowserRecord extends DaemonRecord {
18
22
  }
19
23
  export interface AgentBaySessionLike {
20
24
  sessionId: string;
21
- resourceUrl?: string;
22
25
  browser: {
23
26
  initializeAsync(options: Record<string, unknown>): Promise<boolean>;
24
27
  getEndpointUrl(): Promise<string>;
25
28
  };
29
+ info?(): Promise<{
30
+ success?: boolean;
31
+ data?: unknown;
32
+ errorMessage?: string;
33
+ }>;
34
+ callMcpTool?(toolName: string, args: Record<string, unknown>, autoGenSession?: boolean): Promise<{
35
+ success?: boolean;
36
+ errorMessage?: string;
37
+ }>;
38
+ context?: {
39
+ sync(): Promise<{
40
+ success: boolean;
41
+ errorMessage?: string;
42
+ }>;
43
+ };
26
44
  delete(syncContext?: boolean): Promise<{
27
45
  success: boolean;
28
46
  errorMessage?: string;
@@ -43,16 +61,34 @@ export interface AgentBayClientLike {
43
61
  session?: AgentBaySessionLike;
44
62
  errorMessage?: string;
45
63
  }>;
64
+ context?: {
65
+ get(name: string, create?: boolean): Promise<{
66
+ success: boolean;
67
+ context?: {
68
+ id: string;
69
+ name: string;
70
+ };
71
+ errorMessage?: string;
72
+ }>;
73
+ delete(context: {
74
+ id: string;
75
+ name: string;
76
+ }): Promise<{
77
+ success: boolean;
78
+ errorMessage?: string;
79
+ }>;
80
+ };
46
81
  }
47
82
  export interface AgentBayBrowserSummary {
48
83
  browserId: string;
49
84
  name: string;
50
85
  provider: 'agentbay';
51
86
  headless: false;
52
- persistent: false;
87
+ persistent: boolean;
53
88
  agentbaySessionId: string;
54
89
  agentbayImageId?: string;
55
- agentbayAccessUrl?: string;
90
+ agentbayContextId?: string;
91
+ agentbayContextName?: string;
56
92
  createdAt: string;
57
93
  copiedCookies?: AgentBayBrowserRecord['copiedCookies'];
58
94
  running: boolean;
@@ -79,13 +115,25 @@ interface AgentBayBrowserSessionInitOptions {
79
115
  export declare function agentBayBrowserId(name: string): string;
80
116
  export declare function resolveAgentBayApiKey(): string;
81
117
  export declare function resolveAgentBayImageId(value: unknown): string;
118
+ export declare function agentBayContextName(browserId: string): string;
82
119
  export declare function normalizeDesktopUserAgent(value: unknown): string;
83
120
  export declare function normalizeAgentBayAccessUrl(value: unknown): string | undefined;
84
121
  export declare function hasAgentBayBrowser(selector: string): boolean;
85
122
  export declare function listAgentBayBrowsers(): AgentBayBrowserSummary[];
123
+ export declare function getAgentBayAccessUrl(sessionId: string, dependencies?: Pick<AgentBayBrowserManagerDeps, 'createAgentBayClient' | 'resolveApiKey'>): Promise<string>;
86
124
  export declare function resolveAgentBayEndpoint(sessionId: string): Promise<string>;
87
125
  export declare function keepAgentBaySessionAlive(sessionId: string): Promise<void>;
88
126
  export declare function initializeAgentBayBrowserSession(session: Pick<AgentBaySessionLike, 'browser'>, input: Pick<BrowserLaunchParams, 'url'>, cookies: TransferableCookie[], options?: AgentBayBrowserSessionInitOptions): Promise<number>;
89
127
  export declare function launchAgentBayBrowser(input: BrowserLaunchParams, deps: AgentBayBrowserManagerDeps): Promise<AgentBayBrowserSummary>;
90
- export declare function closeAgentBayBrowser(selector: string): Promise<Record<string, any>>;
128
+ export declare function releaseAgentBayBrowserResources(record: AgentBayBrowserRecord, agentBay: AgentBayClientLike, options?: {
129
+ deleteProfile?: boolean;
130
+ persistRecord?: (record: AgentBayBrowserRecord) => void;
131
+ }): Promise<{
132
+ sessionDeleted: boolean;
133
+ contextSynced?: boolean;
134
+ contextDeleted?: boolean;
135
+ }>;
136
+ export declare function closeAgentBayBrowser(selector: string, options?: {
137
+ deleteProfile?: boolean;
138
+ }): Promise<Record<string, any>>;
91
139
  export {};
package/dist/index.js CHANGED
@@ -4795,7 +4795,15 @@ async function discoverTargets(port = 9222) {
4795
4795
  });
4796
4796
  }
4797
4797
  async function isCdpAvailable(port = 9222, userDataDir) {
4798
- if (readDevToolsActivePort(userDataDir) !== null) return true;
4798
+ const activePort = readDevToolsActivePort(userDataDir)?.port;
4799
+ if (activePort !== void 0) {
4800
+ try {
4801
+ await discoverTargets(activePort);
4802
+ return true;
4803
+ } catch {
4804
+ if (activePort === port) return false;
4805
+ }
4806
+ }
4799
4807
  try {
4800
4808
  await discoverTargets(port);
4801
4809
  return true;
@@ -9574,9 +9582,6 @@ function validateAgentBayInput(input) {
9574
9582
  if (input.headless === true) {
9575
9583
  throw new Error("headless=true is not supported by the AgentBay browser provider");
9576
9584
  }
9577
- if (input.persistent === true) {
9578
- throw new Error("persistent is not supported by the AgentBay CDP provider yet");
9579
- }
9580
9585
  if (input.executablePath) {
9581
9586
  throw new Error("executablePath is only supported by the local browser provider");
9582
9587
  }
@@ -9592,6 +9597,9 @@ function resolveAgentBayImageId(value) {
9592
9597
  }
9593
9598
  return imageId;
9594
9599
  }
9600
+ function agentBayContextName(browserId) {
9601
+ return `mearl-browser-${hashKey(browserId)}`;
9602
+ }
9595
9603
  function normalizeDesktopUserAgent(value) {
9596
9604
  const userAgent = typeof value === "string" ? value.trim() : "";
9597
9605
  if (!userAgent) throw new Error("Could not determine the control browser user agent");
@@ -9608,16 +9616,16 @@ function normalizeAgentBayAccessUrl(value) {
9608
9616
  }
9609
9617
  }
9610
9618
  function browserSummary2(record2, running) {
9611
- const accessUrl = normalizeAgentBayAccessUrl(record2.accessUrl);
9612
9619
  return {
9613
9620
  browserId: record2.browserId,
9614
9621
  name: record2.name,
9615
9622
  provider: "agentbay",
9616
9623
  headless: false,
9617
- persistent: false,
9624
+ persistent: record2.persistent === true,
9618
9625
  agentbaySessionId: record2.sessionId,
9619
9626
  ...record2.imageId ? { agentbayImageId: record2.imageId } : {},
9620
- ...accessUrl ? { agentbayAccessUrl: accessUrl } : {},
9627
+ ...record2.contextId ? { agentbayContextId: record2.contextId } : {},
9628
+ ...record2.contextName ? { agentbayContextName: record2.contextName } : {},
9621
9629
  createdAt: record2.createdAt,
9622
9630
  ...record2.copiedCookies ? { copiedCookies: record2.copiedCookies } : {},
9623
9631
  running
@@ -9626,11 +9634,13 @@ function browserSummary2(record2, running) {
9626
9634
  function listRecords2() {
9627
9635
  const records = [];
9628
9636
  for (const file2 of listRecordFiles(keyedDir(AGENTBAY_BROWSERS_SUBDIR))) {
9629
- const record2 = readRecord(file2);
9630
- if (!record2) {
9637
+ const stored = readRecord(file2);
9638
+ if (!stored) {
9631
9639
  removeRecord(file2);
9632
9640
  continue;
9633
9641
  }
9642
+ const { accessUrl: legacyAccessUrl, ...record2 } = stored;
9643
+ if (legacyAccessUrl !== void 0) writeRecord(file2, record2);
9634
9644
  const running = isManagedProcess(record2.pid, `--browser-id ${record2.browserId}`);
9635
9645
  if (!running) removeRecord(keyedConfigFile("browsers", record2.browserId));
9636
9646
  records.push({ record: record2, running });
@@ -9651,12 +9661,46 @@ function listAgentBayBrowsers() {
9651
9661
  }
9652
9662
  async function requireSession(sessionId, apiKey) {
9653
9663
  const agentBay = await createClient(apiKey);
9664
+ return requireSessionFromClient(agentBay, sessionId);
9665
+ }
9666
+ async function requireSessionFromClient(agentBay, sessionId) {
9654
9667
  const result = await agentBay.get(sessionId);
9655
9668
  if (!result.success || !result.session) {
9656
9669
  throw new Error(result.errorMessage || `AgentBay session not found: ${sessionId}`);
9657
9670
  }
9658
9671
  return result.session;
9659
9672
  }
9673
+ async function getAgentBayAccessUrl(sessionId, dependencies = {}) {
9674
+ const apiKey = (dependencies.resolveApiKey ?? resolveAgentBayApiKey)();
9675
+ const agentBay = dependencies.createAgentBayClient ? await dependencies.createAgentBayClient(apiKey) : await createClient(apiKey);
9676
+ const session = await requireSessionFromClient(agentBay, sessionId);
9677
+ if (!session.info) {
9678
+ throw new Error("The installed AgentBay SDK does not support live session information");
9679
+ }
9680
+ const result = await session.info();
9681
+ if (result.success === false) {
9682
+ throw new Error(result.errorMessage || `AgentBay session info failed: ${sessionId}`);
9683
+ }
9684
+ const data = result.data && typeof result.data === "object" ? result.data : null;
9685
+ const accessUrl = normalizeAgentBayAccessUrl(
9686
+ data && "resourceUrl" in data ? data.resourceUrl : void 0
9687
+ );
9688
+ if (!accessUrl) {
9689
+ throw new Error(`AgentBay session did not return a browser access URL: ${sessionId}`);
9690
+ }
9691
+ return accessUrl;
9692
+ }
9693
+ async function resolvePersistentContext(agentBay, browserId) {
9694
+ if (!agentBay.context) {
9695
+ throw new Error("The installed AgentBay SDK does not support persistent browser contexts");
9696
+ }
9697
+ const name = agentBayContextName(browserId);
9698
+ const result = await agentBay.context.get(name, true);
9699
+ if (!result.success || !result.context?.id) {
9700
+ throw new Error(result.errorMessage || `Failed to create AgentBay browser context: ${name}`);
9701
+ }
9702
+ return { id: result.context.id, name: result.context.name || name };
9703
+ }
9660
9704
  async function resolveAgentBayEndpoint(sessionId) {
9661
9705
  const apiKey = resolveAgentBayApiKey();
9662
9706
  const { AgentBay, GetCdpLinkRequest } = await loadSdk();
@@ -9702,12 +9746,14 @@ async function launchAgentBayBrowser(input, deps) {
9702
9746
  const browserId = agentBayBrowserId(name);
9703
9747
  validateAgentBayInput(input);
9704
9748
  const imageId = resolveAgentBayImageId(input.imageId);
9749
+ const persistent = input.persistent === true;
9705
9750
  const userAgentMode = resolveUserAgentMode(input.userAgentMode);
9706
9751
  const copyCookieDomains = input.copyCookieDomains === void 0 ? [] : normalizeCookieDomains(input.copyCookieDomains);
9707
9752
  const apiKey = (deps.resolveApiKey ?? resolveAgentBayApiKey)();
9708
9753
  const releaseLock = acquireLaunchLock(browserId);
9709
9754
  const persistBrowserRecord = deps.persistBrowserRecord ?? ((record2) => writeRecord(keyedConfigFile(AGENTBAY_BROWSERS_SUBDIR, record2.browserId), record2));
9710
9755
  let session = null;
9756
+ let context;
9711
9757
  let hostPid = 0;
9712
9758
  let recordWritten = false;
9713
9759
  try {
@@ -9717,7 +9763,18 @@ async function launchAgentBayBrowser(input, deps) {
9717
9763
  userAgentMode === "desktop" ? deps.getBrowserUserAgent().then(normalizeDesktopUserAgent) : Promise.resolve(void 0)
9718
9764
  ]);
9719
9765
  const agentBay = await (deps.createAgentBayClient ?? createClient)(apiKey);
9720
- const created = await agentBay.create({ imageId, framework: "mearl" });
9766
+ context = persistent ? await resolvePersistentContext(agentBay, browserId) : void 0;
9767
+ const created = await agentBay.create({
9768
+ imageId,
9769
+ framework: "mearl",
9770
+ ...context ? {
9771
+ browserContext: {
9772
+ contextId: context.id,
9773
+ autoUpload: true,
9774
+ syncMode: "standard"
9775
+ }
9776
+ } : {}
9777
+ });
9721
9778
  if (!created.success || !created.session) {
9722
9779
  throw new Error(created.errorMessage || "AgentBay session creation failed");
9723
9780
  }
@@ -9750,14 +9807,14 @@ async function launchAgentBayBrowser(input, deps) {
9750
9807
  hostPid = host.pid ?? 0;
9751
9808
  if (!hostPid) throw new Error("AgentBay native host launched without a process id");
9752
9809
  host.unref();
9753
- const accessUrl = normalizeAgentBayAccessUrl(session.resourceUrl);
9754
9810
  const record2 = {
9755
9811
  pid: hostPid,
9756
9812
  browserId,
9757
9813
  name,
9758
9814
  sessionId: session.sessionId,
9759
9815
  imageId,
9760
- ...accessUrl ? { accessUrl } : {},
9816
+ persistent,
9817
+ ...context ? { contextId: context.id, contextName: context.name } : {},
9761
9818
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
9762
9819
  ...copyCookieDomains.length > 0 ? { copiedCookies: { domains: copyCookieDomains, count: copiedCookieCount } } : {}
9763
9820
  };
@@ -9783,14 +9840,14 @@ async function launchAgentBayBrowser(input, deps) {
9783
9840
  cleanupError = deleteError;
9784
9841
  }
9785
9842
  if (cleanupError) {
9786
- const accessUrl = normalizeAgentBayAccessUrl(session.resourceUrl);
9787
9843
  const recoveryRecord = {
9788
9844
  pid: hostPid,
9789
9845
  browserId,
9790
9846
  name,
9791
9847
  sessionId: session.sessionId,
9792
9848
  imageId,
9793
- ...accessUrl ? { accessUrl } : {},
9849
+ persistent,
9850
+ ...context ? { contextId: context.id, contextName: context.name } : {},
9794
9851
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
9795
9852
  };
9796
9853
  let recoverySaved = false;
@@ -9827,16 +9884,72 @@ function isMissingSessionError(error48) {
9827
9884
  const message = error48 instanceof Error ? error48.message : String(error48);
9828
9885
  return /not[ -]?found|does not exist|already deleted|invalid session/i.test(message);
9829
9886
  }
9830
- async function closeAgentBayBrowser(selector) {
9831
- const record2 = findRecord(selector);
9832
- if (!record2) throw new Error(`AgentBay browser not found: ${selector}`);
9887
+ async function releaseAgentBayBrowserResources(record2, agentBay, options = {}) {
9888
+ const deleteContext = record2.persistent === true && options.deleteProfile === true;
9889
+ const syncContext = record2.persistent === true && !deleteContext;
9890
+ let contextSynced = Boolean(record2.contextSyncedAt);
9891
+ let session;
9833
9892
  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");
9893
+ session = await requireSessionFromClient(agentBay, record2.sessionId);
9837
9894
  } catch (error48) {
9838
9895
  if (!isMissingSessionError(error48)) throw error48;
9839
9896
  }
9897
+ if (session) {
9898
+ if (syncContext && !contextSynced) {
9899
+ if (!session.callMcpTool) {
9900
+ throw new Error("The installed AgentBay SDK cannot stop the browser before context sync");
9901
+ }
9902
+ const stopped = await session.callMcpTool("stopChrome", {}, false);
9903
+ if (stopped.success === false) {
9904
+ throw new Error(stopped.errorMessage || "AgentBay browser shutdown failed");
9905
+ }
9906
+ if (!session.context) {
9907
+ throw new Error("The installed AgentBay SDK cannot synchronize browser context state");
9908
+ }
9909
+ const synced = await session.context.sync();
9910
+ if (!synced.success) {
9911
+ throw new Error(synced.errorMessage || "AgentBay browser context sync failed");
9912
+ }
9913
+ contextSynced = true;
9914
+ record2.contextSyncedAt = (/* @__PURE__ */ new Date()).toISOString();
9915
+ options.persistRecord?.(record2);
9916
+ }
9917
+ try {
9918
+ const result = await session.delete(false);
9919
+ if (!result.success) {
9920
+ throw new Error(result.errorMessage || "AgentBay session deletion failed");
9921
+ }
9922
+ } catch (error48) {
9923
+ if (!isMissingSessionError(error48)) throw error48;
9924
+ }
9925
+ }
9926
+ if (deleteContext) {
9927
+ if (!record2.contextId || !record2.contextName || !agentBay.context) {
9928
+ throw new Error("AgentBay browser context metadata is unavailable for profile deletion");
9929
+ }
9930
+ const result = await agentBay.context.delete({
9931
+ id: record2.contextId,
9932
+ name: record2.contextName
9933
+ });
9934
+ if (!result.success) throw new Error(result.errorMessage || "AgentBay context deletion failed");
9935
+ }
9936
+ return {
9937
+ sessionDeleted: true,
9938
+ ...syncContext ? { contextSynced } : {},
9939
+ ...deleteContext ? { contextDeleted: true } : {}
9940
+ };
9941
+ }
9942
+ async function closeAgentBayBrowser(selector, options = {}) {
9943
+ const record2 = findRecord(selector);
9944
+ if (!record2) throw new Error(`AgentBay browser not found: ${selector}`);
9945
+ const agentBay = await createClient(resolveAgentBayApiKey());
9946
+ const released = await releaseAgentBayBrowserResources(record2, agentBay, {
9947
+ ...options,
9948
+ persistRecord: (updatedRecord) => writeRecord(
9949
+ keyedConfigFile(AGENTBAY_BROWSERS_SUBDIR, updatedRecord.browserId),
9950
+ updatedRecord
9951
+ )
9952
+ });
9840
9953
  const closingOwnHost = record2.pid === process.pid;
9841
9954
  if (!closingOwnHost) await stopProcess(record2.pid, `--browser-id ${record2.browserId}`);
9842
9955
  removeRecord(keyedConfigFile(AGENTBAY_BROWSERS_SUBDIR, record2.browserId));
@@ -9849,7 +9962,8 @@ async function closeAgentBayBrowser(selector) {
9849
9962
  browserId: record2.browserId,
9850
9963
  provider: "agentbay",
9851
9964
  closed: true,
9852
- sessionDeleted: true
9965
+ ...released,
9966
+ ...record2.persistent === true ? { profileDeleted: options.deleteProfile === true } : { profileDeleted: true }
9853
9967
  };
9854
9968
  }
9855
9969
 
@@ -10006,7 +10120,8 @@ function mergeBrowserInventory(connected, managed) {
10006
10120
  ..."cdpPort" in managedBrowser ? { cdpPort: managedBrowser.cdpPort } : {},
10007
10121
  ..."agentbaySessionId" in managedBrowser ? { agentbaySessionId: managedBrowser.agentbaySessionId } : {},
10008
10122
  ..."agentbayImageId" in managedBrowser && managedBrowser.agentbayImageId ? { agentbayImageId: managedBrowser.agentbayImageId } : {},
10009
- ..."agentbayAccessUrl" in managedBrowser && managedBrowser.agentbayAccessUrl ? { agentbayAccessUrl: managedBrowser.agentbayAccessUrl } : {},
10123
+ ..."agentbayContextId" in managedBrowser && managedBrowser.agentbayContextId ? { agentbayContextId: managedBrowser.agentbayContextId } : {},
10124
+ ..."agentbayContextName" in managedBrowser && managedBrowser.agentbayContextName ? { agentbayContextName: managedBrowser.agentbayContextName } : {},
10010
10125
  createdAt: managedBrowser.createdAt,
10011
10126
  ..."account" in managedBrowser ? { account: managedBrowser.account } : {},
10012
10127
  ..."copiedCookies" in managedBrowser ? { copiedCookies: managedBrowser.copiedCookies } : {}
@@ -10023,7 +10138,8 @@ function mergeBrowserInventory(connected, managed) {
10023
10138
  ..."cdpPort" in browser ? { cdpPort: browser.cdpPort } : {},
10024
10139
  ..."agentbaySessionId" in browser ? { agentbaySessionId: browser.agentbaySessionId } : {},
10025
10140
  ..."agentbayImageId" in browser && browser.agentbayImageId ? { agentbayImageId: browser.agentbayImageId } : {},
10026
- ..."agentbayAccessUrl" in browser && browser.agentbayAccessUrl ? { agentbayAccessUrl: browser.agentbayAccessUrl } : {},
10141
+ ..."agentbayContextId" in browser && browser.agentbayContextId ? { agentbayContextId: browser.agentbayContextId } : {},
10142
+ ..."agentbayContextName" in browser && browser.agentbayContextName ? { agentbayContextName: browser.agentbayContextName } : {},
10027
10143
  createdAt: browser.createdAt,
10028
10144
  ..."account" in browser ? { account: browser.account } : {},
10029
10145
  ..."copiedCookies" in browser ? { copiedCookies: browser.copiedCookies } : {}
@@ -10047,7 +10163,7 @@ import os2 from "node:os";
10047
10163
  import { randomUUID as randomUUID4 } from "node:crypto";
10048
10164
 
10049
10165
  // src/version.ts
10050
- var NATIVE_HOST_VERSION = true ? "2.5.0" : "unknown";
10166
+ var NATIVE_HOST_VERSION = true ? "2.6.0" : "unknown";
10051
10167
 
10052
10168
  // src/analytics.ts
10053
10169
  var MEASUREMENT_ID = "G-W84LJDZ6PH";
@@ -30701,6 +30817,7 @@ var nativeMessageBuffer = Buffer.alloc(0);
30701
30817
  var socketServer = null;
30702
30818
  var legacyServer = null;
30703
30819
  var inventoryExitTimer = null;
30820
+ var cdpRegistrationTimer = null;
30704
30821
  function scheduleInventoryExit() {
30705
30822
  if (inventoryExitTimer) clearTimeout(inventoryExitTimer);
30706
30823
  inventoryExitTimer = setTimeout(() => cleanupAndExit(0), INVENTORY_IDLE_EXIT_MS);
@@ -31010,6 +31127,24 @@ async function handleSocketLine(socket, line) {
31010
31127
  socket.write(JSON.stringify({ id, success: true, data: versions }) + "\n");
31011
31128
  return;
31012
31129
  }
31130
+ if (action === "get_agentbay_access_url") {
31131
+ try {
31132
+ if (!AGENTBAY_SESSION_ID) {
31133
+ throw new Error("The selected browser is not an AgentBay browser");
31134
+ }
31135
+ const agentbayAccessUrl = await getAgentBayAccessUrl(AGENTBAY_SESSION_ID);
31136
+ socket.write(JSON.stringify({ id, success: true, data: { agentbayAccessUrl } }) + "\n");
31137
+ } catch (error48) {
31138
+ socket.write(
31139
+ JSON.stringify({
31140
+ id,
31141
+ success: false,
31142
+ error: error48 instanceof Error ? error48.message : String(error48)
31143
+ }) + "\n"
31144
+ );
31145
+ }
31146
+ return;
31147
+ }
31013
31148
  if (isInternalExtensionAction(action)) {
31014
31149
  socket.write(
31015
31150
  JSON.stringify({ id, success: false, error: "Internal action is not available to clients" }) + "\n"
@@ -31037,7 +31172,7 @@ async function handleSocketLine(socket, line) {
31037
31172
  `Managed browser name is ambiguous: ${selector}. Use the full browserId from browser_list.`
31038
31173
  );
31039
31174
  }
31040
- const result = agentBayExists ? await closeAgentBayBrowser(selector) : await closeManagedBrowser(selector, { deleteProfile: data?.deleteProfile });
31175
+ const result = agentBayExists ? await closeAgentBayBrowser(selector, { deleteProfile: data?.deleteProfile }) : await closeManagedBrowser(selector, { deleteProfile: data?.deleteProfile });
31041
31176
  socket.write(JSON.stringify({ id, success: true, data: result }) + "\n");
31042
31177
  } catch (error48) {
31043
31178
  socket.write(
@@ -31405,6 +31540,10 @@ function cleanupAndExit(code) {
31405
31540
  clearTimeout(inventoryExitTimer);
31406
31541
  inventoryExitTimer = null;
31407
31542
  }
31543
+ if (cdpRegistrationTimer) {
31544
+ clearInterval(cdpRegistrationTimer);
31545
+ cdpRegistrationTimer = null;
31546
+ }
31408
31547
  cdpSession?.close();
31409
31548
  agentChatManager?.dispose();
31410
31549
  agentChatManager = null;
@@ -31426,6 +31565,47 @@ function restartAfterUpdate() {
31426
31565
  forcedExit.unref();
31427
31566
  cleanupAndExit(0);
31428
31567
  }
31568
+ async function isDaemonBrowserAvailable() {
31569
+ if (AGENTBAY_SESSION_ID) {
31570
+ return getCdpSession().getBrowserClient().then(
31571
+ () => true,
31572
+ () => false
31573
+ );
31574
+ }
31575
+ return isCdpAvailable(CDP_PORT, CDP_USER_DATA_DIR);
31576
+ }
31577
+ async function monitorDaemonBrowser() {
31578
+ let published = false;
31579
+ let checking = false;
31580
+ const refresh = async () => {
31581
+ if (checking) return;
31582
+ checking = true;
31583
+ try {
31584
+ const available = await isDaemonBrowserAvailable();
31585
+ if (available && !published) {
31586
+ registerBrowser({
31587
+ browserId: CDP_BROWSER_ID,
31588
+ socketPath: INSTANCE_SOCKET_PATH,
31589
+ browserName: CDP_BROWSER_NAME,
31590
+ nativeHostVersion: NATIVE_HOST_VERSION,
31591
+ transport: "cdp",
31592
+ lastFocusedAt: CDP_BROWSER_ID.startsWith("managed:") ? 0 : void 0
31593
+ });
31594
+ published = true;
31595
+ } else if (!available && published) {
31596
+ unregisterBrowser();
31597
+ published = false;
31598
+ }
31599
+ } finally {
31600
+ checking = false;
31601
+ }
31602
+ };
31603
+ await refresh();
31604
+ cdpRegistrationTimer = setInterval(() => {
31605
+ void refresh();
31606
+ }, 2e3);
31607
+ cdpRegistrationTimer.unref();
31608
+ }
31429
31609
  async function main() {
31430
31610
  initBrowserRegistry({ log });
31431
31611
  pruneDeadBrowsers();
@@ -31447,14 +31627,7 @@ async function main() {
31447
31627
  return;
31448
31628
  }
31449
31629
  if (isDaemonMode && !skipBrowserRegistration) {
31450
- registerBrowser({
31451
- browserId: CDP_BROWSER_ID,
31452
- socketPath: INSTANCE_SOCKET_PATH,
31453
- browserName: CDP_BROWSER_NAME,
31454
- nativeHostVersion: NATIVE_HOST_VERSION,
31455
- transport: "cdp",
31456
- lastFocusedAt: CDP_BROWSER_ID.startsWith("managed:") ? 0 : void 0
31457
- });
31630
+ await monitorDaemonBrowser();
31458
31631
  }
31459
31632
  if (AGENTBAY_SESSION_ID) {
31460
31633
  const keepAliveTimer = setInterval(() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mearl/native-host",
3
- "version": "2.5.0",
3
+ "version": "2.6.0",
4
4
  "description": "Native Messaging Host for Mearl — bridges Chrome Extension and local socket server",
5
5
  "type": "module",
6
6
  "main": "dist/install.js",
@@ -37,9 +37,9 @@
37
37
  "@types/ws": "^8.18.1",
38
38
  "esbuild": "^0.25.2",
39
39
  "typescript": "^5.8.3",
40
- "@mearl/browser-core": "2.5.0",
41
- "@mearl/daemon-core": "2.5.0",
42
- "@mearl/setup": "2.5.0"
40
+ "@mearl/browser-core": "2.6.0",
41
+ "@mearl/daemon-core": "2.6.0",
42
+ "@mearl/setup": "2.6.0"
43
43
  },
44
44
  "scripts": {
45
45
  "build": "rm -rf dist/ && node build.mjs",