real-browser-mcp-server 1.4.5 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +6 -10
  2. package/dist/lib/cjs/index.js +2 -381
  3. package/dist/lib/cjs/index.js.map +1 -1
  4. package/dist/src/index.d.ts.map +1 -1
  5. package/dist/src/index.js +1 -20
  6. package/dist/src/index.js.map +1 -1
  7. package/dist/src/mcp/handlers/browser.d.ts.map +1 -1
  8. package/dist/src/mcp/handlers/browser.js +3 -2
  9. package/dist/src/mcp/handlers/browser.js.map +1 -1
  10. package/dist/src/mcp/handlers/dom.d.ts +7 -80
  11. package/dist/src/mcp/handlers/dom.d.ts.map +1 -1
  12. package/dist/src/mcp/handlers/dom.js +46 -190
  13. package/dist/src/mcp/handlers/dom.js.map +1 -1
  14. package/dist/src/mcp/handlers/extract.d.ts +47 -22
  15. package/dist/src/mcp/handlers/extract.d.ts.map +1 -1
  16. package/dist/src/mcp/handlers/extract.js +66 -269
  17. package/dist/src/mcp/handlers/extract.js.map +1 -1
  18. package/dist/src/mcp/handlers/helpers.d.ts +18 -3
  19. package/dist/src/mcp/handlers/helpers.d.ts.map +1 -1
  20. package/dist/src/mcp/handlers/helpers.js +56 -176
  21. package/dist/src/mcp/handlers/helpers.js.map +1 -1
  22. package/dist/src/mcp/handlers/index.d.ts.map +1 -1
  23. package/dist/src/mcp/handlers/index.js +1 -3
  24. package/dist/src/mcp/handlers/index.js.map +1 -1
  25. package/dist/src/mcp/handlers/media-handlers.d.ts +5 -6
  26. package/dist/src/mcp/handlers/media-handlers.d.ts.map +1 -1
  27. package/dist/src/mcp/handlers/media-handlers.js +1 -69
  28. package/dist/src/mcp/handlers/media-handlers.js.map +1 -1
  29. package/dist/src/mcp/handlers/network.d.ts +95 -12
  30. package/dist/src/mcp/handlers/network.d.ts.map +1 -1
  31. package/dist/src/mcp/handlers/network.js +178 -49
  32. package/dist/src/mcp/handlers/network.js.map +1 -1
  33. package/dist/src/mcp/handlers/utility-handlers.d.ts +36 -102
  34. package/dist/src/mcp/handlers/utility-handlers.d.ts.map +1 -1
  35. package/dist/src/mcp/handlers/utility-handlers.js +93 -177
  36. package/dist/src/mcp/handlers/utility-handlers.js.map +1 -1
  37. package/dist/src/mcp/handlers/vision.d.ts +143 -41
  38. package/dist/src/mcp/handlers/vision.d.ts.map +1 -1
  39. package/dist/src/mcp/handlers/vision.js +155 -88
  40. package/dist/src/mcp/handlers/vision.js.map +1 -1
  41. package/dist/src/mcp/index.d.ts.map +1 -1
  42. package/dist/src/mcp/index.js +2 -13
  43. package/dist/src/mcp/index.js.map +1 -1
  44. package/dist/src/mcp/server.d.ts.map +1 -1
  45. package/dist/src/mcp/server.js +1 -5
  46. package/dist/src/mcp/server.js.map +1 -1
  47. package/dist/src/shared/colors.d.ts +12 -0
  48. package/dist/src/shared/colors.d.ts.map +1 -0
  49. package/dist/src/shared/colors.js +15 -0
  50. package/dist/src/shared/colors.js.map +1 -0
  51. package/dist/src/shared/lib-core.d.ts +17 -0
  52. package/dist/src/shared/lib-core.d.ts.map +1 -0
  53. package/dist/src/shared/lib-core.js +421 -0
  54. package/dist/src/shared/lib-core.js.map +1 -0
  55. package/dist/src/shared/tools.d.ts.map +1 -1
  56. package/dist/src/shared/tools.js +90 -132
  57. package/dist/src/shared/tools.js.map +1 -1
  58. package/dist/test/cjs/test.js +61 -104
  59. package/dist/test/cjs/test.js.map +1 -1
  60. package/lib/esm/index.mjs +2 -396
  61. package/package.json +1 -1
  62. package/dist/src/mcp/handlers/form-handlers.d.ts +0 -9
  63. package/dist/src/mcp/handlers/form-handlers.d.ts.map +0 -1
  64. package/dist/src/mcp/handlers/form-handlers.js +0 -56
  65. package/dist/src/mcp/handlers/form-handlers.js.map +0 -1
  66. package/dist/src/mcp/tools.d.ts +0 -8
  67. package/dist/src/mcp/tools.d.ts.map +0 -1
  68. package/dist/src/mcp/tools.js +0 -19
  69. package/dist/src/mcp/tools.js.map +0 -1
@@ -22,6 +22,8 @@ export declare const networkHandlers: {
22
22
  error?: undefined;
23
23
  totalMessages?: undefined;
24
24
  websockets?: undefined;
25
+ graphql?: undefined;
26
+ har?: undefined;
25
27
  recording?: undefined;
26
28
  records?: undefined;
27
29
  } | {
@@ -35,6 +37,8 @@ export declare const networkHandlers: {
35
37
  error?: undefined;
36
38
  totalMessages?: undefined;
37
39
  websockets?: undefined;
40
+ graphql?: undefined;
41
+ har?: undefined;
38
42
  recording?: undefined;
39
43
  records?: undefined;
40
44
  } | {
@@ -58,6 +62,8 @@ export declare const networkHandlers: {
58
62
  error?: undefined;
59
63
  totalMessages?: undefined;
60
64
  websockets?: undefined;
65
+ graphql?: undefined;
66
+ har?: undefined;
61
67
  recording?: undefined;
62
68
  records?: undefined;
63
69
  } | {
@@ -71,6 +77,8 @@ export declare const networkHandlers: {
71
77
  error?: undefined;
72
78
  totalMessages?: undefined;
73
79
  websockets?: undefined;
80
+ graphql?: undefined;
81
+ har?: undefined;
74
82
  recording?: undefined;
75
83
  records?: undefined;
76
84
  } | {
@@ -84,6 +92,8 @@ export declare const networkHandlers: {
84
92
  note?: undefined;
85
93
  totalMessages?: undefined;
86
94
  websockets?: undefined;
95
+ graphql?: undefined;
96
+ har?: undefined;
87
97
  recording?: undefined;
88
98
  records?: undefined;
89
99
  } | {
@@ -97,6 +107,8 @@ export declare const networkHandlers: {
97
107
  apiCalls?: undefined;
98
108
  interceptedApis?: undefined;
99
109
  error?: undefined;
110
+ graphql?: undefined;
111
+ har?: undefined;
100
112
  recording?: undefined;
101
113
  records?: undefined;
102
114
  } | {
@@ -110,13 +122,22 @@ export declare const networkHandlers: {
110
122
  interceptedApis?: undefined;
111
123
  note?: undefined;
112
124
  totalMessages?: undefined;
125
+ graphql?: undefined;
126
+ har?: undefined;
113
127
  recording?: undefined;
114
128
  records?: undefined;
115
129
  } | {
116
130
  success: boolean;
117
- recording: boolean;
118
131
  count: number;
119
- records: import("../../types").NetworkRecord[];
132
+ graphql: {
133
+ url: any;
134
+ method: any;
135
+ operationName: any;
136
+ query: any;
137
+ variables: any;
138
+ response: any;
139
+ timestamp: any;
140
+ }[];
120
141
  mediaUrls?: undefined;
121
142
  navigations?: undefined;
122
143
  apiCalls?: undefined;
@@ -125,23 +146,85 @@ export declare const networkHandlers: {
125
146
  error?: undefined;
126
147
  totalMessages?: undefined;
127
148
  websockets?: undefined;
128
- }>;
129
- cookie_manager(params?: any): Promise<{
149
+ har?: undefined;
150
+ recording?: undefined;
151
+ records?: undefined;
152
+ } | {
130
153
  success: boolean;
131
- cookies: import("patchright-core").Cookie[];
132
- message?: undefined;
154
+ count: number;
155
+ har: {
156
+ log: {
157
+ version: string;
158
+ creator: {
159
+ name: string;
160
+ version: string;
161
+ };
162
+ entries: {
163
+ startedDateTime: string;
164
+ request: {
165
+ method: any;
166
+ url: any;
167
+ headers: {
168
+ name: string;
169
+ value: string;
170
+ }[];
171
+ postData: {
172
+ text: any;
173
+ } | undefined;
174
+ };
175
+ response: {
176
+ status: any;
177
+ content: {
178
+ mimeType: any;
179
+ text: any;
180
+ };
181
+ };
182
+ time: number;
183
+ }[];
184
+ };
185
+ };
186
+ mediaUrls?: undefined;
187
+ navigations?: undefined;
188
+ apiCalls?: undefined;
189
+ interceptedApis?: undefined;
190
+ note?: undefined;
133
191
  error?: undefined;
192
+ totalMessages?: undefined;
193
+ websockets?: undefined;
194
+ graphql?: undefined;
195
+ recording?: undefined;
196
+ records?: undefined;
134
197
  } | {
135
198
  success: boolean;
136
- message: string;
137
- cookies?: undefined;
199
+ recording: boolean;
200
+ count: number;
201
+ records: import("../../types").NetworkRecord[];
202
+ mediaUrls?: undefined;
203
+ navigations?: undefined;
204
+ apiCalls?: undefined;
205
+ interceptedApis?: undefined;
206
+ note?: undefined;
207
+ error?: undefined;
208
+ totalMessages?: undefined;
209
+ websockets?: undefined;
210
+ graphql?: undefined;
211
+ har?: undefined;
212
+ }>;
213
+ extract_data(params?: any): Promise<any>;
214
+ replay_request(params: any): Promise<{
215
+ success: boolean;
216
+ result: {
217
+ status: number;
218
+ headers: {
219
+ [k: string]: string;
220
+ };
221
+ body: string | null;
222
+ };
138
223
  error?: undefined;
139
224
  } | {
140
225
  success: boolean;
141
- error: string;
142
- cookies?: undefined;
143
- message?: undefined;
226
+ error: any;
227
+ result?: undefined;
144
228
  }>;
145
- extract_data(params?: any): Promise<any>;
146
229
  };
147
230
  //# sourceMappingURL=network.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../../../src/mcp/handlers/network.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,eAAe;4BACI,GAAG;;;;;;;;;8BAiHF,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAqTL,GAAG;;;;;;;;;;;;;;;;0BAwDL,GAAG;CAwqB/B,CAAC"}
1
+ {"version":3,"file":"network.d.ts","sourceRoot":"","sources":["../../../../src/mcp/handlers/network.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,eAAe;4BACI,GAAG;;;;;;;;;8BAiHF,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BA4WP,GAAG;2BA6wBD,GAAG;;;;;;;;;;;;;;;CAuBjC,CAAC"}
@@ -401,6 +401,57 @@ exports.networkHandlers = {
401
401
  return { success: false, error: 'Failed to retrieve WebSocket data: ' + e.message, websockets: [] };
402
402
  }
403
403
  }
404
+ // ====== FEATURE 4: GraphQL Inspector ======
405
+ case 'get_graphql': {
406
+ const gqlRecords = state_1.state.networkRecords.filter((r) => r.isApiCall && r.requestBody && (r.requestBody.includes('"query"') || r.requestBody.includes('query '))).map((r) => {
407
+ let parsedQuery = null, parsedVariables = null, operationName = null;
408
+ try {
409
+ const body = JSON.parse(r.requestBody);
410
+ parsedQuery = body.query;
411
+ parsedVariables = body.variables;
412
+ operationName = body.operationName;
413
+ }
414
+ catch (e) { }
415
+ return {
416
+ url: r.url,
417
+ method: r.method,
418
+ operationName: operationName || 'unknown',
419
+ query: parsedQuery || r.requestBody,
420
+ variables: parsedVariables,
421
+ response: r.responseJson || r.responseBody,
422
+ timestamp: r.timestamp
423
+ };
424
+ });
425
+ return { success: true, count: gqlRecords.length, graphql: gqlRecords };
426
+ }
427
+ // ====== FEATURE 5: HAR Exporter ======
428
+ case 'export_har': {
429
+ // ponytail: minimal HAR 1.2 mapping without dependencies
430
+ const har = {
431
+ log: {
432
+ version: '1.2',
433
+ creator: { name: 'Real Browser MCP', version: '1.0' },
434
+ entries: state_1.state.networkRecords.filter((r) => r.type === 'response').map((r) => ({
435
+ startedDateTime: new Date(r.timestamp).toISOString(),
436
+ request: {
437
+ method: r.method || 'GET',
438
+ url: r.url,
439
+ headers: Object.entries(r.headers || {}).map(([name, value]) => ({ name, value: String(value) })),
440
+ postData: r.requestBody ? { text: r.requestBody } : undefined
441
+ },
442
+ response: {
443
+ status: r.status || 200,
444
+ content: {
445
+ mimeType: r.contentType || 'text/plain',
446
+ text: r.responseBody || ''
447
+ }
448
+ },
449
+ time: 0
450
+ }))
451
+ }
452
+ };
453
+ return { success: true, count: har.log.entries.length, har };
454
+ }
404
455
  }
405
456
  let records = state_1.state.networkRecords;
406
457
  if (filter.resourceType) {
@@ -418,54 +469,6 @@ exports.networkHandlers = {
418
469
  }
419
470
  return { success: true, recording: state_1.state.isRecordingNetwork, count: records.length, records: records.slice(-200) };
420
471
  },
421
- async cookie_manager(params = {}) {
422
- const { page } = (0, state_1.requireBrowser)();
423
- const { action = 'get', name, value, domain, expires } = params;
424
- (0, state_1.notifyProgress)('cookie_manager', 'started', `Cookie action: ${action}`);
425
- // Playwright/Patchright: cookies are managed via the BrowserContext, not the page
426
- const context = page.context();
427
- switch (action) {
428
- case 'get': {
429
- const cookies = await context.cookies();
430
- (0, state_1.notifyProgress)('cookie_manager', 'completed', `Retrieved ${cookies.length} cookies`);
431
- return { success: true, cookies: name ? cookies.filter(c => c.name === name) : cookies };
432
- }
433
- case 'set': {
434
- await context.addCookies([{
435
- name,
436
- value,
437
- domain: domain || new URL(page.url()).hostname,
438
- path: '/',
439
- ...(expires ? { expires } : {})
440
- }]);
441
- (0, state_1.notifyProgress)('cookie_manager', 'completed', `Cookie set: ${name}`);
442
- return { success: true, message: `Cookie ${name} set` };
443
- }
444
- case 'delete': {
445
- // Playwright has no per-cookie delete: clear all, then re-add the ones we keep
446
- const toDelete = await context.cookies();
447
- const remaining = name ? toDelete.filter(c => c.name !== name) : [];
448
- const removedCount = toDelete.length - remaining.length;
449
- await context.clearCookies();
450
- if (remaining.length) {
451
- await context.addCookies(remaining.map(c => ({
452
- name: c.name, value: c.value, domain: c.domain, path: c.path,
453
- ...(c.expires && c.expires > 0 ? { expires: c.expires } : {}),
454
- httpOnly: c.httpOnly, secure: c.secure, sameSite: c.sameSite
455
- })));
456
- }
457
- (0, state_1.notifyProgress)('cookie_manager', 'completed', `Deleted ${removedCount} cookie(s)`);
458
- return { success: true, message: `Deleted ${removedCount} cookie(s)` };
459
- }
460
- case 'clear': {
461
- const allCookies = await context.cookies();
462
- await context.clearCookies();
463
- (0, state_1.notifyProgress)('cookie_manager', 'completed', `Cleared ${allCookies.length} cookies`);
464
- return { success: true, message: `Cleared ${allCookies.length} cookies` };
465
- }
466
- }
467
- return { success: false, error: 'Invalid action' };
468
- },
469
472
  async extract_data(params = {}) {
470
473
  const { page } = (0, state_1.requireBrowser)();
471
474
  const { type = 'auto', pattern, selector, jsonPath, source = 'all', autoDecode = true, flags = 'gi', types = ['all'], includeTitle = true, includeCanonical = true, maxMatches = 100, maxJsonObjects = 50, waitForSelector = false, selectorTimeout = 10000 } = params;
@@ -699,8 +702,112 @@ exports.networkHandlers = {
699
702
  }
700
703
  return autoResults;
701
704
  };
705
+ // Helper: Extract Links (merged from link_harvester)
706
+ const extractLinks = async () => {
707
+ const { includeHidden = true, searchIframes = false } = params;
708
+ const doExtract = async (context) => {
709
+ return await context.evaluate(({ incHidden }) => {
710
+ const allLinks = [];
711
+ const seenUrls = new Set();
712
+ const addLink = (href, text, source, element) => {
713
+ if (!href || seenUrls.has(href))
714
+ return;
715
+ if (!href.startsWith('http') && !href.startsWith('//'))
716
+ return;
717
+ if (href.startsWith('//'))
718
+ href = window.location.protocol + href;
719
+ seenUrls.add(href);
720
+ allLinks.push({
721
+ href,
722
+ text: (text || '').trim().substring(0, 100),
723
+ source,
724
+ hidden: element ? (element.offsetParent === null || getComputedStyle(element).display === 'none' || getComputedStyle(element).visibility === 'hidden') : false
725
+ });
726
+ };
727
+ document.querySelectorAll('a[href]').forEach(a => addLink(a.href, a.textContent, 'anchor', a));
728
+ const dataAttrs = ['data-href', 'data-url', 'data-link', 'data-src', 'data-file', 'data-download'];
729
+ dataAttrs.forEach(attr => document.querySelectorAll(`[${attr}]`).forEach(el => addLink(el.getAttribute(attr), el.textContent, `${attr}`, el)));
730
+ if (incHidden) {
731
+ document.querySelectorAll('[onclick]').forEach(el => {
732
+ const onclick = el.getAttribute('onclick');
733
+ if (!onclick)
734
+ return;
735
+ const urlMatches = onclick.match(/https?:\/\/[^\s"'<>]+/gi) || [];
736
+ urlMatches.forEach(url => addLink(url, el.textContent, 'onclick', el));
737
+ const hrefMatch = onclick.match(/location\.href\s*=\s*['"]([^'"]+)['"]/);
738
+ if (hrefMatch)
739
+ addLink(hrefMatch[1], el.textContent, 'onclick-location', el);
740
+ const openMatch = onclick.match(/window\.open\s*\(\s*['"]([^'"]+)['"]/);
741
+ if (openMatch)
742
+ addLink(openMatch[1], el.textContent, 'onclick-window-open', el);
743
+ });
744
+ const scripts = [...document.querySelectorAll('script')].slice(0, 20);
745
+ scripts.forEach(script => {
746
+ const content = script.textContent || '';
747
+ const patterns = [
748
+ /["']?(https?:\/\/[^"'\s<>]+\.(mp4|mkv|avi|m3u8|mpd|zip|rar|pdf))[^"'\s<>]*["']?/gi,
749
+ /download[_-]?url\s*[:=]\s*["']([^"']+)["']/gi,
750
+ /file\s*[:=]\s*["']([^"']+)["']/gi
751
+ ];
752
+ patterns.forEach(pattern => {
753
+ let match;
754
+ while ((match = pattern.exec(content)) !== null)
755
+ addLink(match[1], 'script-extracted', 'script', null);
756
+ });
757
+ });
758
+ }
759
+ document.querySelectorAll('a[href^="javascript:"]').forEach(a => {
760
+ const match = a.getAttribute('href')?.match(/https?:\/\/[^\s"'<>]+/gi);
761
+ if (match)
762
+ match.forEach(url => addLink(url, a.textContent, 'javascript-href', a));
763
+ });
764
+ document.querySelectorAll('input[type="hidden"]').forEach((input) => {
765
+ if (input.value && (input.value.startsWith('http') || input.value.startsWith('//')))
766
+ addLink(input.value, input.name || input.id, 'hidden-input', input);
767
+ });
768
+ const metaRefresh = document.querySelector('meta[http-equiv="refresh"]');
769
+ if (metaRefresh) {
770
+ const match = metaRefresh.getAttribute('content')?.match(/url=(.+)/i);
771
+ if (match)
772
+ addLink(match[1].trim().replace(/['"]/g, ''), 'meta-refresh', 'meta', null);
773
+ }
774
+ document.querySelectorAll('iframe[src]').forEach((iframe) => addLink(iframe.src, 'iframe', 'iframe', iframe));
775
+ return allLinks;
776
+ }, { incHidden: includeHidden }).catch(() => []);
777
+ };
778
+ let links = await doExtract(page);
779
+ if (searchIframes) {
780
+ const frames = page.frames();
781
+ for (let i = 1; i < frames.length && i < 5; i++) {
782
+ try {
783
+ const frame = frames[i];
784
+ if (frame.url() && frame.url() !== 'about:blank') {
785
+ const frameLinks = await doExtract(frame);
786
+ frameLinks.forEach((link) => link.source = `iframe:${link.source}`);
787
+ links = [...links, ...frameLinks];
788
+ }
789
+ }
790
+ catch (e) { }
791
+ }
792
+ }
793
+ if (!includeHidden)
794
+ links = links.filter((link) => !link.hidden);
795
+ const seen = new Set();
796
+ return links.filter((link) => {
797
+ if (seen.has(link.href))
798
+ return false;
799
+ seen.add(link.href);
800
+ return true;
801
+ });
802
+ };
702
803
  // Main switch based on type
703
804
  switch (type) {
805
+ case 'links': {
806
+ const links = await extractLinks();
807
+ results.extracted = { count: links.length, links };
808
+ (0, state_1.notifyProgress)('extract_data', 'completed', `Links: ${links.length} extracted`);
809
+ break;
810
+ }
704
811
  case 'regex': {
705
812
  if (!pattern) {
706
813
  return { success: false, error: 'Pattern is required for regex extraction' };
@@ -728,7 +835,7 @@ exports.networkHandlers = {
728
835
  }
729
836
  case 'structured': {
730
837
  if (!selector) {
731
- return { success: false, error: 'Selector is required for structured extraction' };
838
+ return { success: false, error: 'Selector is required for structured extraction. 💡 AI HINT: Run see_page(annotate: true) first to discover valid selectors or annotation IDs.' };
732
839
  }
733
840
  results.extracted = await extractStructured(selector, waitForSelector, selectorTimeout);
734
841
  if (results.extracted.error) {
@@ -1143,6 +1250,28 @@ exports.networkHandlers = {
1143
1250
  return { success: false, error: `Unknown type: ${type}. Supported: regex, json, meta, structured, auto, deobfuscate, apiDiscovery, decrypt` };
1144
1251
  }
1145
1252
  return results;
1253
+ },
1254
+ async replay_request(params) {
1255
+ const { page } = (0, state_1.requireBrowser)();
1256
+ const { url, method = 'GET', headers, body } = params;
1257
+ (0, state_1.notifyProgress)('replay_request', 'started', `Replaying ${method} to ${url}`);
1258
+ // ponytail: reuse page.evaluate to fire native fetch, bypasses CORS & uses exact browser context auth/cookies
1259
+ try {
1260
+ const result = await page.evaluate(async ({ u, m, h, b }) => {
1261
+ const res = await fetch(u, { method: m, headers: h, body: b });
1262
+ return {
1263
+ status: res.status,
1264
+ headers: Object.fromEntries(res.headers.entries()),
1265
+ body: await res.text().catch(() => null)
1266
+ };
1267
+ }, { u: url, m: method, h: headers || {}, b: body });
1268
+ (0, state_1.notifyProgress)('replay_request', 'completed', `Replay finished with status ${result.status}`);
1269
+ return { success: true, result };
1270
+ }
1271
+ catch (e) {
1272
+ (0, state_1.notifyProgress)('replay_request', 'error', `Replay failed: ${e.message}`);
1273
+ return { success: false, error: e.message };
1274
+ }
1146
1275
  }
1147
1276
  };
1148
1277
  //# sourceMappingURL=network.js.map