@ait-co/devtools 0.1.126 → 0.1.127

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.
@@ -64,6 +64,14 @@ interface RelayRunOptions {
64
64
  */
65
65
  collectCaptures?: boolean;
66
66
  }
67
+ /**
68
+ * Sentinel string embedded in the error message by `injectAndRunBundle` when
69
+ * the per-file evaluate race hits the timeout. Used by the retry guard so only
70
+ * genuine timeouts get a second attempt — not bundle errors or parse failures.
71
+ *
72
+ * Exported for unit tests that assert the retry path is taken.
73
+ */
74
+ declare const EVALUATE_TIMEOUT_MARKER = "rpc: evaluate timed out after";
67
75
  /**
68
76
  * Runs all `files` sequentially over the given CDP `connection`.
69
77
  *
@@ -94,5 +102,5 @@ declare function flattenResults(report: RelayRunReport): Array<TestResult & {
94
102
  file: string;
95
103
  }>;
96
104
  //#endregion
97
- export { runTestFilesOverRelay as a, flattenResults as i, RelayRunOptions as n, RelayRunReport as r, FileResult as t };
98
- //# sourceMappingURL=relay-worker-7biDlEjo.d.ts.map
105
+ export { flattenResults as a, RelayRunReport as i, FileResult as n, runTestFilesOverRelay as o, RelayRunOptions as r, EVALUATE_TIMEOUT_MARKER as t };
106
+ //# sourceMappingURL=relay-worker-TReZtzGl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay-worker-TReZtzGl.d.ts","names":[],"sources":["../src/test-runner/relay-worker.ts"],"mappings":";;;;;;;UAwBiB,UAAA;EAcf;EAZA,IAAA;EAcA;EAZA,MAAA,EAAQ,SAAA;IAAc,KAAA;EAAA;AAAA;;UAIP,cAAA;EAwBS;EAtBxB,SAAA;EA0Be;EAxBf,QAAA;;EAEA,KAAA,EAAO,UAAA;EA0BP;EAxBA,MAAA;IACE,MAAA;IACA,MAAA;IACA,OAAA;IACA,KAAA;EAAA;EA+CgC;;;;AAuBpC;;;;;;EA1DE,QAAA,EAAU,cAAA;AAAA;;UAIK,eAAA;EAuDf;;;EAnDA,aAAA,GAAgB,aAAA;EAsDf;;;;EAjDD,SAAA;EAoP4B;;;;;;;;;;;EAxO5B,eAAA;AAAA;;;;;;;;cAUW,uBAAA;;;;;;;;;;;;;;;;;;;;;;iBAuBS,qBAAA,CACpB,UAAA,EAAY,aAAA,EACZ,KAAA,YACA,IAAA,GAAO,eAAA,GACN,OAAA,CAAQ,cAAA;;;;;iBAmMK,cAAA,CAAe,MAAA,EAAQ,cAAA,GAAiB,KAAA,CAAM,UAAA;EAAe,IAAA;AAAA"}
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
+ import * as path$1 from "node:path";
3
+ import path, { basename, isAbsolute, resolve } from "node:path";
2
4
  import { parseArgs } from "node:util";
3
5
  import * as fs from "node:fs/promises";
4
6
  import { glob, mkdir, writeFile } from "node:fs/promises";
5
- import * as path$1 from "node:path";
6
- import path, { isAbsolute, resolve } from "node:path";
7
7
  import { accessSync } from "node:fs";
8
8
  import { fileURLToPath } from "node:url";
9
9
  //#region src/test-runner/discover.ts
@@ -624,6 +624,14 @@ async function injectAndRunBundle(connection, bundleCode, timeoutMs = DEFAULT_TI
624
624
  //#endregion
625
625
  //#region src/test-runner/relay-worker.ts
626
626
  /**
627
+ * Sentinel string embedded in the error message by `injectAndRunBundle` when
628
+ * the per-file evaluate race hits the timeout. Used by the retry guard so only
629
+ * genuine timeouts get a second attempt — not bundle errors or parse failures.
630
+ *
631
+ * Exported for unit tests that assert the retry path is taken.
632
+ */
633
+ const EVALUATE_TIMEOUT_MARKER = "rpc: evaluate timed out after";
634
+ /**
627
635
  * Runs all `files` sequentially over the given CDP `connection`.
628
636
  *
629
637
  * For each file:
@@ -666,15 +674,49 @@ async function runTestFilesOverRelay(connection, files, opts) {
666
674
  let fileEntry;
667
675
  try {
668
676
  const { code } = await bundleTestFile(file, opts?.bundleOptions);
669
- const rpcResult = await injectAndRunBundle(connection, code, opts?.timeoutMs);
670
- if (rpcResult.ok) fileEntry = {
671
- file,
672
- result: rpcResult.report
673
- };
674
- else fileEntry = {
675
- file,
676
- result: { error: rpcResult.error }
677
+ /**
678
+ * Runs one evaluate attempt and returns a FileResult, or `null` when the
679
+ * result is a genuine timeout and the caller should retry.
680
+ *
681
+ * We need to distinguish:
682
+ * - `rpcResult.ok = false` + timeout error → retry candidate
683
+ * - `rpcResult.ok = false` + other error → final error, no retry
684
+ * - `injectAndRunBundle` throws → treated like a final error
685
+ * (throws happen on CDP exceptionDetails, not on the Promise.race
686
+ * timeout — the timeout produces `rpcResult.ok=false` with the
687
+ * EVALUATE_TIMEOUT_MARKER message)
688
+ */
689
+ const attempt = async () => {
690
+ let rpcResult;
691
+ try {
692
+ rpcResult = await injectAndRunBundle(connection, code, opts?.timeoutMs);
693
+ } catch (e) {
694
+ return {
695
+ file,
696
+ result: { error: e instanceof Error ? e.message : String(e) }
697
+ };
698
+ }
699
+ if (rpcResult.ok) return {
700
+ file,
701
+ result: rpcResult.report
702
+ };
703
+ if (rpcResult.error.includes("rpc: evaluate timed out after")) return null;
704
+ return {
705
+ file,
706
+ result: { error: rpcResult.error }
707
+ };
677
708
  };
709
+ const firstResult = await attempt();
710
+ if (firstResult !== null) fileEntry = firstResult;
711
+ else {
712
+ process.stderr.write(`relay-worker: evaluate timed out for ${file} — retrying once\n`);
713
+ const retryResult = await attempt();
714
+ if (retryResult !== null) fileEntry = retryResult;
715
+ else fileEntry = {
716
+ file,
717
+ result: { error: `${EVALUATE_TIMEOUT_MARKER} ${opts?.timeoutMs ?? 3e4}ms (after retry)` }
718
+ };
719
+ }
678
720
  } catch (e) {
679
721
  fileEntry = {
680
722
  file,
@@ -961,6 +1003,42 @@ function resolveTimeouts(rawTimeout, rawAttachTimeout) {
961
1003
  };
962
1004
  }
963
1005
  /**
1006
+ * Renders per-file result lines and the aggregate totals line to a string.
1007
+ *
1008
+ * Each file gets one line:
1009
+ * - Error/timeout: `FAIL <basename>: <error-class>`
1010
+ * - Pass (0 tests): `OK <basename>: 0 passed (empty file)`
1011
+ * - Pass: `OK <basename>: N passed[, M failed][, K skipped]`
1012
+ *
1013
+ * The aggregate totals line always follows.
1014
+ *
1015
+ * SECRET-HANDLING: only `basename(file)` is used — no absolute paths, relay
1016
+ * URLs, wss URLs, scheme URLs, or TOTP codes appear in the output. The error
1017
+ * string comes from `result.error` which is already secret-free (relay-worker
1018
+ * produces only error-class messages like "rpc: evaluate timed out after
1019
+ * 30000ms").
1020
+ *
1021
+ * Exported so unit tests can assert the per-file lines without spawning a
1022
+ * subprocess or going through the full relay attach flow.
1023
+ */
1024
+ function renderSummary(report) {
1025
+ const lines = [];
1026
+ for (const { file, result } of report.files) {
1027
+ const name = basename(file);
1028
+ if ("error" in result) lines.push(`FAIL ${name}: ${result.error}`);
1029
+ else {
1030
+ const parts = [`${result.passed} passed`];
1031
+ if (result.failed > 0) parts.push(`${result.failed} failed`);
1032
+ if (result.skipped > 0) parts.push(`${result.skipped} skipped`);
1033
+ const suffix = result.passed + result.failed + result.skipped === 0 ? " (empty file)" : "";
1034
+ lines.push(`OK ${name}: ${parts.join(", ")}${suffix}`);
1035
+ }
1036
+ }
1037
+ const { totals, duration } = report;
1038
+ lines.push(`\ndevtools-test: ${totals.passed} passed, ${totals.failed} failed, ${totals.skipped} skipped (${duration}ms)`);
1039
+ return lines.join("\n");
1040
+ }
1041
+ /**
964
1042
  * Runs `files` over `connection` and returns the aggregate report.
965
1043
  * This pure function is the testable core of the CLI (and is what the
966
1044
  * `run_tests` MCP tool calls against the daemon's attached connection); it is
@@ -968,10 +1046,7 @@ function resolveTimeouts(rawTimeout, rawAttachTimeout) {
968
1046
  */
969
1047
  async function runWithConnection(connection, files, opts) {
970
1048
  const report = await runTestFilesOverRelay(connection, files, opts);
971
- if (opts?.printSummary) {
972
- const { totals } = report;
973
- process.stdout.write(`\ndevtools-test: ${totals.passed} passed, ${totals.failed} failed, ${totals.skipped} skipped (${report.duration}ms)\n`);
974
- }
1049
+ if (opts?.printSummary) process.stdout.write(`\n${renderSummary(report)}\n`);
975
1050
  return report;
976
1051
  }
977
1052
  /**