cbrowser 18.73.1 → 18.73.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 (112) hide show
  1. package/dist/analysis/accessibility-empathy.d.ts.map +1 -1
  2. package/dist/analysis/accessibility-empathy.js +88 -4
  3. package/dist/analysis/accessibility-empathy.js.map +1 -1
  4. package/dist/analysis/agent-ready-audit.d.ts.map +1 -1
  5. package/dist/analysis/agent-ready-audit.js +66 -5
  6. package/dist/analysis/agent-ready-audit.js.map +1 -1
  7. package/dist/analysis/page-understanding.d.ts.map +1 -1
  8. package/dist/analysis/page-understanding.js +77 -12
  9. package/dist/analysis/page-understanding.js.map +1 -1
  10. package/dist/analysis/webmcp-readiness.d.ts.map +1 -1
  11. package/dist/analysis/webmcp-readiness.js +18 -7
  12. package/dist/analysis/webmcp-readiness.js.map +1 -1
  13. package/dist/browser/assertion-value.d.ts +54 -0
  14. package/dist/browser/assertion-value.d.ts.map +1 -0
  15. package/dist/browser/assertion-value.js +90 -0
  16. package/dist/browser/assertion-value.js.map +1 -0
  17. package/dist/browser.d.ts +8 -0
  18. package/dist/browser.d.ts.map +1 -1
  19. package/dist/browser.js +106 -27
  20. package/dist/browser.js.map +1 -1
  21. package/dist/cli-args.d.ts +33 -0
  22. package/dist/cli-args.d.ts.map +1 -0
  23. package/dist/cli-args.js +54 -0
  24. package/dist/cli-args.js.map +1 -0
  25. package/dist/cli.js +14 -22
  26. package/dist/cli.js.map +1 -1
  27. package/dist/config.d.ts.map +1 -1
  28. package/dist/config.js +28 -1
  29. package/dist/config.js.map +1 -1
  30. package/dist/mcp-server-remote.d.ts +17 -1
  31. package/dist/mcp-server-remote.d.ts.map +1 -1
  32. package/dist/mcp-server-remote.js +22 -3
  33. package/dist/mcp-server-remote.js.map +1 -1
  34. package/dist/mcp-server.d.ts.map +1 -1
  35. package/dist/mcp-server.js +20 -0
  36. package/dist/mcp-server.js.map +1 -1
  37. package/dist/mcp-tools/base/analysis-tools.d.ts +1 -1
  38. package/dist/mcp-tools/base/analysis-tools.d.ts.map +1 -1
  39. package/dist/mcp-tools/base/analysis-tools.js +83 -10
  40. package/dist/mcp-tools/base/analysis-tools.js.map +1 -1
  41. package/dist/mcp-tools/base/assertion-tools.d.ts +1 -1
  42. package/dist/mcp-tools/base/assertion-tools.d.ts.map +1 -1
  43. package/dist/mcp-tools/base/assertion-tools.js +26 -3
  44. package/dist/mcp-tools/base/assertion-tools.js.map +1 -1
  45. package/dist/mcp-tools/base/audit-tools.d.ts.map +1 -1
  46. package/dist/mcp-tools/base/audit-tools.js +85 -8
  47. package/dist/mcp-tools/base/audit-tools.js.map +1 -1
  48. package/dist/mcp-tools/base/browser-management-tools.d.ts +1 -1
  49. package/dist/mcp-tools/base/browser-management-tools.d.ts.map +1 -1
  50. package/dist/mcp-tools/base/browser-management-tools.js +66 -15
  51. package/dist/mcp-tools/base/browser-management-tools.js.map +1 -1
  52. package/dist/mcp-tools/base/browser-state-tools.d.ts.map +1 -1
  53. package/dist/mcp-tools/base/browser-state-tools.js +6 -1
  54. package/dist/mcp-tools/base/browser-state-tools.js.map +1 -1
  55. package/dist/mcp-tools/base/cognitive-tools.d.ts.map +1 -1
  56. package/dist/mcp-tools/base/cognitive-tools.js +103 -30
  57. package/dist/mcp-tools/base/cognitive-tools.js.map +1 -1
  58. package/dist/mcp-tools/base/extraction-tools.d.ts.map +1 -1
  59. package/dist/mcp-tools/base/extraction-tools.js +6 -0
  60. package/dist/mcp-tools/base/extraction-tools.js.map +1 -1
  61. package/dist/mcp-tools/base/interaction-tools.d.ts.map +1 -1
  62. package/dist/mcp-tools/base/interaction-tools.js +76 -11
  63. package/dist/mcp-tools/base/interaction-tools.js.map +1 -1
  64. package/dist/mcp-tools/base/session-tools.d.ts +1 -1
  65. package/dist/mcp-tools/base/session-tools.d.ts.map +1 -1
  66. package/dist/mcp-tools/base/session-tools.js +64 -7
  67. package/dist/mcp-tools/base/session-tools.js.map +1 -1
  68. package/dist/mcp-tools/base/site-knowledge-tools.d.ts.map +1 -1
  69. package/dist/mcp-tools/base/site-knowledge-tools.js +15 -1
  70. package/dist/mcp-tools/base/site-knowledge-tools.js.map +1 -1
  71. package/dist/mcp-tools/base/testing-tools.d.ts.map +1 -1
  72. package/dist/mcp-tools/base/testing-tools.js +20 -0
  73. package/dist/mcp-tools/base/testing-tools.js.map +1 -1
  74. package/dist/mcp-tools/base/visual-testing-tools.d.ts.map +1 -1
  75. package/dist/mcp-tools/base/visual-testing-tools.js +52 -23
  76. package/dist/mcp-tools/base/visual-testing-tools.js.map +1 -1
  77. package/dist/mcp-tools/persona-creation-tools.d.ts.map +1 -1
  78. package/dist/mcp-tools/persona-creation-tools.js +18 -0
  79. package/dist/mcp-tools/persona-creation-tools.js.map +1 -1
  80. package/dist/mcp-tools/session-policy.d.ts +43 -0
  81. package/dist/mcp-tools/session-policy.d.ts.map +1 -0
  82. package/dist/mcp-tools/session-policy.js +54 -0
  83. package/dist/mcp-tools/session-policy.js.map +1 -0
  84. package/dist/performance/metrics.d.ts +0 -3
  85. package/dist/performance/metrics.d.ts.map +1 -1
  86. package/dist/performance/metrics.js +45 -1
  87. package/dist/performance/metrics.js.map +1 -1
  88. package/dist/personas.d.ts.map +1 -1
  89. package/dist/personas.js +38 -4
  90. package/dist/personas.js.map +1 -1
  91. package/dist/remediation/patch-generator.d.ts.map +1 -1
  92. package/dist/remediation/patch-generator.js +36 -2
  93. package/dist/remediation/patch-generator.js.map +1 -1
  94. package/dist/testing/coverage.d.ts +8 -0
  95. package/dist/testing/coverage.d.ts.map +1 -1
  96. package/dist/testing/coverage.js +12 -0
  97. package/dist/testing/coverage.js.map +1 -1
  98. package/dist/types.d.ts +33 -0
  99. package/dist/types.d.ts.map +1 -1
  100. package/dist/types.js.map +1 -1
  101. package/dist/visual/cognitive-transport.d.ts +15 -2
  102. package/dist/visual/cognitive-transport.d.ts.map +1 -1
  103. package/dist/visual/cognitive-transport.js +85 -31
  104. package/dist/visual/cognitive-transport.js.map +1 -1
  105. package/dist/visual/perceptual-transport.d.ts +8 -0
  106. package/dist/visual/perceptual-transport.d.ts.map +1 -1
  107. package/dist/visual/perceptual-transport.js +19 -0
  108. package/dist/visual/perceptual-transport.js.map +1 -1
  109. package/dist/visual/regression.d.ts.map +1 -1
  110. package/dist/visual/regression.js +76 -3
  111. package/dist/visual/regression.js.map +1 -1
  112. package/package.json +1 -1
package/dist/browser.js CHANGED
@@ -19,6 +19,7 @@ import { DEVICE_PRESETS, LOCATION_PRESETS, CBrowserErrorCode } from "./types.js"
19
19
  import { runCognitiveJourney, isApiKeyConfigured, } from "./cognitive/index.js";
20
20
  import { SessionManager } from "./browser/session-manager.js";
21
21
  import { SelectorCacheManager } from "./browser/selector-cache.js";
22
+ import { extractExpected, extractSubject, UNPARSEABLE_MESSAGE } from "./browser/assertion-value.js";
22
23
  import { OverlayHandler } from "./browser/overlay-handler.js";
23
24
  import { getRemoteMode, MAX_RESPONSE_SIZE } from "./mcp-tools/screenshot-utils.js";
24
25
  import { clampRect } from "./recording/timing.js";
@@ -152,6 +153,26 @@ export async function launchBrowserWithFallback(browserType = chromium, options
152
153
  }
153
154
  }
154
155
  }
156
+ /**
157
+ * Compress a Playwright error for a tool response.
158
+ *
159
+ * Playwright appends a "Call log:" listing every retry, which for a click that
160
+ * waits out its timeout runs to dozens of near-identical lines and roughly 4k
161
+ * tokens. The first lines say what failed and the last say how it ended; the
162
+ * middle is the same line repeated. Keep both ends, drop the middle, and say how
163
+ * much was dropped so nobody wonders what they are not seeing. (2026-07-29)
164
+ */
165
+ function summarizePlaywrightError(message, headLines = 6, tailLines = 4) {
166
+ const lines = message.split("\n");
167
+ if (lines.length <= headLines + tailLines + 2)
168
+ return message;
169
+ const dropped = lines.length - headLines - tailLines;
170
+ return [
171
+ ...lines.slice(0, headLines),
172
+ ` … ${dropped} more lines of Playwright retry log elided (set verbose for the full log) …`,
173
+ ...lines.slice(-tailLines),
174
+ ].join("\n");
175
+ }
155
176
  export class CBrowser {
156
177
  config;
157
178
  paths;
@@ -161,6 +182,14 @@ export class CBrowser {
161
182
  currentPersona = null;
162
183
  networkRequests = [];
163
184
  networkResponses = new Map();
185
+ /**
186
+ * In-flight requests awaiting a response, so the response handler can attach
187
+ * status and duration to the originating record. Keyed by url+method, capped
188
+ * so a page that fires requests which never resolve cannot grow it without
189
+ * bound. (2026-07-28)
190
+ */
191
+ pendingRequests = new Map();
192
+ static MAX_PENDING_REQUESTS = 2000;
164
193
  harEntries = [];
165
194
  isRecordingHar = false;
166
195
  skipSessionRestore;
@@ -1185,6 +1214,17 @@ For more help: https://playwright.dev/docs/browsers
1185
1214
  timestamp: new Date().toISOString(),
1186
1215
  };
1187
1216
  this.networkRequests.push(networkRequest);
1217
+ // Track it so the response handler can complete the record. Evict the
1218
+ // oldest entry rather than growing forever when responses never arrive.
1219
+ if (this.pendingRequests.size >= CBrowser.MAX_PENDING_REQUESTS) {
1220
+ const oldest = this.pendingRequests.keys().next();
1221
+ if (!oldest.done)
1222
+ this.pendingRequests.delete(oldest.value);
1223
+ }
1224
+ this.pendingRequests.set(request.url() + request.method(), {
1225
+ req: networkRequest,
1226
+ startedAt: Date.now(),
1227
+ });
1188
1228
  if (this.isRecordingHar) {
1189
1229
  // Start HAR entry
1190
1230
  const harEntry = {
@@ -1204,12 +1244,17 @@ For more help: https://playwright.dev/docs/browsers
1204
1244
  });
1205
1245
  this.page.on("response", async (response) => {
1206
1246
  const key = response.url() + response.request().method();
1207
- const _networkResponse = {
1208
- url: response.url(),
1209
- status: response.status(),
1210
- statusText: response.statusText(),
1211
- headers: response.headers(),
1212
- };
1247
+ // This status was already being computed here and then discarded into an
1248
+ // unused local, which is why get_network_requests could never report the
1249
+ // status codes and timing its description promised. Attach it to the
1250
+ // originating request record instead. (2026-07-28)
1251
+ const pending = this.pendingRequests.get(key);
1252
+ if (pending) {
1253
+ pending.req.status = response.status();
1254
+ pending.req.statusText = response.statusText();
1255
+ pending.req.durationMs = Date.now() - pending.startedAt;
1256
+ this.pendingRequests.delete(key);
1257
+ }
1213
1258
  if (this.isRecordingHar && this.networkResponses.has(key)) {
1214
1259
  const partial = this.networkResponses.get(key);
1215
1260
  const startTime = new Date(partial.startedDateTime).getTime();
@@ -1284,6 +1329,7 @@ For more help: https://playwright.dev/docs/browsers
1284
1329
  clearNetworkHistory() {
1285
1330
  this.networkRequests = [];
1286
1331
  this.harEntries = [];
1332
+ this.pendingRequests.clear();
1287
1333
  }
1288
1334
  // =========================================================================
1289
1335
  // Performance Metrics
@@ -1293,7 +1339,7 @@ For more help: https://playwright.dev/docs/browsers
1293
1339
  */
1294
1340
  async getPerformanceMetrics() {
1295
1341
  const page = await this.getPage();
1296
- const metrics = await page.evaluate(() => {
1342
+ const metrics = await page.evaluate(async () => {
1297
1343
  const result = {};
1298
1344
  // Navigation timing
1299
1345
  const navTiming = performance.getEntriesByType("navigation")[0];
@@ -1309,19 +1355,45 @@ For more help: https://playwright.dev/docs/browsers
1309
1355
  result.fcp = entry.startTime;
1310
1356
  }
1311
1357
  }
1312
- // LCP from PerformanceObserver (if available)
1313
- const lcpEntries = performance.getEntriesByType("largest-contentful-paint");
1314
- if (lcpEntries.length > 0) {
1315
- result.lcp = lcpEntries[lcpEntries.length - 1].startTime;
1316
- }
1317
- // CLS from layout-shift entries
1318
- const clsEntries = performance.getEntriesByType("layout-shift");
1358
+ // LCP and CLS are delivered ONLY to a PerformanceObserver. Chromium never
1359
+ // adds largest-contentful-paint or layout-shift entries to the performance
1360
+ // timeline, so the previous getEntriesByType() calls returned [] on every
1361
+ // page. LCP was therefore always undefined, and CLS was always exactly 0 —
1362
+ // which looked like a real measurement of a perfectly stable page rather
1363
+ // than no measurement at all. `buffered: true` replays the entries that
1364
+ // already fired before this observer existed. (2026-07-29)
1319
1365
  let clsScore = 0;
1320
- for (const entry of clsEntries) {
1321
- if (!entry.hadRecentInput) {
1322
- clsScore += entry.value || 0;
1366
+ await new Promise((resolve) => {
1367
+ let settled = false;
1368
+ const finish = () => { if (!settled) {
1369
+ settled = true;
1370
+ resolve();
1371
+ } };
1372
+ try {
1373
+ const lcpObs = new PerformanceObserver((list) => {
1374
+ const entries = list.getEntries();
1375
+ if (entries.length > 0) {
1376
+ result.lcp = entries[entries.length - 1].startTime;
1377
+ }
1378
+ });
1379
+ lcpObs.observe({ type: "largest-contentful-paint", buffered: true });
1380
+ const clsObs = new PerformanceObserver((list) => {
1381
+ for (const entry of list.getEntries()) {
1382
+ if (!entry.hadRecentInput) {
1383
+ clsScore += entry.value || 0;
1384
+ }
1385
+ }
1386
+ });
1387
+ clsObs.observe({ type: "layout-shift", buffered: true });
1323
1388
  }
1324
- }
1389
+ catch {
1390
+ // Older engines without these entry types: leave lcp undefined rather
1391
+ // than reporting a fabricated value.
1392
+ finish();
1393
+ }
1394
+ // Buffered entries arrive on the next task; give late shifts a moment too.
1395
+ setTimeout(finish, 300);
1396
+ });
1325
1397
  result.cls = clsScore;
1326
1398
  // Resource counts
1327
1399
  const resources = performance.getEntriesByType("resource");
@@ -2090,7 +2162,10 @@ For more help: https://playwright.dev/docs/browsers
2090
2162
  const result = {
2091
2163
  success: false,
2092
2164
  screenshot: await this.screenshot(),
2093
- message: `Failed to click: ${errorMessage}`,
2165
+ // Was the raw Playwright error, whose Call log ran to ~4k tokens of
2166
+ // near-identical retry lines and crowded out everything else in the
2167
+ // response. verbose still gets the whole thing. (2026-07-29)
2168
+ message: `Failed to click: ${options.verbose ? errorMessage : summarizePlaywrightError(errorMessage)}`,
2094
2169
  };
2095
2170
  if (options.verbose) {
2096
2171
  const available = await this.getAvailableClickables(page);
@@ -4036,8 +4111,9 @@ For more help: https://playwright.dev/docs/browsers
4036
4111
  // Page title assertions
4037
4112
  if (lowerAssertion.includes("title") && (lowerAssertion.includes("is") || lowerAssertion.includes("contains"))) {
4038
4113
  const title = await page.title();
4039
- const match = assertion.match(/["']([^"']+)["']/);
4040
- const expected = match?.[1] || "";
4114
+ const expected = extractExpected(assertion);
4115
+ if (expected === null)
4116
+ return { passed: false, assertion, actual: title, message: UNPARSEABLE_MESSAGE };
4041
4117
  if (lowerAssertion.includes("contains")) {
4042
4118
  const passed = title.toLowerCase().includes(expected.toLowerCase());
4043
4119
  return { passed, assertion, actual: title, expected, message: passed ? "Title contains expected text" : `Title "${title}" does not contain "${expected}"` };
@@ -4050,8 +4126,9 @@ For more help: https://playwright.dev/docs/browsers
4050
4126
  // URL assertions
4051
4127
  if (lowerAssertion.includes("url") && (lowerAssertion.includes("is") || lowerAssertion.includes("contains"))) {
4052
4128
  const url = page.url();
4053
- const match = assertion.match(/["']([^"']+)["']/);
4054
- const expected = match?.[1] || "";
4129
+ const expected = extractExpected(assertion);
4130
+ if (expected === null)
4131
+ return { passed: false, assertion, actual: url, message: UNPARSEABLE_MESSAGE };
4055
4132
  if (lowerAssertion.includes("contains")) {
4056
4133
  const passed = url.includes(expected);
4057
4134
  return { passed, assertion, actual: url, expected, message: passed ? "URL contains expected text" : `URL "${url}" does not contain "${expected}"` };
@@ -4064,8 +4141,9 @@ For more help: https://playwright.dev/docs/browsers
4064
4141
  // Text presence assertions
4065
4142
  // v14.3.0: Filter script/style content to avoid ad injection false negatives
4066
4143
  if (lowerAssertion.includes("page") && (lowerAssertion.includes("contains") || lowerAssertion.includes("has") || lowerAssertion.includes("shows"))) {
4067
- const match = assertion.match(/["']([^"']+)["']/);
4068
- const expected = match?.[1] || "";
4144
+ const expected = extractExpected(assertion);
4145
+ if (expected === null)
4146
+ return { passed: false, assertion, message: UNPARSEABLE_MESSAGE };
4069
4147
  // v14.3.0: Use filtered text extraction (excludes script/style content)
4070
4148
  const content = await page.evaluate(() => {
4071
4149
  const clone = document.body.cloneNode(true);
@@ -4079,8 +4157,9 @@ For more help: https://playwright.dev/docs/browsers
4079
4157
  }
4080
4158
  // Element existence assertions
4081
4159
  if (lowerAssertion.includes("exists") || lowerAssertion.includes("visible") || lowerAssertion.includes("present")) {
4082
- const match = assertion.match(/["']([^"']+)["']/);
4083
- const selector = match?.[1] || "";
4160
+ const selector = extractSubject(assertion);
4161
+ if (selector === null)
4162
+ return { passed: false, assertion, message: UNPARSEABLE_MESSAGE };
4084
4163
  const element = await this.findElement(selector);
4085
4164
  const passed = element !== null;
4086
4165
  return { passed, assertion, expected: selector, message: passed ? `Element "${selector}" exists` : `Element "${selector}" not found` };