@monoes/monobrowse 1.0.2 → 1.0.4

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.
Files changed (277) hide show
  1. package/dist/src/__tests__/batch-eval-parsing.test.d.ts +2 -0
  2. package/dist/src/__tests__/batch-eval-parsing.test.d.ts.map +1 -0
  3. package/dist/src/__tests__/batch-eval-parsing.test.js +47 -0
  4. package/dist/src/__tests__/batch-eval-parsing.test.js.map +1 -0
  5. package/dist/src/__tests__/output.test.d.ts +2 -0
  6. package/dist/src/__tests__/output.test.d.ts.map +1 -0
  7. package/dist/src/__tests__/output.test.js +54 -0
  8. package/dist/src/__tests__/output.test.js.map +1 -0
  9. package/dist/src/__tests__/ref-cache.test.d.ts +2 -0
  10. package/dist/src/__tests__/ref-cache.test.d.ts.map +1 -0
  11. package/dist/src/__tests__/ref-cache.test.js +145 -0
  12. package/dist/src/__tests__/ref-cache.test.js.map +1 -0
  13. package/dist/src/browser/action-builder/analyzer.d.ts +0 -0
  14. package/dist/src/browser/action-builder/analyzer.d.ts.map +0 -0
  15. package/dist/src/browser/action-builder/analyzer.js +0 -0
  16. package/dist/src/browser/action-builder/analyzer.js.map +1 -1
  17. package/dist/src/browser/action-builder/types.d.ts +0 -0
  18. package/dist/src/browser/action-builder/types.d.ts.map +0 -0
  19. package/dist/src/browser/action-builder/types.js +0 -0
  20. package/dist/src/browser/action-builder/types.js.map +0 -0
  21. package/dist/src/browser/actions.d.ts +1 -1
  22. package/dist/src/browser/actions.d.ts.map +1 -1
  23. package/dist/src/browser/actions.js +19 -2
  24. package/dist/src/browser/actions.js.map +1 -1
  25. package/dist/src/browser/adapters/gemini.d.ts +0 -0
  26. package/dist/src/browser/adapters/gemini.d.ts.map +0 -0
  27. package/dist/src/browser/adapters/gemini.js +0 -0
  28. package/dist/src/browser/adapters/gemini.js.map +0 -0
  29. package/dist/src/browser/adapters/google.d.ts +0 -0
  30. package/dist/src/browser/adapters/google.d.ts.map +0 -0
  31. package/dist/src/browser/adapters/google.js +0 -0
  32. package/dist/src/browser/adapters/google.js.map +0 -0
  33. package/dist/src/browser/adapters/index.d.ts +0 -0
  34. package/dist/src/browser/adapters/index.d.ts.map +0 -0
  35. package/dist/src/browser/adapters/index.js +0 -0
  36. package/dist/src/browser/adapters/index.js.map +0 -0
  37. package/dist/src/browser/adapters/instagram.d.ts +0 -0
  38. package/dist/src/browser/adapters/instagram.d.ts.map +0 -0
  39. package/dist/src/browser/adapters/instagram.js +0 -0
  40. package/dist/src/browser/adapters/instagram.js.map +0 -0
  41. package/dist/src/browser/adapters/linkedin.d.ts +0 -0
  42. package/dist/src/browser/adapters/linkedin.d.ts.map +0 -0
  43. package/dist/src/browser/adapters/linkedin.js +0 -0
  44. package/dist/src/browser/adapters/linkedin.js.map +0 -0
  45. package/dist/src/browser/adapters/microsoft.d.ts +0 -0
  46. package/dist/src/browser/adapters/microsoft.d.ts.map +0 -0
  47. package/dist/src/browser/adapters/microsoft.js +0 -0
  48. package/dist/src/browser/adapters/microsoft.js.map +0 -0
  49. package/dist/src/browser/adapters/x.d.ts +0 -0
  50. package/dist/src/browser/adapters/x.d.ts.map +0 -0
  51. package/dist/src/browser/adapters/x.js +0 -0
  52. package/dist/src/browser/adapters/x.js.map +0 -0
  53. package/dist/src/browser/batch.d.ts +0 -0
  54. package/dist/src/browser/batch.d.ts.map +0 -0
  55. package/dist/src/browser/batch.js +0 -0
  56. package/dist/src/browser/batch.js.map +0 -0
  57. package/dist/src/browser/browser.d.ts +10 -0
  58. package/dist/src/browser/browser.d.ts.map +1 -1
  59. package/dist/src/browser/browser.js +111 -3
  60. package/dist/src/browser/browser.js.map +1 -1
  61. package/dist/src/browser/cdp.d.ts +0 -0
  62. package/dist/src/browser/cdp.d.ts.map +1 -1
  63. package/dist/src/browser/cdp.js +1 -1
  64. package/dist/src/browser/cdp.js.map +1 -1
  65. package/dist/src/browser/console-log.d.ts +0 -0
  66. package/dist/src/browser/console-log.d.ts.map +0 -0
  67. package/dist/src/browser/console-log.js +0 -0
  68. package/dist/src/browser/console-log.js.map +0 -0
  69. package/dist/src/browser/dashboard/api-types.d.ts +0 -0
  70. package/dist/src/browser/dashboard/api-types.d.ts.map +0 -0
  71. package/dist/src/browser/dashboard/api-types.js +0 -0
  72. package/dist/src/browser/dashboard/api-types.js.map +0 -0
  73. package/dist/src/browser/dashboard/server.d.ts +0 -0
  74. package/dist/src/browser/dashboard/server.d.ts.map +1 -1
  75. package/dist/src/browser/dashboard/server.js +37 -2
  76. package/dist/src/browser/dashboard/server.js.map +1 -1
  77. package/dist/src/browser/dashboard/ui.html +1 -1
  78. package/dist/src/browser/dialog.d.ts +0 -0
  79. package/dist/src/browser/dialog.d.ts.map +0 -0
  80. package/dist/src/browser/dialog.js +0 -0
  81. package/dist/src/browser/dialog.js.map +0 -0
  82. package/dist/src/browser/emulation.d.ts +0 -0
  83. package/dist/src/browser/emulation.d.ts.map +0 -0
  84. package/dist/src/browser/emulation.js +0 -0
  85. package/dist/src/browser/emulation.js.map +0 -0
  86. package/dist/src/browser/find.d.ts +0 -0
  87. package/dist/src/browser/find.d.ts.map +0 -0
  88. package/dist/src/browser/find.js +0 -0
  89. package/dist/src/browser/find.js.map +1 -1
  90. package/dist/src/browser/har.d.ts +0 -0
  91. package/dist/src/browser/har.d.ts.map +0 -0
  92. package/dist/src/browser/har.js +0 -0
  93. package/dist/src/browser/har.js.map +0 -0
  94. package/dist/src/browser/index.d.ts +1 -0
  95. package/dist/src/browser/index.d.ts.map +1 -1
  96. package/dist/src/browser/index.js +1 -0
  97. package/dist/src/browser/index.js.map +1 -1
  98. package/dist/src/browser/network.d.ts +7 -0
  99. package/dist/src/browser/network.d.ts.map +1 -1
  100. package/dist/src/browser/network.js +10 -0
  101. package/dist/src/browser/network.js.map +1 -1
  102. package/dist/src/browser/pdf.d.ts +0 -0
  103. package/dist/src/browser/pdf.d.ts.map +0 -0
  104. package/dist/src/browser/pdf.js +0 -0
  105. package/dist/src/browser/pdf.js.map +1 -1
  106. package/dist/src/browser/profiler.d.ts +0 -0
  107. package/dist/src/browser/profiler.d.ts.map +0 -0
  108. package/dist/src/browser/profiler.js +0 -0
  109. package/dist/src/browser/profiler.js.map +1 -1
  110. package/dist/src/browser/record.d.ts +3 -0
  111. package/dist/src/browser/record.d.ts.map +1 -1
  112. package/dist/src/browser/record.js +37 -5
  113. package/dist/src/browser/record.js.map +1 -1
  114. package/dist/src/browser/ref-cache.d.ts +41 -0
  115. package/dist/src/browser/ref-cache.d.ts.map +1 -0
  116. package/dist/src/browser/ref-cache.js +120 -0
  117. package/dist/src/browser/ref-cache.js.map +1 -0
  118. package/dist/src/browser/screenshot.d.ts +0 -0
  119. package/dist/src/browser/screenshot.d.ts.map +0 -0
  120. package/dist/src/browser/screenshot.js +0 -0
  121. package/dist/src/browser/screenshot.js.map +1 -1
  122. package/dist/src/browser/session.d.ts +0 -0
  123. package/dist/src/browser/session.d.ts.map +1 -1
  124. package/dist/src/browser/session.js +12 -7
  125. package/dist/src/browser/session.js.map +1 -1
  126. package/dist/src/browser/snapshot.d.ts +0 -0
  127. package/dist/src/browser/snapshot.d.ts.map +0 -0
  128. package/dist/src/browser/snapshot.js +0 -0
  129. package/dist/src/browser/snapshot.js.map +1 -1
  130. package/dist/src/browser/storage.d.ts +0 -0
  131. package/dist/src/browser/storage.d.ts.map +0 -0
  132. package/dist/src/browser/storage.js +0 -0
  133. package/dist/src/browser/storage.js.map +0 -0
  134. package/dist/src/browser/tabs.d.ts +1 -1
  135. package/dist/src/browser/tabs.d.ts.map +1 -1
  136. package/dist/src/browser/tabs.js +20 -4
  137. package/dist/src/browser/tabs.js.map +1 -1
  138. package/dist/src/browser/trace.d.ts +0 -0
  139. package/dist/src/browser/trace.d.ts.map +0 -0
  140. package/dist/src/browser/trace.js +0 -0
  141. package/dist/src/browser/trace.js.map +1 -1
  142. package/dist/src/browser/types.d.ts +0 -0
  143. package/dist/src/browser/types.d.ts.map +0 -0
  144. package/dist/src/browser/types.js +0 -0
  145. package/dist/src/browser/types.js.map +0 -0
  146. package/dist/src/browser/vitals.d.ts +0 -0
  147. package/dist/src/browser/vitals.d.ts.map +0 -0
  148. package/dist/src/browser/vitals.js +0 -0
  149. package/dist/src/browser/vitals.js.map +0 -0
  150. package/dist/src/browser/wait.d.ts +0 -0
  151. package/dist/src/browser/wait.d.ts.map +0 -0
  152. package/dist/src/browser/wait.js +0 -0
  153. package/dist/src/browser/wait.js.map +0 -0
  154. package/dist/src/cli/action.d.ts +0 -0
  155. package/dist/src/cli/action.d.ts.map +1 -1
  156. package/dist/src/cli/action.js +87 -2
  157. package/dist/src/cli/action.js.map +1 -1
  158. package/dist/src/cli/commands.d.ts +22 -0
  159. package/dist/src/cli/commands.d.ts.map +1 -1
  160. package/dist/src/cli/commands.js +276 -28
  161. package/dist/src/cli/commands.js.map +1 -1
  162. package/dist/src/cli/output.d.ts +0 -0
  163. package/dist/src/cli/output.d.ts.map +1 -1
  164. package/dist/src/cli/output.js +0 -0
  165. package/dist/src/cli/output.js.map +0 -0
  166. package/dist/src/cli/platform.d.ts +0 -0
  167. package/dist/src/cli/platform.d.ts.map +0 -0
  168. package/dist/src/cli/platform.js +0 -0
  169. package/dist/src/cli/platform.js.map +0 -0
  170. package/dist/src/cli/types.d.ts +0 -0
  171. package/dist/src/cli/types.d.ts.map +0 -0
  172. package/dist/src/cli/types.js +0 -0
  173. package/dist/src/cli/types.js.map +0 -0
  174. package/dist/src/cli.d.ts +0 -0
  175. package/dist/src/cli.d.ts.map +0 -0
  176. package/dist/src/cli.js.map +1 -1
  177. package/dist/src/index.d.ts +0 -0
  178. package/dist/src/index.d.ts.map +0 -0
  179. package/dist/src/index.js +0 -0
  180. package/dist/src/index.js.map +0 -0
  181. package/dist/tsconfig.tsbuildinfo +1 -1
  182. package/package.json +2 -2
  183. package/src/__tests__/batch-eval-parsing.test.ts +52 -0
  184. package/src/__tests__/output.test.ts +62 -0
  185. package/src/__tests__/ref-cache.test.ts +175 -0
  186. package/src/browser/action-builder/analyzer.ts +0 -0
  187. package/src/browser/action-builder/types.ts +0 -0
  188. package/src/browser/actions.ts +22 -2
  189. package/src/browser/adapters/gemini.ts +0 -0
  190. package/src/browser/adapters/google.ts +0 -0
  191. package/src/browser/adapters/index.ts +0 -0
  192. package/src/browser/adapters/instagram.ts +0 -0
  193. package/src/browser/adapters/linkedin.ts +0 -0
  194. package/src/browser/adapters/microsoft.ts +0 -0
  195. package/src/browser/adapters/x.ts +0 -0
  196. package/src/browser/batch.ts +0 -0
  197. package/src/browser/browser.ts +115 -2
  198. package/src/browser/cdp.ts +1 -1
  199. package/src/browser/console-log.ts +0 -0
  200. package/src/browser/dashboard/api-types.ts +0 -0
  201. package/src/browser/dashboard/server.ts +37 -2
  202. package/src/browser/dashboard/ui.html +1 -1
  203. package/src/browser/dialog.ts +0 -0
  204. package/src/browser/emulation.ts +0 -0
  205. package/src/browser/find.ts +0 -0
  206. package/src/browser/har.ts +0 -0
  207. package/src/browser/index.ts +1 -0
  208. package/src/browser/network.ts +11 -0
  209. package/src/browser/pdf.ts +0 -0
  210. package/src/browser/profiler.ts +0 -0
  211. package/src/browser/record.ts +43 -6
  212. package/src/browser/ref-cache.ts +141 -0
  213. package/src/browser/screenshot.ts +0 -0
  214. package/src/browser/session.ts +13 -7
  215. package/src/browser/snapshot.ts +0 -0
  216. package/src/browser/storage.ts +0 -0
  217. package/src/browser/tabs.ts +36 -7
  218. package/src/browser/trace.ts +0 -0
  219. package/src/browser/types.ts +0 -0
  220. package/src/browser/vitals.ts +0 -0
  221. package/src/browser/wait.ts +0 -0
  222. package/src/cli/action.ts +85 -3
  223. package/src/cli/commands.ts +272 -28
  224. package/src/cli/output.ts +0 -0
  225. package/src/cli/platform.ts +0 -0
  226. package/src/cli/types.ts +0 -0
  227. package/src/cli.ts +0 -0
  228. package/src/index.ts +0 -0
  229. package/dist/package.json +0 -71
  230. package/dist/src/browser/playbook/browser-handlers.d.ts +0 -3
  231. package/dist/src/browser/playbook/browser-handlers.d.ts.map +0 -1
  232. package/dist/src/browser/playbook/browser-handlers.js +0 -377
  233. package/dist/src/browser/playbook/browser-handlers.js.map +0 -1
  234. package/dist/src/browser/playbook/builtin-handlers.d.ts +0 -3
  235. package/dist/src/browser/playbook/builtin-handlers.d.ts.map +0 -1
  236. package/dist/src/browser/playbook/builtin-handlers.js +0 -364
  237. package/dist/src/browser/playbook/builtin-handlers.js.map +0 -1
  238. package/dist/src/browser/playbook/index.d.ts +0 -3
  239. package/dist/src/browser/playbook/index.d.ts.map +0 -1
  240. package/dist/src/browser/playbook/index.js +0 -3
  241. package/dist/src/browser/playbook/index.js.map +0 -1
  242. package/dist/src/browser/playbook/store.d.ts +0 -8
  243. package/dist/src/browser/playbook/store.d.ts.map +0 -1
  244. package/dist/src/browser/playbook/store.js +0 -87
  245. package/dist/src/browser/playbook/store.js.map +0 -1
  246. package/dist/src/browser/workflow/builtin-handlers.d.ts +0 -3
  247. package/dist/src/browser/workflow/builtin-handlers.d.ts.map +0 -1
  248. package/dist/src/browser/workflow/builtin-handlers.js +0 -343
  249. package/dist/src/browser/workflow/builtin-handlers.js.map +0 -1
  250. package/dist/src/browser/workflow/engine.d.ts +0 -10
  251. package/dist/src/browser/workflow/engine.d.ts.map +0 -1
  252. package/dist/src/browser/workflow/engine.js +0 -174
  253. package/dist/src/browser/workflow/engine.js.map +0 -1
  254. package/dist/src/browser/workflow/expression.d.ts +0 -4
  255. package/dist/src/browser/workflow/expression.d.ts.map +0 -1
  256. package/dist/src/browser/workflow/expression.js +0 -89
  257. package/dist/src/browser/workflow/expression.js.map +0 -1
  258. package/dist/src/browser/workflow/index.d.ts +0 -6
  259. package/dist/src/browser/workflow/index.d.ts.map +0 -1
  260. package/dist/src/browser/workflow/index.js +0 -6
  261. package/dist/src/browser/workflow/index.js.map +0 -1
  262. package/dist/src/browser/workflow/store.d.ts +0 -8
  263. package/dist/src/browser/workflow/store.d.ts.map +0 -1
  264. package/dist/src/browser/workflow/store.js +0 -87
  265. package/dist/src/browser/workflow/store.js.map +0 -1
  266. package/dist/src/browser/workflow/types.d.ts +0 -62
  267. package/dist/src/browser/workflow/types.d.ts.map +0 -1
  268. package/dist/src/browser/workflow/types.js +0 -2
  269. package/dist/src/browser/workflow/types.js.map +0 -1
  270. package/dist/src/cli/playbook.d.ts +0 -3
  271. package/dist/src/cli/playbook.d.ts.map +0 -1
  272. package/dist/src/cli/playbook.js +0 -265
  273. package/dist/src/cli/playbook.js.map +0 -1
  274. package/dist/src/cli/workflow.d.ts +0 -2
  275. package/dist/src/cli/workflow.d.ts.map +0 -1
  276. package/dist/src/cli/workflow.js +0 -3
  277. package/dist/src/cli/workflow.js.map +0 -1
@@ -10,16 +10,42 @@ import { createPlatformCommand } from './platform.js';
10
10
  import type { CdpClient, ElementRef, NetworkRoute, FindAction } from '../index.js';
11
11
 
12
12
  // Runtime state (single session per CLI process)
13
+ const DEFAULT_PORT = 9222;
13
14
  let _client: CdpClient | null = null;
14
15
  let _sessionId = '';
15
16
  let _targetId = '';
16
- let _port = 9222;
17
+ let _port = DEFAULT_PORT;
17
18
  let _refs: Map<string, ElementRef> = new Map();
18
19
 
19
20
  async function getBrowser() {
20
21
  return import('../index.js');
21
22
  }
22
23
 
24
+ // Each CLI invocation is a fresh process, so this module's `_port` variable
25
+ // is always freshly initialized to DEFAULT_PORT — a `--port 9333` passed to
26
+ // an earlier `open` command has no effect on this process's own default.
27
+ // Resolve the persisted "active port" (written by `open --port N`, read via
28
+ // ref-cache.ts's saveActivePort/loadActivePort) so subsequent commands
29
+ // attach to the browser the user actually opened instead of silently
30
+ // launching/attaching to a second, unrelated Chrome instance on 9222.
31
+ async function resolveDefaultPort(browser: Awaited<ReturnType<typeof getBrowser>>): Promise<number> {
32
+ const persisted = await browser.loadActivePortInfo();
33
+ if (!persisted) return DEFAULT_PORT;
34
+ if (!persisted.launched) {
35
+ // connect-origin port: the browser belongs to someone else. If it died,
36
+ // relaunching our own headless Chrome on that port would squat the
37
+ // user's debug port and silently swap which browser commands act on —
38
+ // fail loudly instead.
39
+ try {
40
+ await fetch(`http://127.0.0.1:${persisted.port}/json/version`, { signal: AbortSignal.timeout(1500) });
41
+ } catch {
42
+ await browser.clearActivePort();
43
+ throw new Error(`Connected browser on port ${persisted.port} is gone — re-run \`connect\` (or \`open\` to launch a fresh one).`);
44
+ }
45
+ }
46
+ return persisted.port;
47
+ }
48
+
23
49
  async function ensureConnected(port: number, targetId?: string) {
24
50
  const browser = await getBrowser();
25
51
  if (!_client || !_client.isConnected()) {
@@ -30,16 +56,54 @@ async function ensureConnected(port: number, targetId?: string) {
30
56
  browser.teardownConsoleCapture(_sessionId);
31
57
  _client.close();
32
58
  }
33
- _port = await browser.launchBrowser({ port, headless: true });
59
+ const effectivePort = port === DEFAULT_PORT ? await resolveDefaultPort(browser) : port;
60
+ _port = await browser.launchBrowser({ port: effectivePort, headless: true });
34
61
  const conn = await browser.connectToTarget(_port, targetId);
35
62
  _client = conn.client;
36
63
  _sessionId = conn.sessionId;
37
64
  _targetId = conn.target.id;
38
65
  _refs = new Map();
66
+ await hydrateRefsFromCache(browser, _targetId, conn.target.url);
39
67
  }
40
68
  return { client: _client!, sessionId: _sessionId, targetId: _targetId };
41
69
  }
42
70
 
71
+ // Each CLI invocation is a fresh process, so the in-memory _refs Map built by
72
+ // a prior `snapshot` command is gone by the time a later `find`/`click`/etc.
73
+ // command runs. Rehydrate it from the on-disk ref cache (written by
74
+ // captureSnapshot call sites below) so refs resolved by a previous process
75
+ // remain usable. Falls back silently to an empty Map if no cache exists.
76
+ //
77
+ // The cached `url` field (captured at snapshot time) is compared against the
78
+ // browser's CURRENT url (`currentUrl`, from the just-fetched target info).
79
+ // A stale `backendDOMNodeId` can still successfully resolve via
80
+ // DOM.getBoxModel even after a same-tab SPA navigation or DOM mutation
81
+ // changed what's actually at those coordinates — so a mismatch here means
82
+ // EVERY ref in the cache is potentially pointing at the wrong element. We
83
+ // hard-invalidate (skip hydration entirely, so any @eN lookup fails loudly
84
+ // via resolveRef's "not found" error) rather than the weaker 30s time-based
85
+ // staleness check below, which only warns.
86
+ async function hydrateRefsFromCache(
87
+ browser: Awaited<ReturnType<typeof getBrowser>>,
88
+ targetId: string,
89
+ currentUrl: string
90
+ ): Promise<void> {
91
+ const cached = await browser.loadRefCache(targetId);
92
+ if (!cached) return;
93
+ if (currentUrl && cached.url && currentUrl !== cached.url) {
94
+ output.printError(
95
+ `Stale references — page has navigated (cache: ${cached.url} → current: ${currentUrl}). Re-run snapshot before using @eN refs.`
96
+ );
97
+ return; // leave _refs empty — do not attempt to resolve refs against a different page
98
+ }
99
+ _refs = cached.refs;
100
+ if (cached.stale) {
101
+ output.printWarning(
102
+ `AX ref cache is ${Math.round(cached.ageMs / 1000)}s old — page may have changed since the last snapshot; re-run snapshot if refs don't resolve as expected`
103
+ );
104
+ }
105
+ }
106
+
43
107
  // URL patterns that signal an unambiguous login/auth wall.
44
108
  // Excludes 'auth', 'oauth', 'sso', 'saml' — their callback/ACS/token paths
45
109
  // (/auth/callback, /sso/callback, /saml/acs) are completion endpoints, not walls.
@@ -128,11 +192,14 @@ async function switchToHeaded(url: string, port: number): Promise<void> {
128
192
  const authCookies = await browser.getCookies(_client, _sessionId).catch(() => [] as unknown[]);
129
193
  const authLocalStorage = await browser.getLocalStorage(_client, _sessionId).catch(() => ({}) as Record<string, string>);
130
194
 
131
- // Close headed session
195
+ // Close headed session — actually terminate the underlying Chrome process
196
+ // (Browser.close CDP command, PID-kill fallback), not just our CDP client
197
+ // connection, so the visible authenticated window doesn't linger forever.
132
198
  browser.teardownRouteInterception(_sessionId);
133
199
  browser.stopRequestCapture(_sessionId);
134
200
  browser.teardownDialogHandling(_sessionId);
135
201
  browser.teardownConsoleCapture(_sessionId);
202
+ await browser.closeBrowser(_client, headedPort);
136
203
  _client.close();
137
204
  _client = null;
138
205
  _sessionId = '';
@@ -154,6 +221,18 @@ async function switchToHeaded(url: string, port: number): Promise<void> {
154
221
  }
155
222
  }
156
223
 
224
+ /**
225
+ * Sanitize an image-format flag. The host CLI (monomind) defines a GLOBAL
226
+ * --format flag for output shaping (text|json|table, default 'text') that
227
+ * leaks into subcommand flags — so ctx.flags.format arrives as 'text' even
228
+ * when the user never passed it, and Chrome rejects it with
229
+ * "CDP error -32602: Invalid image format". Only trust values that are real
230
+ * image formats; otherwise fall back to the subcommand's own default.
231
+ */
232
+ function imageFormat<T extends string>(value: unknown, allowed: readonly T[], fallback: T): T {
233
+ return allowed.includes(value as T) ? (value as T) : fallback;
234
+ }
235
+
157
236
  function print(msg: string) {
158
237
  process.stdout.write(msg + '\n');
159
238
  }
@@ -203,11 +282,18 @@ const openCommand: Command = {
203
282
  }
204
283
 
205
284
  _port = await browser.launchBrowser({ port, headless: !forceHeaded });
285
+ // Persist the active port so subsequent CLI invocations (each a fresh
286
+ // process) default to attaching here instead of hardcoded 9222.
287
+ await browser.saveActivePort(_port);
206
288
  const conn = await browser.connectToTarget(_port);
207
289
  _client = conn.client;
208
290
  _sessionId = conn.sessionId;
209
291
  _targetId = conn.target.id;
210
292
  _refs = new Map();
293
+ // A snapshot taken before this navigation is no longer valid for the new
294
+ // page — drop the persisted ref cache so a stale process's weak
295
+ // time-based check can't resurrect it before the next explicit snapshot.
296
+ await browser.clearRefCache();
211
297
 
212
298
  if (ctx.flags.state && ctx.flags.session) {
213
299
  output.printWarning('Both --state and --session provided; --state takes precedence');
@@ -266,13 +352,11 @@ const snapshotCommand: Command = {
266
352
  });
267
353
 
268
354
  _refs = result.refs;
355
+ await browser.saveRefCache(_targetId, result.url, _refs);
269
356
 
270
357
  const applyOutputLimits = (text: string): string => {
271
- let out = text;
272
358
  const maxOutput = ctx.flags['max-output'] as number | undefined;
273
- if (maxOutput && out.length > maxOutput) {
274
- out = out.slice(0, maxOutput) + `\n[... truncated at ${maxOutput} chars]`;
275
- }
359
+ let out = maxOutput ? truncateForOutput(text, maxOutput) : text;
276
360
  if (ctx.flags['content-boundaries']) {
277
361
  const nonce = Math.random().toString(36).slice(2, 10);
278
362
  out = `MONOMIND_PAGE_CONTENT nonce=${nonce} origin=${result.url}\n${out}\nEND_MONOMIND_PAGE_CONTENT nonce=${nonce}`;
@@ -470,16 +554,29 @@ const waitCommand: Command = {
470
554
  const rawTimeout = Math.min((ctx.flags.timeout as number) ?? 30000, MAX_DOWNLOAD_TIMEOUT);
471
555
  const finalPath = await new Promise<string>((resolve, reject) => {
472
556
  let guid = '';
557
+ let settled = false;
473
558
  // C2: capture off() functions to avoid listener leaks
474
559
  const offBegin = client.on('Browser.downloadWillBegin', (params: Record<string, unknown>) => { guid = params.guid as string; });
475
560
  let offProgress: (() => void) | undefined;
476
561
  // cleanup defined before setTimeout so the timeout callback can call it
477
562
  let tid: ReturnType<typeof setTimeout>;
478
- const cleanup = () => { clearTimeout(tid); offBegin?.(); offProgress?.(); };
479
- tid = setTimeout(() => { cleanup(); reject(new Error('Download timed out')); }, rawTimeout);
563
+ let pollTid: ReturnType<typeof setInterval> | undefined;
564
+ const cleanup = () => { clearTimeout(tid); clearInterval(pollTid); offBegin?.(); offProgress?.(); };
565
+ const finish = (path: string) => {
566
+ if (settled) return;
567
+ settled = true;
568
+ cleanup();
569
+ resolve(path);
570
+ };
571
+ const fail = (err: Error) => {
572
+ if (settled) return;
573
+ settled = true;
574
+ cleanup();
575
+ reject(err);
576
+ };
577
+ tid = setTimeout(() => fail(new Error('Download timed out')), rawTimeout);
480
578
  offProgress = client.on('Browser.downloadProgress', async (params: Record<string, unknown>) => {
481
579
  if (params.guid === guid && params.state === 'completed') {
482
- cleanup();
483
580
  const { readdir, rename, rmdir } = await import('fs/promises');
484
581
  const files = await readdir(downloadDir);
485
582
  if (files.length > 0) {
@@ -487,16 +584,40 @@ const waitCommand: Command = {
487
584
  await mkdir(dirname(savePath), { recursive: true });
488
585
  await rename(src, savePath);
489
586
  await rmdir(downloadDir).catch(() => {}); // I1: cleanup temp dir
490
- resolve(savePath);
587
+ finish(savePath);
491
588
  } else {
492
589
  await rmdir(downloadDir).catch(() => {}); // I1: cleanup temp dir
493
- reject(new Error('Download completed but no file found'));
590
+ fail(new Error('Download completed but no file found'));
494
591
  }
495
592
  } else if (params.guid === guid && params.state === 'canceled') {
496
- cleanup();
497
- reject(new Error('Download was canceled'));
593
+ fail(new Error('Download was canceled'));
498
594
  }
499
595
  });
596
+
597
+ // Fallback for the empty-guid race: this process's listener attaches
598
+ // AFTER a separate `click` process may have already started (and even
599
+ // finished) the download — so downloadWillBegin/downloadProgress for it
600
+ // were never observed here. While guid is still empty, poll the expected
601
+ // output path directly: if it already exists with a stable, non-zero
602
+ // size across two consecutive checks, treat the download as complete.
603
+ let lastSize = -1;
604
+ let stableReads = 0;
605
+ pollTid = setInterval(async () => {
606
+ if (settled || guid) return; // a real CDP event has taken over
607
+ try {
608
+ const { stat } = await import('fs/promises');
609
+ const st = await stat(savePath);
610
+ if (st.isFile() && st.size > 0 && st.size === lastSize) {
611
+ stableReads++;
612
+ if (stableReads >= 2) finish(savePath);
613
+ } else {
614
+ stableReads = 0;
615
+ }
616
+ lastSize = st.size;
617
+ } catch {
618
+ // savePath not present yet — keep polling until timeout
619
+ }
620
+ }, 300);
500
621
  });
501
622
  output.printSuccess(`Download saved: ${finalPath}`);
502
623
  return { success: true, data: { path: finalPath } };
@@ -545,7 +666,7 @@ const screenshotCommand: Command = {
545
666
  result = await browser.captureScreenshot(client, sessionId, {
546
667
  path: ctx.args[0] as string,
547
668
  fullPage: ctx.flags.full as boolean,
548
- format: ctx.flags.format as 'png' | 'jpeg' | 'webp',
669
+ format: imageFormat(ctx.flags.format, ['png', 'jpeg', 'webp'] as const, 'png'),
549
670
  quality: ctx.flags.quality as number,
550
671
  annotate,
551
672
  refs: annotate ? _refs : undefined,
@@ -772,6 +893,10 @@ const navigateCommand: Command = {
772
893
  throw new Error(`Unknown direction: ${direction}. Use: back|forward|reload`);
773
894
  }
774
895
 
896
+ // Refs captured before this navigation may now point at different content.
897
+ _refs = new Map();
898
+ await browser.clearRefCache();
899
+
775
900
  output.printSuccess(`Navigated: ${direction}`);
776
901
  return { success: true };
777
902
  },
@@ -918,6 +1043,7 @@ const stateCommand: Command = {
918
1043
  await browser.clearLocalStorage(c, sid);
919
1044
  await browser.clearSessionStorage(c, sid);
920
1045
  _refs = new Map();
1046
+ await browser.clearRefCache();
921
1047
  output.printSuccess('Browser state cleared (cookies, localStorage, sessionStorage, refs)');
922
1048
  return { success: true };
923
1049
  }
@@ -1076,12 +1202,26 @@ const networkCommand: Command = {
1076
1202
  },
1077
1203
  };
1078
1204
 
1205
+ // Default output cap for eval when --max-output isn't passed explicitly —
1206
+ // print(String(result)) had NO limit at all (unlike snapshot's --max-output),
1207
+ // so e.g. `eval "document.documentElement.outerHTML"` could dump megabytes
1208
+ // straight into an agent's context. An explicit --max-output still overrides
1209
+ // this default.
1210
+ const DEFAULT_EVAL_MAX_OUTPUT = 50_000;
1211
+
1212
+ function truncateForOutput(text: string, maxOutput: number): string {
1213
+ if (!(maxOutput > 0) || text.length <= maxOutput) return text;
1214
+ return text.slice(0, maxOutput) + `\n[... truncated at ${maxOutput} chars]`;
1215
+ }
1216
+
1079
1217
  const evalCommand: Command = {
1080
1218
  name: 'eval',
1081
1219
  description: 'Evaluate JavaScript in page context. Usage: monomind browse eval "document.title"',
1082
1220
  options: [
1083
1221
  { name: 'json', type: 'boolean', description: 'Output as JSON', default: false },
1084
1222
  { name: 'stdin', type: 'boolean', description: 'Read JS expression from stdin (heredoc-friendly for multiline scripts)', default: false },
1223
+ { name: 'max-output', type: 'number', description: `Truncate printed output to N characters (default ${DEFAULT_EVAL_MAX_OUTPUT}; 0 disables truncation)` },
1224
+ { name: 'timeout', type: 'number', description: 'Max ms to wait for evaluation to settle (default 30000)' },
1085
1225
  ],
1086
1226
  action: async (ctx: CommandContext): Promise<CommandResult> => {
1087
1227
  const { client, sessionId } = await ensureConnected(_port);
@@ -1095,12 +1235,14 @@ const evalCommand: Command = {
1095
1235
  }
1096
1236
  if (!expr) throw new Error('Usage: monomind browse eval "<expression>" (or pipe with --stdin)');
1097
1237
 
1098
- const result = await browser.evaluateJs(client, sessionId, expr);
1238
+ const timeoutMs = ctx.flags.timeout as number | undefined;
1239
+ const result = await browser.evaluateJs(client, sessionId, expr, timeoutMs);
1099
1240
 
1241
+ const maxOutput = (ctx.flags['max-output'] as number | undefined) ?? DEFAULT_EVAL_MAX_OUTPUT;
1100
1242
  if (ctx.flags.json) {
1101
- print(JSON.stringify({ data: result }));
1243
+ print(truncateForOutput(JSON.stringify({ data: result }), maxOutput));
1102
1244
  } else {
1103
- print(String(result ?? ''));
1245
+ print(truncateForOutput(String(result ?? ''), maxOutput));
1104
1246
  }
1105
1247
 
1106
1248
  return { success: true, data: { result } };
@@ -1134,9 +1276,54 @@ const closeCommand: Command = {
1134
1276
  _sessionId = '';
1135
1277
  _targetId = '';
1136
1278
  _refs = new Map();
1279
+ // Session is gone — forget the persisted port and stale refs so later
1280
+ // invocations don't chase a dead endpoint (or the wrong elements).
1281
+ await browser.clearActivePort();
1282
+ await browser.clearRefCache();
1137
1283
  output.printSuccess('Browser session closed');
1138
1284
  } else {
1139
- output.printInfo('No active browser session');
1285
+ // Each CLI invocation is a fresh process, so `close` almost always
1286
+ // lands here. The persisted port file is the real session handle:
1287
+ // if monobrowse LAUNCHED that browser (open), gracefully Browser.close
1288
+ // it — otherwise every open→close cycle leaks a headless Chrome. If we
1289
+ // merely ATTACHED to it (connect, launched:false), never kill it: it's
1290
+ // the user's own browser. Either way, forget the port and refs.
1291
+ const browser = await getBrowser();
1292
+ const persisted = await browser.loadActivePortInfo();
1293
+ if (persisted?.launched) {
1294
+ try {
1295
+ const conn = await browser.connectToTarget(persisted.port);
1296
+ try {
1297
+ await browser.closeBrowser(conn.client, persisted.port);
1298
+ } finally {
1299
+ // Always drop our websocket — a hung Browser.close must not keep
1300
+ // this CLI process's event loop alive.
1301
+ try { conn.client.close(); } catch { /* already gone */ }
1302
+ }
1303
+ // closeBrowser has no PID fallback in a fresh process (launchedPids
1304
+ // is per-process) — re-probe so we report what actually happened.
1305
+ // Poll briefly: Browser.close is acknowledged before the process
1306
+ // actually exits, so a single immediate probe false-alarms.
1307
+ let stillUp = true;
1308
+ const probeDeadline = Date.now() + 3000;
1309
+ while (stillUp && Date.now() < probeDeadline) {
1310
+ try {
1311
+ await fetch(`http://127.0.0.1:${persisted.port}/json/version`, { signal: AbortSignal.timeout(800) });
1312
+ await new Promise(r => setTimeout(r, 300));
1313
+ } catch { stillUp = false; }
1314
+ }
1315
+ if (stillUp) output.printWarning(`Browser on port ${persisted.port} did not exit — kill it manually if needed`);
1316
+ else output.printSuccess(`Closed browser on port ${persisted.port}`);
1317
+ } catch {
1318
+ output.printInfo(`No browser answering on port ${persisted.port} — nothing to close`);
1319
+ }
1320
+ } else {
1321
+ output.printInfo(persisted
1322
+ ? `Detached from browser on port ${persisted.port} (attached via connect — left running)`
1323
+ : 'No active browser session');
1324
+ }
1325
+ await browser.clearActivePort();
1326
+ await browser.clearRefCache();
1140
1327
  }
1141
1328
  return { success: true };
1142
1329
  },
@@ -2228,6 +2415,7 @@ const diffCommand: Command = {
2228
2415
  const snap2 = await browser.captureSnapshot(client, sessionId, { interactiveOnly: ctx.flags.interactive as boolean });
2229
2416
 
2230
2417
  _refs = snap2.refs;
2418
+ await browser.saveRefCache(_targetId, snap2.url, _refs);
2231
2419
 
2232
2420
  // Text diff
2233
2421
  const lines1 = snap1.text.split('\n');
@@ -2265,6 +2453,10 @@ const pushstateCommand: Command = {
2265
2453
  const url = ctx.args[0] as string;
2266
2454
  if (!url) throw new Error('Usage: monomind browse pushstate <url>');
2267
2455
  await browser.pushState(client, sessionId, url);
2456
+ // SPA navigation changes what's on the page without a full page load —
2457
+ // refs captured before this pushState call may now resolve to different content.
2458
+ _refs = new Map();
2459
+ await browser.clearRefCache();
2268
2460
  output.printSuccess(`pushState: ${url}`);
2269
2461
  return { success: true };
2270
2462
  },
@@ -2290,6 +2482,54 @@ function tokenizeBatchCommand(input: string): string[] {
2290
2482
  return tokens;
2291
2483
  }
2292
2484
 
2485
+ /**
2486
+ * Splits one line of a `batch` command string into a subcommand name, its
2487
+ * args, and any recognized leading flags.
2488
+ *
2489
+ * `eval` is special-cased: its sole argument is a raw JS expression, which
2490
+ * legitimately contains its own string-literal quotes (e.g.
2491
+ * `eval document.querySelector('a')`). tokenizeBatchCommand's shell-style
2492
+ * word-splitting treats `'...'`/`"..."` as grouping delimiters and discards
2493
+ * them — correct for a value like `fill @e1 "some text"`, but for `eval` it
2494
+ * silently deletes the expression's own quotes, turning a string literal
2495
+ * into a bare (undefined) identifier reference and producing a
2496
+ * ReferenceError instead of evaluating the intended expression. For `eval`,
2497
+ * only its own known --flags are consumed from the front; everything after
2498
+ * them is taken verbatim as one argument, untouched by tokenization.
2499
+ *
2500
+ * Exported for direct unit testing — not part of the CLI's public API.
2501
+ */
2502
+ export function parseBatchCommandLine(cmdStr: string): { subName: string; subArgs: string[]; flags: Record<string, unknown> } {
2503
+ const trimmed = cmdStr.trim();
2504
+ const evalMatch = trimmed.match(/^eval\b\s*/);
2505
+ if (!evalMatch) {
2506
+ const parts = tokenizeBatchCommand(trimmed);
2507
+ return { subName: parts[0], subArgs: parts.slice(1), flags: {} };
2508
+ }
2509
+
2510
+ let rest = trimmed.slice(evalMatch[0].length);
2511
+ const flags: Record<string, unknown> = {};
2512
+ let consumedAnother = true;
2513
+ while (consumedAnother) {
2514
+ consumedAnother = false;
2515
+ const withValue = rest.match(/^--(max-output|timeout)\s+(-?\d+)\s*/);
2516
+ if (withValue) {
2517
+ flags[withValue[1]] = Number(withValue[2]);
2518
+ rest = rest.slice(withValue[0].length);
2519
+ consumedAnother = true;
2520
+ continue;
2521
+ }
2522
+ const boolFlag = rest.match(/^--(json|stdin)\b\s*/);
2523
+ if (boolFlag) {
2524
+ flags[boolFlag[1]] = true;
2525
+ rest = rest.slice(boolFlag[0].length);
2526
+ consumedAnother = true;
2527
+ continue;
2528
+ }
2529
+ }
2530
+ return { subName: 'eval', subArgs: [rest], flags };
2531
+ }
2532
+
2293
2533
  const batchCommand: Command = {
2294
2534
  name: 'batch',
2295
2535
  description: 'Execute multiple commands. Usage: monomind browse batch "open url" "snapshot -i" "click @e1"',
@@ -2303,9 +2543,7 @@ const batchCommand: Command = {
2303
2543
 
2304
2544
  const results: Array<{ command: string; success: boolean; error?: string }> = [];
2305
2545
  for (const cmdStr of commands) {
2306
- const parts = tokenizeBatchCommand(cmdStr);
2307
- const subName = parts[0];
2308
- const subArgs = parts.slice(1);
2546
+ const { subName, subArgs, flags: preParsedFlags } = parseBatchCommandLine(cmdStr);
2309
2547
 
2310
2548
  const subCmd = browseCommand.subcommands?.find((s) => s.name === subName);
2311
2549
  if (!subCmd?.action) {
@@ -2316,7 +2554,7 @@ const batchCommand: Command = {
2316
2554
  }
2317
2555
 
2318
2556
  try {
2319
- const parsedFlags: CommandContext['flags'] = { _: [] };
2557
+ const parsedFlags: CommandContext['flags'] = { _: [], ...preParsedFlags };
2320
2558
  const consumedIndices = new Set<number>();
2321
2559
  // Parse --flags from subArgs, tracking which indices are flag names/values
2322
2560
  for (let i = 0; i < subArgs.length; i++) {
@@ -2420,7 +2658,7 @@ const removeinitscriptCommand: Command = {
2420
2658
 
2421
2659
  const connectCommand: Command = {
2422
2660
  name: 'connect',
2423
- description: 'Connect to existing Chrome instance. Usage: monomind browse connect [--port 9222] [--target <id>] [--auto-connect]',
2661
+ description: 'Connect to existing Chrome instance; later commands reuse this session (note: `open` without --port still launches on its own default). Usage: monomind browse connect [--port 9222] [--target <id>] [--auto-connect]',
2424
2662
  options: [
2425
2663
  { name: 'port', short: 'p', type: 'number', description: 'CDP port', default: 9222 },
2426
2664
  { name: 'target', type: 'string', description: 'Target ID to attach to' },
@@ -2472,6 +2710,12 @@ const connectCommand: Command = {
2472
2710
  _targetId = conn.target.id;
2473
2711
  _port = port;
2474
2712
  _refs = new Map();
2713
+ // Persist the port like `open` does — without this, the NEXT CLI process
2714
+ // (each command is a fresh process) resolves the hardcoded default and
2715
+ // tries to launch its own Chrome on 9222 instead of reusing this session.
2716
+ // launched:false marks this browser as someone else's — close must never
2717
+ // kill it, and a dead endpoint must not be silently relaunched.
2718
+ await browser.saveActivePort(port, { launched: false });
2475
2719
  const url = await browser.getCurrentUrl(_client, _sessionId);
2476
2720
  const title = await browser.getCurrentTitle(_client, _sessionId);
2477
2721
  output.printSuccess(`Connected: ${title} (${url})`);
@@ -2496,7 +2740,7 @@ const recordCommand: Command = {
2496
2740
  switch (action) {
2497
2741
  case 'start':
2498
2742
  await browser.startRecording(client, sessionId, {
2499
- format: ctx.flags.format as 'jpeg' | 'png',
2743
+ format: imageFormat(ctx.flags.format, ['jpeg', 'png'] as const, 'jpeg'),
2500
2744
  quality: ctx.flags.quality as number,
2501
2745
  });
2502
2746
  output.printSuccess('Recording started');
@@ -2510,12 +2754,12 @@ const recordCommand: Command = {
2510
2754
  case 'restart': {
2511
2755
  const prevStatus = browser.getRecordingStatus(sessionId);
2512
2756
  let prevPath: string | undefined;
2513
- if (prevStatus.recording) {
2757
+ if (prevStatus.recording || prevStatus.autoStopped) {
2514
2758
  prevPath = await browser.stopRecording(client, sessionId, ctx.args[1] as string);
2515
2759
  output.printInfo(`Previous recording saved: ${prevPath}`);
2516
2760
  }
2517
2761
  await browser.startRecording(client, sessionId, {
2518
- format: ctx.flags.format as 'jpeg' | 'png',
2762
+ format: imageFormat(ctx.flags.format, ['jpeg', 'png'] as const, 'jpeg'),
2519
2763
  quality: ctx.flags.quality as number,
2520
2764
  });
2521
2765
  output.printSuccess('Recording restarted');
@@ -2524,7 +2768,7 @@ const recordCommand: Command = {
2524
2768
  case 'status': {
2525
2769
  const status = browser.getRecordingStatus(sessionId);
2526
2770
  if (ctx.flags.json) print(JSON.stringify({ data: status }));
2527
- else print(`Recording: ${status.recording} | Frames: ${status.frames}`);
2771
+ else print(`Recording: ${status.recording} | Frames: ${status.frames}${status.autoStopped ? ' (auto-stopped: buffer limit reached — run "record stop" to save)' : ''}`);
2528
2772
  return { success: true, data: status };
2529
2773
  }
2530
2774
  default:
package/src/cli/output.ts CHANGED
File without changes
File without changes
package/src/cli/types.ts CHANGED
File without changes
package/src/cli.ts CHANGED
File without changes
package/src/index.ts CHANGED
File without changes
package/dist/package.json DELETED
@@ -1,71 +0,0 @@
1
- {
2
- "name": "@monoes/monobrowse",
3
- "version": "1.0.1",
4
- "description": "Native browser automation via Chrome DevTools Protocol — the engine powering monomind browse",
5
- "type": "module",
6
- "bin": {
7
- "monobrowse": "./src/cli.js"
8
- },
9
- "main": "./src/index.js",
10
- "types": "./src/index.d.ts",
11
- "exports": {
12
- ".": {
13
- "types": "./src/index.d.ts",
14
- "import": "./src/index.js"
15
- },
16
- "./cli": {
17
- "types": "./src/cli.d.ts",
18
- "import": "./src/cli.js"
19
- },
20
- "./cli/commands": {
21
- "types": "./src/cli/commands.d.ts",
22
- "import": "./src/cli/commands.js"
23
- },
24
- "./cli/types": {
25
- "types": "./src/cli/types.d.ts",
26
- "import": "./src/cli/types.js"
27
- }
28
- },
29
- "files": [
30
- "dist",
31
- "src"
32
- ],
33
- "engines": {
34
- "node": ">=18.0.0"
35
- },
36
- "keywords": [
37
- "browser-automation",
38
- "cdp",
39
- "chrome-devtools-protocol",
40
- "monomind",
41
- "web-scraping",
42
- "accessibility"
43
- ],
44
- "author": {
45
- "name": "nokhodian",
46
- "email": "nokhodian@gmail.com",
47
- "url": "https://github.com/nokhodian"
48
- },
49
- "license": "MIT",
50
- "repository": {
51
- "type": "git",
52
- "url": "https://github.com/monoes/monomind.git",
53
- "directory": "packages/@monoes/monobrowse"
54
- },
55
- "dependencies": {
56
- "commander": "^12.1.0",
57
- "ws": "^8.21.0"
58
- },
59
- "peerDependencies": {
60
- "@anthropic-ai/sdk": ">=0.20.0"
61
- },
62
- "peerDependenciesMeta": {
63
- "@anthropic-ai/sdk": {
64
- "optional": true
65
- }
66
- },
67
- "devDependencies": {
68
- "@anthropic-ai/sdk": "^0.39.0",
69
- "typescript": "^5.8.3"
70
- }
71
- }
@@ -1,3 +0,0 @@
1
- import type { NodeHandler } from '@monoes/monoplaybook';
2
- export declare function createBrowserHandlers(): Map<string, NodeHandler>;
3
- //# sourceMappingURL=browser-handlers.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"browser-handlers.d.ts","sourceRoot":"","sources":["../../../../src/browser/playbook/browser-handlers.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,WAAW,EAAQ,MAAM,sBAAsB,CAAC;AAkI9D,wBAAgB,qBAAqB,IAAI,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CA2ShE"}