monomind 1.10.33 → 1.10.35

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 (28) hide show
  1. package/.claude/helpers/intelligence.cjs +3 -2
  2. package/.claude/helpers/session.cjs +2 -1
  3. package/.claude/skills/monomind/.monomind/registry.json +6 -0
  4. package/package.json +1 -1
  5. package/packages/@monomind/cli/dist/src/browser/browser.js +16 -12
  6. package/packages/@monomind/cli/dist/src/browser/cdp.d.ts +1 -1
  7. package/packages/@monomind/cli/dist/src/browser/cdp.js +4 -2
  8. package/packages/@monomind/cli/dist/src/browser/find.d.ts +1 -0
  9. package/packages/@monomind/cli/dist/src/browser/find.js +12 -0
  10. package/packages/@monomind/cli/dist/src/browser/har.d.ts +26 -0
  11. package/packages/@monomind/cli/dist/src/browser/har.js +130 -0
  12. package/packages/@monomind/cli/dist/src/browser/index.d.ts +5 -0
  13. package/packages/@monomind/cli/dist/src/browser/index.js +5 -0
  14. package/packages/@monomind/cli/dist/src/browser/network.d.ts +15 -0
  15. package/packages/@monomind/cli/dist/src/browser/network.js +54 -0
  16. package/packages/@monomind/cli/dist/src/browser/profiler.d.ts +10 -0
  17. package/packages/@monomind/cli/dist/src/browser/profiler.js +55 -0
  18. package/packages/@monomind/cli/dist/src/browser/record.d.ts +21 -0
  19. package/packages/@monomind/cli/dist/src/browser/record.js +48 -0
  20. package/packages/@monomind/cli/dist/src/browser/snapshot.js +23 -2
  21. package/packages/@monomind/cli/dist/src/browser/trace.d.ts +10 -0
  22. package/packages/@monomind/cli/dist/src/browser/trace.js +72 -0
  23. package/packages/@monomind/cli/dist/src/browser/types.d.ts +1 -0
  24. package/packages/@monomind/cli/dist/src/browser/vitals.d.ts +15 -0
  25. package/packages/@monomind/cli/dist/src/browser/vitals.js +116 -0
  26. package/packages/@monomind/cli/dist/src/browser/wait.js +1 -1
  27. package/packages/@monomind/cli/dist/src/commands/browse.js +426 -23
  28. package/packages/@monomind/cli/package.json +1 -1
@@ -10,11 +10,12 @@ const fs = require('fs');
10
10
  const path = require('path');
11
11
  const os = require('os');
12
12
 
13
- const DATA_DIR = path.join(process.cwd(), '.monomind', 'data');
13
+ const _CWD = process.env.CLAUDE_PROJECT_DIR || process.cwd();
14
+ const DATA_DIR = path.join(_CWD, '.monomind', 'data');
14
15
  const STORE_PATH = path.join(DATA_DIR, 'auto-memory-store.json');
15
16
  const RANKED_PATH = path.join(DATA_DIR, 'ranked-context.json');
16
17
  const PENDING_PATH = path.join(DATA_DIR, 'pending-insights.jsonl');
17
- const SESSION_DIR = path.join(process.cwd(), '.monomind', 'sessions');
18
+ const SESSION_DIR = path.join(_CWD, '.monomind', 'sessions');
18
19
  const SESSION_FILE = path.join(SESSION_DIR, 'current.json');
19
20
 
20
21
  // ── Safety limits (fixes #1530, #1531) ─────────────────────────────────────
@@ -12,7 +12,8 @@ const platform = os.platform();
12
12
  const homeDir = os.homedir();
13
13
 
14
14
  function getDataDir() {
15
- const localDir = path.join(process.cwd(), '.monomind', 'sessions');
15
+ const baseDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
16
+ const localDir = path.join(baseDir, '.monomind', 'sessions');
16
17
  if (fs.existsSync(path.dirname(localDir))) {
17
18
  return localDir;
18
19
  }
@@ -0,0 +1,6 @@
1
+ {
2
+ "version": "1.0.0",
3
+ "generatedAt": "2026-05-17T17:12:24.935Z",
4
+ "totalAgents": 0,
5
+ "agents": []
6
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "monomind",
3
- "version": "1.10.33",
3
+ "version": "1.10.35",
4
4
  "description": "Monomind - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -4,6 +4,8 @@ import { tmpdir } from 'os';
4
4
  import { join } from 'path';
5
5
  import { CdpClient, fetchTargets, fetchNewTarget } from './cdp.js';
6
6
  import { CHROME_EXECUTABLES } from './types.js';
7
+ import { enableConsoleCapture } from './console-log.js';
8
+ import { setupDialogAutoHandling } from './dialog.js';
7
9
  const DEFAULT_PORT = 9222;
8
10
  const LAUNCH_TIMEOUT = 10_000;
9
11
  const POLL_INTERVAL = 200;
@@ -112,6 +114,9 @@ export async function connectToTarget(port, targetId) {
112
114
  client.send('DOM.enable', {}, sessionId),
113
115
  client.send('Accessibility.enable', {}, sessionId),
114
116
  ]);
117
+ // Wire up auto-capture listeners so console/errors/dialogs work immediately
118
+ enableConsoleCapture(client, sessionId);
119
+ setupDialogAutoHandling(client, sessionId);
115
120
  return { client, target, sessionId };
116
121
  }
117
122
  export async function openUrl(client, sessionId, url) {
@@ -122,7 +127,7 @@ export async function waitForLoad(client, sessionId, condition = 'load', timeout
122
127
  if (condition === 'load' || condition === 'domcontentloaded') {
123
128
  const event = condition === 'load' ? 'Page.loadEventFired' : 'Page.domContentEventFired';
124
129
  await Promise.race([
125
- client.once(event),
130
+ client.once(event, sessionId),
126
131
  sleep(timeout).then(() => { throw new Error(`Timeout waiting for ${condition}`); }),
127
132
  ]);
128
133
  return;
@@ -137,16 +142,23 @@ async function waitForNetworkIdle(client, sessionId, idleMs) {
137
142
  return new Promise((resolve) => {
138
143
  let pending = 0;
139
144
  let timer = null;
145
+ const settle = () => {
146
+ offReq();
147
+ offResp();
148
+ offFail();
149
+ resolve();
150
+ };
140
151
  const check = () => {
141
152
  if (pending === 0) {
142
153
  if (timer)
143
154
  clearTimeout(timer);
144
- timer = setTimeout(resolve, idleMs);
155
+ timer = setTimeout(settle, idleMs);
145
156
  }
146
157
  else {
147
- if (timer)
158
+ if (timer) {
148
159
  clearTimeout(timer);
149
- timer = null;
160
+ timer = null;
161
+ }
150
162
  }
151
163
  };
152
164
  const offReq = client.on('Network.requestWillBeSent', (_, sid) => {
@@ -168,14 +180,6 @@ async function waitForNetworkIdle(client, sessionId, idleMs) {
168
180
  }
169
181
  });
170
182
  check();
171
- // Cleanup after resolution
172
- const originalResolve = resolve;
173
- resolve = () => {
174
- offReq();
175
- offResp();
176
- offFail();
177
- originalResolve();
178
- };
179
183
  });
180
184
  }
181
185
  export async function getCurrentUrl(client, sessionId) {
@@ -8,7 +8,7 @@ export declare class CdpClient {
8
8
  connect(wsUrl: string): Promise<void>;
9
9
  send<T = Record<string, unknown>>(method: string, params?: Record<string, unknown>, sessionId?: string): Promise<T>;
10
10
  on(event: string, fn: (params: Record<string, unknown>, sessionId?: string) => void): () => void;
11
- once(event: string): Promise<Record<string, unknown>>;
11
+ once(event: string, sessionId?: string): Promise<Record<string, unknown>>;
12
12
  close(): void;
13
13
  isConnected(): boolean;
14
14
  }
@@ -74,9 +74,11 @@ export class CdpClient {
74
74
  this.eventListeners.get(event).add(fn);
75
75
  return () => this.eventListeners.get(event)?.delete(fn);
76
76
  }
77
- once(event) {
77
+ once(event, sessionId) {
78
78
  return new Promise((resolve) => {
79
- const off = this.on(event, (params) => {
79
+ const off = this.on(event, (params, sid) => {
80
+ if (sessionId !== undefined && sid !== sessionId)
81
+ return;
80
82
  off();
81
83
  resolve(params);
82
84
  });
@@ -12,6 +12,7 @@ export declare function findBySelector(client: CdpClient, sessionId: string, sel
12
12
  export declare function findByRole(client: CdpClient, sessionId: string, refs: Map<string, ElementRef>, role: string, options?: FindOptions): Promise<ElementRef | null>;
13
13
  export declare function findByText(client: CdpClient, sessionId: string, refs: Map<string, ElementRef>, text: string, options?: FindOptions): Promise<ElementRef | null>;
14
14
  export declare function findByLabel(client: CdpClient, sessionId: string, refs: Map<string, ElementRef>, label: string, options?: FindOptions): Promise<ElementRef | null>;
15
+ export declare function findByPlaceholder(client: CdpClient, sessionId: string, refs: Map<string, ElementRef>, placeholder: string, options?: FindOptions): Promise<ElementRef | null>;
15
16
  export declare function findByTestId(client: CdpClient, sessionId: string, testId: string): Promise<string | null>;
16
17
  export declare function isVisible(client: CdpClient, sessionId: string, ref: ElementRef): Promise<boolean>;
17
18
  export declare function isEnabled(client: CdpClient, sessionId: string, ref: ElementRef): Promise<boolean>;
@@ -43,6 +43,18 @@ export async function findByText(client, sessionId, refs, text, options = {}) {
43
43
  export async function findByLabel(client, sessionId, refs, label, options = {}) {
44
44
  return findByText(client, sessionId, refs, label, options);
45
45
  }
46
+ export async function findByPlaceholder(client, sessionId, refs, placeholder, options = {}) {
47
+ const lower = placeholder.toLowerCase();
48
+ const candidates = [...refs.values()].filter((r) => {
49
+ const desc = (r.description ?? '').toLowerCase();
50
+ return options.exact ? desc === lower : desc.includes(lower);
51
+ });
52
+ if (options.nth !== undefined)
53
+ return candidates[options.nth - 1] ?? null;
54
+ if (options.last)
55
+ return candidates[candidates.length - 1];
56
+ return candidates[0] ?? null;
57
+ }
46
58
  export async function findByTestId(client, sessionId, testId) {
47
59
  const selectors = [
48
60
  `[data-testid="${testId}"]`,
@@ -0,0 +1,26 @@
1
+ import type { CdpClient } from './cdp.js';
2
+ interface HarRequest {
3
+ id: string;
4
+ url: string;
5
+ method: string;
6
+ status: number;
7
+ statusText: string;
8
+ mimeType: string;
9
+ requestHeaders: Record<string, string>;
10
+ responseHeaders: Record<string, string>;
11
+ startTime: number;
12
+ endTime: number;
13
+ size: number;
14
+ encodedSize: number;
15
+ fromCache: boolean;
16
+ responseBody?: string;
17
+ }
18
+ export declare function startHarRecording(client: CdpClient, sessionId: string): Promise<void>;
19
+ export declare function stopHarRecording(client: CdpClient, sessionId: string, outputPath?: string, captureResponseBodies?: boolean): Promise<string>;
20
+ export declare function getHarStatus(sessionId: string): {
21
+ recording: boolean;
22
+ requestCount: number;
23
+ };
24
+ export declare function getRequests(sessionId: string): Partial<HarRequest>[];
25
+ export {};
26
+ //# sourceMappingURL=har.d.ts.map
@@ -0,0 +1,130 @@
1
+ import { writeFile } from 'fs/promises';
2
+ import { join } from 'path';
3
+ import { tmpdir } from 'os';
4
+ const _sessions = new Map();
5
+ export async function startHarRecording(client, sessionId) {
6
+ if (_sessions.has(sessionId))
7
+ throw new Error('HAR recording already in progress');
8
+ const requests = new Map();
9
+ const startTime = Date.now();
10
+ const offReq = client.on('Network.requestWillBeSent', (params, sid) => {
11
+ if (sid !== sessionId)
12
+ return;
13
+ const p = params;
14
+ requests.set(p.requestId, {
15
+ id: p.requestId,
16
+ url: p.request.url,
17
+ method: p.request.method,
18
+ requestHeaders: p.request.headers,
19
+ startTime: p.timestamp * 1000,
20
+ fromCache: false,
21
+ });
22
+ });
23
+ const offResp = client.on('Network.responseReceived', (params, sid) => {
24
+ if (sid !== sessionId)
25
+ return;
26
+ const p = params;
27
+ const entry = requests.get(p.requestId);
28
+ if (entry) {
29
+ entry.status = p.response.status;
30
+ entry.statusText = p.response.statusText;
31
+ entry.mimeType = p.response.mimeType;
32
+ entry.responseHeaders = p.response.headers;
33
+ entry.fromCache = p.response.fromDiskCache || p.response.fromServiceWorker;
34
+ entry.endTime = p.timestamp * 1000;
35
+ }
36
+ });
37
+ const offFinished = client.on('Network.loadingFinished', (params, sid) => {
38
+ if (sid !== sessionId)
39
+ return;
40
+ const p = params;
41
+ const entry = requests.get(p.requestId);
42
+ if (entry) {
43
+ entry.encodedSize = p.encodedDataLength;
44
+ entry.endTime = p.timestamp * 1000;
45
+ }
46
+ });
47
+ _sessions.set(sessionId, { requests, offReq, offResp, offFinished, startTime });
48
+ }
49
+ export async function stopHarRecording(client, sessionId, outputPath, captureResponseBodies = false) {
50
+ const state = _sessions.get(sessionId);
51
+ if (!state)
52
+ throw new Error('No active HAR recording for this session');
53
+ state.offReq();
54
+ state.offResp();
55
+ state.offFinished();
56
+ // Optionally fetch response bodies
57
+ if (captureResponseBodies) {
58
+ for (const [reqId, entry] of state.requests.entries()) {
59
+ try {
60
+ const body = await client.send('Network.getResponseBody', { requestId: reqId }, sessionId);
61
+ entry.responseBody = body.base64Encoded
62
+ ? Buffer.from(body.body, 'base64').toString('utf8')
63
+ : body.body;
64
+ entry.size = entry.responseBody.length;
65
+ }
66
+ catch {
67
+ // body may not be available for cached/redirected responses
68
+ }
69
+ }
70
+ }
71
+ _sessions.delete(sessionId);
72
+ const har = buildHar(Array.from(state.requests.values()), state.startTime);
73
+ const path = outputPath ?? join(tmpdir(), `monomind-har-${Date.now()}.har`);
74
+ await writeFile(path, JSON.stringify(har, null, 2));
75
+ return path;
76
+ }
77
+ export function getHarStatus(sessionId) {
78
+ const state = _sessions.get(sessionId);
79
+ return { recording: !!state, requestCount: state?.requests.size ?? 0 };
80
+ }
81
+ export function getRequests(sessionId) {
82
+ const state = _sessions.get(sessionId);
83
+ return state ? Array.from(state.requests.values()) : [];
84
+ }
85
+ function buildHar(entries, startTime) {
86
+ return {
87
+ log: {
88
+ version: '1.2',
89
+ creator: { name: 'monomind browse', version: '1.0.0' },
90
+ pages: [{
91
+ startedDateTime: new Date(startTime).toISOString(),
92
+ id: 'page_1',
93
+ title: '',
94
+ pageTimings: {},
95
+ }],
96
+ entries: entries.map((e) => ({
97
+ startedDateTime: new Date(e.startTime ?? startTime).toISOString(),
98
+ time: (e.endTime ?? e.startTime ?? startTime) - (e.startTime ?? startTime),
99
+ request: {
100
+ method: e.method ?? 'GET',
101
+ url: e.url ?? '',
102
+ httpVersion: 'HTTP/1.1',
103
+ cookies: [],
104
+ headers: Object.entries(e.requestHeaders ?? {}).map(([name, value]) => ({ name, value })),
105
+ queryString: [],
106
+ headersSize: -1,
107
+ bodySize: -1,
108
+ },
109
+ response: {
110
+ status: e.status ?? 0,
111
+ statusText: e.statusText ?? '',
112
+ httpVersion: 'HTTP/1.1',
113
+ cookies: [],
114
+ headers: Object.entries(e.responseHeaders ?? {}).map(([name, value]) => ({ name, value })),
115
+ content: {
116
+ size: e.size ?? -1,
117
+ mimeType: e.mimeType ?? 'application/octet-stream',
118
+ text: e.responseBody,
119
+ },
120
+ redirectURL: '',
121
+ headersSize: -1,
122
+ bodySize: e.encodedSize ?? -1,
123
+ },
124
+ cache: {},
125
+ timings: { send: 0, wait: 0, receive: 0 },
126
+ })),
127
+ },
128
+ };
129
+ }
130
+ //# sourceMappingURL=har.js.map
@@ -15,4 +15,9 @@ export * from './find.js';
15
15
  export * from './pdf.js';
16
16
  export * from './emulation.js';
17
17
  export * from './batch.js';
18
+ export * from './record.js';
19
+ export * from './trace.js';
20
+ export * from './profiler.js';
21
+ export * from './vitals.js';
22
+ export * from './har.js';
18
23
  //# sourceMappingURL=index.d.ts.map
@@ -15,4 +15,9 @@ export * from './find.js';
15
15
  export * from './pdf.js';
16
16
  export * from './emulation.js';
17
17
  export * from './batch.js';
18
+ export * from './record.js';
19
+ export * from './trace.js';
20
+ export * from './profiler.js';
21
+ export * from './vitals.js';
22
+ export * from './har.js';
18
23
  //# sourceMappingURL=index.js.map
@@ -6,6 +6,21 @@ export declare function clearCookies(client: CdpClient, sessionId: string): Prom
6
6
  export declare function setExtraHeaders(client: CdpClient, sessionId: string, headers: Record<string, string>): Promise<void>;
7
7
  export declare function enableInterception(client: CdpClient, sessionId: string): Promise<void>;
8
8
  export declare function setupRoutes(client: CdpClient, sessionId: string, routes: NetworkRoute[]): Promise<void>;
9
+ export declare function startRequestCapture(client: CdpClient, sessionId: string): void;
10
+ export declare function stopRequestCapture(sessionId: string): void;
11
+ export declare function getCapturedRequests(sessionId: string): {
12
+ url: string;
13
+ method: string;
14
+ status?: number;
15
+ mimeType?: string;
16
+ requestHeaders?: Record<string, string>;
17
+ responseHeaders?: Record<string, string>;
18
+ startTime: number;
19
+ endTime?: number;
20
+ encodedSize?: number;
21
+ }[];
22
+ export declare function clearCapturedRequests(sessionId: string): void;
23
+ export declare function disableInterception(client: CdpClient, sessionId: string): Promise<void>;
9
24
  export declare function getLocalStorage(client: CdpClient, sessionId: string): Promise<Record<string, string>>;
10
25
  export declare function setLocalStorage(client: CdpClient, sessionId: string, data: Record<string, string>): Promise<void>;
11
26
  //# sourceMappingURL=network.d.ts.map
@@ -50,6 +50,60 @@ export async function setupRoutes(client, sessionId, routes) {
50
50
  }
51
51
  });
52
52
  }
53
+ const _capturedRequests = new Map();
54
+ const _captureListeners = new Map();
55
+ export function startRequestCapture(client, sessionId) {
56
+ if (_capturedRequests.has(sessionId))
57
+ return;
58
+ const list = [];
59
+ _capturedRequests.set(sessionId, list);
60
+ const offReq = client.on('Network.requestWillBeSent', (params, sid) => {
61
+ if (sid !== sessionId)
62
+ return;
63
+ const p = params;
64
+ list.push({ url: p.request.url, method: p.request.method, requestHeaders: p.request.headers, startTime: p.timestamp * 1000 });
65
+ });
66
+ const offResp = client.on('Network.responseReceived', (params, sid) => {
67
+ if (sid !== sessionId)
68
+ return;
69
+ const p = params;
70
+ const entry = list.find((r) => r.url === p.response.url && !r.status);
71
+ if (entry) {
72
+ entry.status = p.response.status;
73
+ entry.mimeType = p.response.mimeType;
74
+ entry.responseHeaders = p.response.headers;
75
+ entry.endTime = p.timestamp * 1000;
76
+ }
77
+ });
78
+ const offFinished = client.on('Network.loadingFinished', (params, sid) => {
79
+ if (sid !== sessionId)
80
+ return;
81
+ const p = params;
82
+ const entry = list[list.length - 1];
83
+ if (entry) {
84
+ entry.encodedSize = p.encodedDataLength;
85
+ entry.endTime = p.timestamp * 1000;
86
+ }
87
+ });
88
+ _captureListeners.set(sessionId, [offReq, offResp, offFinished]);
89
+ }
90
+ export function stopRequestCapture(sessionId) {
91
+ const offs = _captureListeners.get(sessionId);
92
+ if (offs) {
93
+ for (const off of offs)
94
+ off();
95
+ _captureListeners.delete(sessionId);
96
+ }
97
+ }
98
+ export function getCapturedRequests(sessionId) {
99
+ return _capturedRequests.get(sessionId) ?? [];
100
+ }
101
+ export function clearCapturedRequests(sessionId) {
102
+ _capturedRequests.set(sessionId, []);
103
+ }
104
+ export async function disableInterception(client, sessionId) {
105
+ await client.send('Fetch.disable', {}, sessionId);
106
+ }
53
107
  export async function getLocalStorage(client, sessionId) {
54
108
  const result = await client.send('Runtime.evaluate', {
55
109
  expression: 'JSON.stringify(Object.fromEntries(Object.entries(localStorage)))',
@@ -0,0 +1,10 @@
1
+ import type { CdpClient } from './cdp.js';
2
+ export interface ProfilerOptions {
3
+ path?: string;
4
+ samplingInterval?: number;
5
+ }
6
+ export declare function startCpuProfile(client: CdpClient, sessionId: string, options?: ProfilerOptions): Promise<void>;
7
+ export declare function stopCpuProfile(client: CdpClient, sessionId: string, outputPath?: string): Promise<string>;
8
+ export declare function isProfilingActive(sessionId: string): boolean;
9
+ export declare function startHeapSnapshot(client: CdpClient, sessionId: string, outputPath?: string): Promise<string>;
10
+ //# sourceMappingURL=profiler.d.ts.map
@@ -0,0 +1,55 @@
1
+ import { writeFile } from 'fs/promises';
2
+ import { join } from 'path';
3
+ import { tmpdir } from 'os';
4
+ const _sessions = new Set();
5
+ export async function startCpuProfile(client, sessionId, options = {}) {
6
+ if (_sessions.has(sessionId)) {
7
+ throw new Error('CPU profiler already running for this session');
8
+ }
9
+ await client.send('Profiler.enable', {}, sessionId);
10
+ if (options.samplingInterval !== undefined) {
11
+ await client.send('Profiler.setSamplingInterval', { interval: options.samplingInterval }, sessionId);
12
+ }
13
+ await client.send('Profiler.start', {}, sessionId);
14
+ _sessions.add(sessionId);
15
+ }
16
+ export async function stopCpuProfile(client, sessionId, outputPath) {
17
+ if (!_sessions.has(sessionId)) {
18
+ throw new Error('No active CPU profiler for this session');
19
+ }
20
+ const result = await client.send('Profiler.stop', {}, sessionId);
21
+ await client.send('Profiler.disable', {}, sessionId);
22
+ _sessions.delete(sessionId);
23
+ const path = outputPath ?? join(tmpdir(), `monomind-profile-${Date.now()}.cpuprofile`);
24
+ await writeFile(path, JSON.stringify(result.profile));
25
+ return path;
26
+ }
27
+ export function isProfilingActive(sessionId) {
28
+ return _sessions.has(sessionId);
29
+ }
30
+ export async function startHeapSnapshot(client, sessionId, outputPath) {
31
+ await client.send('HeapProfiler.enable', {}, sessionId);
32
+ const chunks = [];
33
+ const off = client.on('HeapProfiler.addHeapSnapshotChunk', (params, sid) => {
34
+ if (sid !== sessionId)
35
+ return;
36
+ chunks.push(params.chunk);
37
+ });
38
+ await new Promise((resolve) => {
39
+ const off2 = client.on('HeapProfiler.reportHeapSnapshotProgress', (params, sid) => {
40
+ if (sid !== sessionId)
41
+ return;
42
+ if (params.finished) {
43
+ off2();
44
+ resolve();
45
+ }
46
+ });
47
+ client.send('HeapProfiler.takeHeapSnapshot', { reportProgress: true }, sessionId).catch(() => { });
48
+ });
49
+ off();
50
+ await client.send('HeapProfiler.disable', {}, sessionId);
51
+ const path = outputPath ?? join(tmpdir(), `monomind-heap-${Date.now()}.heapsnapshot`);
52
+ await writeFile(path, chunks.join(''));
53
+ return path;
54
+ }
55
+ //# sourceMappingURL=profiler.js.map
@@ -0,0 +1,21 @@
1
+ import type { CdpClient } from './cdp.js';
2
+ export interface RecordOptions {
3
+ path?: string;
4
+ format?: 'jpeg' | 'png' | 'webp';
5
+ quality?: number;
6
+ everyNthFrame?: number;
7
+ maxWidth?: number;
8
+ maxHeight?: number;
9
+ }
10
+ export interface RecordingState {
11
+ frames: string[];
12
+ offScreencast: (() => void) | null;
13
+ }
14
+ export declare function startRecording(client: CdpClient, sessionId: string, options?: RecordOptions): Promise<void>;
15
+ export declare function stopRecording(client: CdpClient, sessionId: string, outputPath?: string): Promise<string>;
16
+ export declare function getRecordingStatus(sessionId: string): {
17
+ recording: boolean;
18
+ frames: number;
19
+ };
20
+ export declare function saveFrameAsPng(client: CdpClient, sessionId: string, frameIndex: number, outputPath: string): Promise<void>;
21
+ //# sourceMappingURL=record.d.ts.map
@@ -0,0 +1,48 @@
1
+ import { writeFile } from 'fs/promises';
2
+ import { join } from 'path';
3
+ import { tmpdir } from 'os';
4
+ const _sessions = new Map();
5
+ export async function startRecording(client, sessionId, options = {}) {
6
+ if (_sessions.has(sessionId)) {
7
+ throw new Error('Recording already in progress for this session');
8
+ }
9
+ const state = { frames: [], offScreencast: null };
10
+ _sessions.set(sessionId, state);
11
+ state.offScreencast = client.on('Page.screencastFrame', async (params, sid) => {
12
+ if (sid !== sessionId)
13
+ return;
14
+ const { data, sessionId: frameSessionId } = params;
15
+ state.frames.push(data);
16
+ await client.send('Page.screencastFrameAck', { sessionId: frameSessionId }, sessionId).catch(() => { });
17
+ });
18
+ await client.send('Page.startScreencast', {
19
+ format: options.format ?? 'jpeg',
20
+ quality: options.quality ?? 80,
21
+ everyNthFrame: options.everyNthFrame ?? 1,
22
+ ...(options.maxWidth ? { maxWidth: options.maxWidth } : {}),
23
+ ...(options.maxHeight ? { maxHeight: options.maxHeight } : {}),
24
+ }, sessionId);
25
+ }
26
+ export async function stopRecording(client, sessionId, outputPath) {
27
+ const state = _sessions.get(sessionId);
28
+ if (!state)
29
+ throw new Error('No active recording for this session');
30
+ await client.send('Page.stopScreencast', {}, sessionId);
31
+ state.offScreencast?.();
32
+ _sessions.delete(sessionId);
33
+ const path = outputPath ?? join(tmpdir(), `monomind-screencast-${Date.now()}.frames.json`);
34
+ await writeFile(path, JSON.stringify({ frameCount: state.frames.length, frames: state.frames }));
35
+ return path;
36
+ }
37
+ export function getRecordingStatus(sessionId) {
38
+ const state = _sessions.get(sessionId);
39
+ return { recording: !!state, frames: state?.frames.length ?? 0 };
40
+ }
41
+ export async function saveFrameAsPng(client, sessionId, frameIndex, outputPath) {
42
+ const state = _sessions.get(sessionId);
43
+ if (!state || frameIndex >= state.frames.length) {
44
+ throw new Error(`Frame ${frameIndex} not available`);
45
+ }
46
+ await writeFile(outputPath, Buffer.from(state.frames[frameIndex], 'base64'));
47
+ }
48
+ //# sourceMappingURL=record.js.map
@@ -1,8 +1,27 @@
1
1
  import { INTERACTIVE_ROLES } from './types.js';
2
2
  import { getCurrentUrl, getCurrentTitle } from './browser.js';
3
3
  export async function captureSnapshot(client, sessionId, options = {}) {
4
- const { interactiveOnly = false, compact = false } = options;
5
- const { nodes } = await client.send('Accessibility.getFullAXTree', {}, sessionId);
4
+ const { interactiveOnly = false, compact = false, maxDepth, selector } = options;
5
+ // If a selector scope is requested, resolve it to a backendDOMNodeId and use getPartialAXTree
6
+ let nodes;
7
+ if (selector) {
8
+ const nodeResult = await client.send('Runtime.evaluate', { expression: `document.querySelector(${JSON.stringify(selector)})?.getAttribute('data-ax-node-id') ?? null`, returnByValue: true }, sessionId);
9
+ // Use DOM.querySelector to find the backend node
10
+ const doc = await client.send('DOM.getDocument', {}, sessionId);
11
+ const found = await client.send('DOM.querySelector', { nodeId: doc.root.nodeId, selector }, sessionId).catch(() => ({ nodeId: 0 }));
12
+ if (found.nodeId) {
13
+ const partial = await client.send('Accessibility.getPartialAXTree', { nodeId: found.nodeId, fetchRelatives: true }, sessionId).catch(async () => client.send('Accessibility.getFullAXTree', {}, sessionId));
14
+ nodes = partial.nodes;
15
+ }
16
+ else {
17
+ const full = await client.send('Accessibility.getFullAXTree', {}, sessionId);
18
+ nodes = full.nodes;
19
+ }
20
+ }
21
+ else {
22
+ const full = await client.send('Accessibility.getFullAXTree', {}, sessionId);
23
+ nodes = full.nodes;
24
+ }
6
25
  const url = await getCurrentUrl(client, sessionId);
7
26
  const title = await getCurrentTitle(client, sessionId);
8
27
  const refs = new Map();
@@ -14,6 +33,8 @@ export async function captureSnapshot(client, sessionId, options = {}) {
14
33
  const processNode = (node, depth) => {
15
34
  if (node.ignored)
16
35
  return;
36
+ if (maxDepth !== undefined && depth > maxDepth)
37
+ return;
17
38
  const role = node.role?.value ?? 'generic';
18
39
  const name = node.name?.value ?? '';
19
40
  const description = node.description?.value;
@@ -0,0 +1,10 @@
1
+ import type { CdpClient } from './cdp.js';
2
+ export interface TraceOptions {
3
+ path?: string;
4
+ categories?: string[];
5
+ screenshots?: boolean;
6
+ }
7
+ export declare function startTrace(client: CdpClient, sessionId: string, options?: TraceOptions): Promise<void>;
8
+ export declare function stopTrace(client: CdpClient, sessionId: string, outputPath?: string): Promise<string>;
9
+ export declare function getTraceStatus(sessionId: string): boolean;
10
+ //# sourceMappingURL=trace.d.ts.map