cbrowser 14.2.2 → 14.2.3

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.
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;GAWG;AAkVH,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAmwEpD"}
1
+ {"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../src/mcp-server.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;GAWG;AAkYH,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAqxEpD"}
@@ -49,6 +49,44 @@ async function getBrowser() {
49
49
  }
50
50
  return browser;
51
51
  }
52
+ /**
53
+ * v14.2.1: Retry wrapper for transient browser errors.
54
+ * Retries operations that fail with common transient error patterns.
55
+ */
56
+ async function withRetry(operation, options = {}) {
57
+ const maxRetries = options.maxRetries ?? 2;
58
+ const retryDelay = options.retryDelay ?? 500;
59
+ let lastError = null;
60
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
61
+ try {
62
+ return await operation();
63
+ }
64
+ catch (e) {
65
+ lastError = e;
66
+ const errorMessage = lastError.message || "";
67
+ // Check if this is a transient error worth retrying
68
+ const isTransient = errorMessage.includes("Target closed") ||
69
+ errorMessage.includes("Execution context") ||
70
+ errorMessage.includes("Session closed") ||
71
+ errorMessage.includes("Connection refused") ||
72
+ errorMessage.includes("Browser disconnected");
73
+ if (!isTransient || attempt === maxRetries) {
74
+ throw lastError;
75
+ }
76
+ // Wait before retry with exponential backoff
77
+ await new Promise((r) => setTimeout(r, retryDelay * (attempt + 1)));
78
+ // Try to recover the browser before retrying
79
+ try {
80
+ const b = await getBrowser();
81
+ await b.recoverBrowser();
82
+ }
83
+ catch {
84
+ // Recovery failed, will retry operation anyway
85
+ }
86
+ }
87
+ }
88
+ throw lastError;
89
+ }
52
90
  // Session storage (in-memory, cleared when server restarts)
53
91
  const comparisonSessions = new Map();
54
92
  // WCAG criteria reference for barrier mapping
@@ -230,22 +268,25 @@ export async function startMcpServer() {
230
268
  server.tool("navigate", "Navigate to a URL and take a screenshot", {
231
269
  url: z.string().url().describe("The URL to navigate to"),
232
270
  }, async ({ url }) => {
233
- const b = await getBrowser();
234
- const result = await b.navigate(url);
235
- return {
236
- content: [
237
- {
238
- type: "text",
239
- text: JSON.stringify({
240
- success: true,
241
- url: result.url,
242
- title: result.title,
243
- loadTime: result.loadTime,
244
- screenshot: result.screenshot,
245
- }, null, 2),
246
- },
247
- ],
248
- };
271
+ // v14.2.1: Wrap with retry for transient errors
272
+ return await withRetry(async () => {
273
+ const b = await getBrowser();
274
+ const result = await b.navigate(url);
275
+ return {
276
+ content: [
277
+ {
278
+ type: "text",
279
+ text: JSON.stringify({
280
+ success: true,
281
+ url: result.url,
282
+ title: result.title,
283
+ loadTime: result.loadTime,
284
+ screenshot: result.screenshot,
285
+ }, null, 2),
286
+ },
287
+ ],
288
+ };
289
+ });
249
290
  });
250
291
  // =========================================================================
251
292
  // Interaction Tools
@@ -255,24 +296,27 @@ export async function startMcpServer() {
255
296
  force: z.boolean().optional().describe("Bypass safety checks for destructive actions"),
256
297
  verbose: z.boolean().optional().describe("Return available elements and AI suggestions on failure"),
257
298
  }, async ({ selector, force, verbose }) => {
258
- const b = await getBrowser();
259
- const result = await b.click(selector, { force, verbose });
260
- const response = {
261
- success: result.success,
262
- message: result.message,
263
- screenshot: result.screenshot,
264
- };
265
- if (verbose && !result.success) {
266
- if (result.availableElements)
267
- response.availableElements = result.availableElements;
268
- if (result.aiSuggestion)
269
- response.aiSuggestion = result.aiSuggestion;
270
- if (result.debugScreenshot)
271
- response.debugScreenshot = result.debugScreenshot;
272
- }
273
- return {
274
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
275
- };
299
+ // v14.2.1: Wrap with retry for transient errors
300
+ return await withRetry(async () => {
301
+ const b = await getBrowser();
302
+ const result = await b.click(selector, { force, verbose });
303
+ const response = {
304
+ success: result.success,
305
+ message: result.message,
306
+ screenshot: result.screenshot,
307
+ };
308
+ if (verbose && !result.success) {
309
+ if (result.availableElements)
310
+ response.availableElements = result.availableElements;
311
+ if (result.aiSuggestion)
312
+ response.aiSuggestion = result.aiSuggestion;
313
+ if (result.debugScreenshot)
314
+ response.debugScreenshot = result.debugScreenshot;
315
+ }
316
+ return {
317
+ content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
318
+ };
319
+ });
276
320
  });
277
321
  server.tool("smart_click", "Click with auto-retry and self-healing selectors. v11.8.0: Added confidence gating - only reports success if healed selector has >= 60% confidence.", {
278
322
  selector: z.string().describe("Element to click"),
@@ -326,23 +370,26 @@ export async function startMcpServer() {
326
370
  value: z.string().describe("Value to enter"),
327
371
  verbose: z.boolean().optional().describe("Return available inputs and AI suggestions on failure"),
328
372
  }, async ({ selector, value, verbose }) => {
329
- const b = await getBrowser();
330
- const result = await b.fill(selector, value, { verbose });
331
- const response = {
332
- success: result.success,
333
- message: result.message,
334
- };
335
- if (verbose && !result.success) {
336
- if (result.availableInputs)
337
- response.availableInputs = result.availableInputs;
338
- if (result.aiSuggestion)
339
- response.aiSuggestion = result.aiSuggestion;
340
- if (result.debugScreenshot)
341
- response.debugScreenshot = result.debugScreenshot;
342
- }
343
- return {
344
- content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
345
- };
373
+ // v14.2.1: Wrap with retry for transient errors
374
+ return await withRetry(async () => {
375
+ const b = await getBrowser();
376
+ const result = await b.fill(selector, value, { verbose });
377
+ const response = {
378
+ success: result.success,
379
+ message: result.message,
380
+ };
381
+ if (verbose && !result.success) {
382
+ if (result.availableInputs)
383
+ response.availableInputs = result.availableInputs;
384
+ if (result.aiSuggestion)
385
+ response.aiSuggestion = result.aiSuggestion;
386
+ if (result.debugScreenshot)
387
+ response.debugScreenshot = result.debugScreenshot;
388
+ }
389
+ return {
390
+ content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
391
+ };
392
+ });
346
393
  });
347
394
  // =========================================================================
348
395
  // Extraction Tools
@@ -350,30 +397,36 @@ export async function startMcpServer() {
350
397
  server.tool("screenshot", "Take a screenshot of the current page", {
351
398
  path: z.string().optional().describe("Optional path to save the screenshot"),
352
399
  }, async ({ path }) => {
353
- const b = await getBrowser();
354
- const file = await b.screenshot(path);
355
- return {
356
- content: [
357
- {
358
- type: "text",
359
- text: JSON.stringify({ screenshot: file }, null, 2),
360
- },
361
- ],
362
- };
400
+ // v14.2.1: Wrap with retry for transient errors
401
+ return await withRetry(async () => {
402
+ const b = await getBrowser();
403
+ const file = await b.screenshot(path);
404
+ return {
405
+ content: [
406
+ {
407
+ type: "text",
408
+ text: JSON.stringify({ screenshot: file }, null, 2),
409
+ },
410
+ ],
411
+ };
412
+ });
363
413
  });
364
414
  server.tool("extract", "Extract data from the page", {
365
415
  what: z.enum(["links", "headings", "forms", "images", "text"]).describe("What to extract"),
366
416
  }, async ({ what }) => {
367
- const b = await getBrowser();
368
- const result = await b.extract(what);
369
- return {
370
- content: [
371
- {
372
- type: "text",
373
- text: JSON.stringify(result.data, null, 2),
374
- },
375
- ],
376
- };
417
+ // v14.2.1: Wrap with retry for transient errors
418
+ return await withRetry(async () => {
419
+ const b = await getBrowser();
420
+ const result = await b.extract(what);
421
+ return {
422
+ content: [
423
+ {
424
+ type: "text",
425
+ text: JSON.stringify(result.data, null, 2),
426
+ },
427
+ ],
428
+ };
429
+ });
377
430
  });
378
431
  // =========================================================================
379
432
  // Assertion Tools
@@ -503,16 +556,19 @@ export async function startMcpServer() {
503
556
  // Self-Healing Tools
504
557
  // =========================================================================
505
558
  server.tool("heal_stats", "Get self-healing selector cache statistics", {}, async () => {
506
- const b = await getBrowser();
507
- const stats = b.getSelectorCacheStats();
508
- return {
509
- content: [
510
- {
511
- type: "text",
512
- text: JSON.stringify(stats, null, 2),
513
- },
514
- ],
515
- };
559
+ // v14.2.1: Wrap with retry for transient errors
560
+ return await withRetry(async () => {
561
+ const b = await getBrowser();
562
+ const stats = b.getSelectorCacheStats();
563
+ return {
564
+ content: [
565
+ {
566
+ type: "text",
567
+ text: JSON.stringify(stats, null, 2),
568
+ },
569
+ ],
570
+ };
571
+ });
516
572
  });
517
573
  // =========================================================================
518
574
  // Visual Testing Tools (v7.0.0+)