ccqa 1.45.0 → 1.45.1

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.
package/dist/bin/ccqa.mjs CHANGED
@@ -7167,6 +7167,7 @@ async function startBrowserCoverage(opts) {
7167
7167
  var Engine = class {
7168
7168
  client;
7169
7169
  connectFn;
7170
+ /** Last known address of the browser; see `refreshWsUrl`. */
7170
7171
  wsUrl;
7171
7172
  reconnects = 0;
7172
7173
  reconnectInFlight = false;
@@ -7285,6 +7286,22 @@ var Engine = class {
7285
7286
  this.stopped = true;
7286
7287
  this.clearTimer();
7287
7288
  }
7289
+ /**
7290
+ * Answers why the address could not be refreshed, so the retry that follows
7291
+ * says whether it is dialling a stale address or a dead browser. Told apart
7292
+ * because one is a driver to fix and the other is a browser to wait for, and
7293
+ * a silent stale address is the shape of the bug this exists to prevent.
7294
+ */
7295
+ async refreshWsUrl() {
7296
+ const ask = this.opts.currentCdpUrl;
7297
+ if (ask === void 0) return void 0;
7298
+ try {
7299
+ this.wsUrl = await browserWebSocketUrl(await ask());
7300
+ return;
7301
+ } catch (error) {
7302
+ return message(error);
7303
+ }
7304
+ }
7288
7305
  async reconnect(initialReason) {
7289
7306
  if (this.reconnectInFlight) return;
7290
7307
  this.reconnectInFlight = true;
@@ -7300,11 +7317,12 @@ var Engine = class {
7300
7317
  this.opts.warn(`browser coverage transport dropped (${reason}); reconnecting in ${delay}ms (${this.reconnects}/${MAX_RECONNECTS})`);
7301
7318
  await new Promise((resolve) => setTimeout(resolve, delay));
7302
7319
  if (this.stopped) return;
7320
+ const stale = await this.refreshWsUrl();
7303
7321
  let fresh;
7304
7322
  try {
7305
7323
  fresh = await this.connectFn(this.wsUrl);
7306
7324
  } catch (error) {
7307
- reason = `reconnect failed: ${message(error)}`;
7325
+ reason = stale ? `reconnect failed: ${message(error)} (address may be stale: ${stale})` : `reconnect failed: ${message(error)}`;
7308
7326
  continue;
7309
7327
  }
7310
7328
  if (this.stopped) {
@@ -7319,6 +7337,7 @@ var Engine = class {
7319
7337
  reason = `reconnect failed: ${message(error)}`;
7320
7338
  continue;
7321
7339
  }
7340
+ this.reconnects = 0;
7322
7341
  if (this.stopped) {
7323
7342
  this.clearTimer();
7324
7343
  fresh.close();
@@ -7749,9 +7768,10 @@ var CoverageSession = class CoverageSession {
7749
7768
  * destinations, the roots — lives here, so the caller only supplies where
7750
7769
  * the browser is.
7751
7770
  */
7752
- armBrowser(ref, cdpUrl, coverageDir) {
7771
+ armBrowser(ref, browser, coverageDir) {
7753
7772
  return startBrowserCoverage({
7754
- cdpUrl,
7773
+ cdpUrl: browser.cdpUrl,
7774
+ currentCdpUrl: browser.currentCdpUrl?.bind(browser),
7755
7775
  specId: specIdFor(this.runId, ref),
7756
7776
  origins: this.origins,
7757
7777
  assetOrigins: this.assetOrigins,
@@ -8257,7 +8277,7 @@ async function runOneSpec$1(ref, opts, blocks) {
8257
8277
  });
8258
8278
  const acquired = browserHandle;
8259
8279
  opts.teardown?.onFinalize(() => acquired.dispose());
8260
- browserEngine = await measurement.collector.armBrowser(ref, browserHandle.cdpUrl, coverageDir);
8280
+ browserEngine = await measurement.collector.armBrowser(ref, browserHandle, coverageDir);
8261
8281
  if (browserHandle.amendCommand) command = browserHandle.amendCommand(command);
8262
8282
  Object.assign(childEnv, browserHandle.env);
8263
8283
  } catch (err) {
@@ -13884,6 +13904,37 @@ async function acquireAgentBrowserEndpoint(ctx) {
13884
13904
  "about:blank"
13885
13905
  ]);
13886
13906
  if (warm.status !== 0) throw new Error(`could not start the session's browser: ${warm.stderr || warm.stdout}`);
13907
+ return {
13908
+ cdpUrl: askCdpUrl(session),
13909
+ currentCdpUrl: () => askCdpUrlSoon(session),
13910
+ dispose: async () => {}
13911
+ };
13912
+ }
13913
+ /**
13914
+ * The same question on a short leash. Its caller is racing its own backoff
13915
+ * between reconnect attempts, so an answer that arrives after the budget is
13916
+ * spent is worth less than no answer: `spawnAB` would sit through a 30s EAGAIN
13917
+ * retry and a 35s hard timeout, which is the whole reconnect window several
13918
+ * times over.
13919
+ */
13920
+ const CDP_URL_TIMEOUT_MS = 2e3;
13921
+ async function askCdpUrlSoon(session) {
13922
+ const { promisify } = await import("node:util");
13923
+ const { execFile } = await import("node:child_process");
13924
+ const { stdout } = await promisify(execFile)(resolveAgentBrowserBin$1(), [
13925
+ "--session",
13926
+ session,
13927
+ "get",
13928
+ "cdp-url"
13929
+ ], {
13930
+ timeout: CDP_URL_TIMEOUT_MS,
13931
+ encoding: "utf8"
13932
+ });
13933
+ const cdpUrl = stdout.trim().split("\n").pop()?.trim() ?? "";
13934
+ if (!/^wss?:\/\//.test(cdpUrl)) throw new Error(`agent-browser answered no cdp-url for session ${session}`);
13935
+ return cdpUrl;
13936
+ }
13937
+ function askCdpUrl(session) {
13887
13938
  const answer = spawnAB([
13888
13939
  "--session",
13889
13940
  session,
@@ -13892,10 +13943,7 @@ async function acquireAgentBrowserEndpoint(ctx) {
13892
13943
  ]);
13893
13944
  const cdpUrl = answer.stdout.trim().split("\n").pop()?.trim() ?? "";
13894
13945
  if (answer.status !== 0 || !/^wss?:\/\//.test(cdpUrl)) throw new Error(`agent-browser did not answer \`get cdp-url\` for session ${session}: ${answer.stderr || answer.stdout}`);
13895
- return {
13896
- cdpUrl,
13897
- dispose: async () => {}
13898
- };
13946
+ return cdpUrl;
13899
13947
  }
13900
13948
  //#endregion
13901
13949
  //#region src/diagnose/snapshot.ts
@@ -14279,7 +14327,7 @@ async function runOneSpec(args) {
14279
14327
  browserEngine = await opts.coverage.armBrowser({
14280
14328
  featureName,
14281
14329
  specName
14282
- }, handle.cdpUrl, coverageDir);
14330
+ }, handle, coverageDir);
14283
14331
  } catch (err) {
14284
14332
  coverageBroken = errMessage(err);
14285
14333
  warn(`coverage: could not attach to the live browser (${coverageBroken})`);
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccqa",
3
- "version": "1.45.0",
3
+ "version": "1.45.1",
4
4
  "type": "module",
5
5
  "description": "Browser test recorder powered by Claude Code and agent-browser",
6
6
  "repository": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccqa",
3
- "version": "1.45.0",
3
+ "version": "1.45.1",
4
4
  "type": "module",
5
5
  "description": "Browser test recorder powered by Claude Code and agent-browser",
6
6
  "repository": {