ccqa 1.51.0 → 1.52.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.
- package/README.md +11 -4
- package/dist/bin/ccqa.mjs +22449 -13372
- package/dist/evidence-constants-BufWx8Bt.cjs +59 -0
- package/dist/hub-client/index.cjs +434 -0
- package/dist/hub-client/index.d.cts +1376 -0
- package/dist/hub-client/index.d.mts +28 -4
- package/dist/package.json +49 -9
- package/dist/runtime/judge.cjs +1307 -0
- package/dist/runtime/judge.d.cts +68 -0
- package/dist/runtime/judge.d.mts +24 -2
- package/dist/runtime/judge.mjs +1229 -3
- package/dist/runtime/step-evidence.cjs +106 -0
- package/dist/runtime/step-evidence.d.cts +53 -0
- package/dist/runtime/step-evidence.mjs +1 -1
- package/dist/runtime/test-helpers.cjs +461 -0
- package/dist/runtime/test-helpers.d.cts +39 -0
- package/dist/runtime/test-helpers.mjs +134 -3
- package/package.json +49 -9
- package/dist/diagnose-CZms9Cer.mjs +0 -2432
- package/dist/spawn-ab-CR_Sr7wh.mjs +0 -199
- /package/dist/{evidence-constants-Cm_S_5od.mjs → evidence-constants-C425F7ZG.mjs} +0 -0
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
import { statSync } from "node:fs";
|
|
3
|
-
import { delimiter, dirname, join } from "node:path";
|
|
4
|
-
import { spawnSync } from "node:child_process";
|
|
5
|
-
//#region src/runtime/agent-browser-bin.ts
|
|
6
|
-
const require = createRequire(import.meta.url);
|
|
7
|
-
function hasAgentBrowserShim(dir) {
|
|
8
|
-
try {
|
|
9
|
-
statSync(join(dir, "agent-browser"));
|
|
10
|
-
return true;
|
|
11
|
-
} catch {
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Walks up from `start` looking for a `node_modules/.bin/agent-browser` shim.
|
|
17
|
-
* Returns the .bin directory containing the shim, or null if none is found.
|
|
18
|
-
*/
|
|
19
|
-
function findNodeModulesBin(start) {
|
|
20
|
-
let cur = start;
|
|
21
|
-
while (true) {
|
|
22
|
-
const candidate = join(cur, "node_modules", ".bin");
|
|
23
|
-
if (hasAgentBrowserShim(candidate)) return candidate;
|
|
24
|
-
const parent = dirname(cur);
|
|
25
|
-
if (parent === cur) return null;
|
|
26
|
-
cur = parent;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
/** The shim-directory walk shared by every resolution below (no env override). */
|
|
30
|
-
function resolveShimDir() {
|
|
31
|
-
const fromCwd = findNodeModulesBin(process.cwd());
|
|
32
|
-
if (fromCwd) return fromCwd;
|
|
33
|
-
const fromSelf = findNodeModulesBin(dirname(require.resolve("agent-browser/package.json")));
|
|
34
|
-
if (fromSelf) return fromSelf;
|
|
35
|
-
try {
|
|
36
|
-
const candidate = join(dirname(require.resolve("agent-browser/package.json")), "node_modules", ".bin");
|
|
37
|
-
if (hasAgentBrowserShim(candidate)) return candidate;
|
|
38
|
-
} catch {}
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* INVARIANT: every agent-browser invocation in one ccqa process — the host
|
|
43
|
-
* side (`spawnAB`: state load, replay probes, …) and the Claude subprocess
|
|
44
|
-
* (via the PATH prepended by `pathWithAgentBrowserShim`) — must resolve to
|
|
45
|
-
* the SAME binary. agent-browser runs one daemon per binary, and a state
|
|
46
|
-
* loaded into one daemon's session is invisible to a same-named session on
|
|
47
|
-
* another daemon, so a split resolution silently loses session restores.
|
|
48
|
-
* Both entry points below therefore share one resolution order:
|
|
49
|
-
* `CCQA_AB_BIN` (explicit override, e.g. the e2e harness or a dev build
|
|
50
|
-
* driving a consumer project's agent-browser) → the peer-installed shim
|
|
51
|
-
* (consumer project first, then ccqa's own tree).
|
|
52
|
-
*/
|
|
53
|
-
/**
|
|
54
|
-
* Resolves the executable `spawnAB` should invoke. Falls back to the package
|
|
55
|
-
* JS entry (resolvable whenever the peer dependency is installed) when no
|
|
56
|
-
* shim directory exists; throws only if agent-browser is missing entirely.
|
|
57
|
-
*/
|
|
58
|
-
function resolveAgentBrowserBin() {
|
|
59
|
-
const override = process.env["CCQA_AB_BIN"];
|
|
60
|
-
if (override) return override;
|
|
61
|
-
const shimDir = resolveShimDir();
|
|
62
|
-
if (shimDir) return join(shimDir, "agent-browser");
|
|
63
|
-
return require.resolve("agent-browser/bin/agent-browser.js");
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Resolves the directory containing the `agent-browser` shim that npm/pnpm
|
|
67
|
-
* exposes on PATH for the peer-installed package. Used by `ccqa trace` /
|
|
68
|
-
* `ccqa run` to prepend this directory to PATH so the Claude subprocess can
|
|
69
|
-
* invoke `agent-browser ...` without requiring a global install.
|
|
70
|
-
*
|
|
71
|
-
* Returns null if agent-browser cannot be located.
|
|
72
|
-
*/
|
|
73
|
-
function resolveAgentBrowserBinDir() {
|
|
74
|
-
const override = process.env["CCQA_AB_BIN"];
|
|
75
|
-
if (override) return dirname(override);
|
|
76
|
-
return resolveShimDir();
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Returns a PATH string with the agent-browser shim directory prepended,
|
|
80
|
-
* so `agent-browser ...` resolves without a global install. Falls back to
|
|
81
|
-
* the original PATH when the package can't be resolved.
|
|
82
|
-
*/
|
|
83
|
-
function pathWithAgentBrowserShim(currentPath) {
|
|
84
|
-
const path = currentPath ?? "";
|
|
85
|
-
const dir = resolveAgentBrowserBinDir();
|
|
86
|
-
if (!dir) return path;
|
|
87
|
-
if (path.split(delimiter).includes(dir)) return path;
|
|
88
|
-
return dir + delimiter + path;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Confirms before launching Claude that an `agent-browser` shim is reachable
|
|
92
|
-
* via PATH. We do this up front so a missing peer dependency fails fast with
|
|
93
|
-
* a clear message, instead of Claude burning tokens probing the system with
|
|
94
|
-
* `which`, `find`, `npm install`, etc.
|
|
95
|
-
*
|
|
96
|
-
* The `resolver` argument is for tests; production calls take no args.
|
|
97
|
-
*/
|
|
98
|
-
function assertAgentBrowserAvailable(resolver = resolveAgentBrowserBinDir) {
|
|
99
|
-
const probe = process.env["CCQA_AB_BIN"] ?? (() => {
|
|
100
|
-
const dir = resolver();
|
|
101
|
-
return dir === null ? null : join(dir, "agent-browser");
|
|
102
|
-
})();
|
|
103
|
-
if (!probe) throw new AgentBrowserUnavailableError();
|
|
104
|
-
try {
|
|
105
|
-
const s = statSync(probe);
|
|
106
|
-
if (!s.isFile() && !s.isSymbolicLink()) throw new AgentBrowserUnavailableError();
|
|
107
|
-
} catch {
|
|
108
|
-
throw new AgentBrowserUnavailableError();
|
|
109
|
-
}
|
|
110
|
-
return dirname(probe);
|
|
111
|
-
}
|
|
112
|
-
var AgentBrowserUnavailableError = class extends Error {
|
|
113
|
-
constructor() {
|
|
114
|
-
super("agent-browser binary not found on PATH");
|
|
115
|
-
this.name = "AgentBrowserUnavailableError";
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
/** Human-readable explanation shown to the user when the guard fires. */
|
|
119
|
-
function formatAgentBrowserUnavailableMessage() {
|
|
120
|
-
return [
|
|
121
|
-
"agent-browser is not installed or not on PATH.",
|
|
122
|
-
"",
|
|
123
|
-
"ccqa drives the browser via the peer-installed `agent-browser` package.",
|
|
124
|
-
"Install it in this project:",
|
|
125
|
-
"",
|
|
126
|
-
" pnpm add -D agent-browser",
|
|
127
|
-
" # or",
|
|
128
|
-
" npm install -D agent-browser",
|
|
129
|
-
"",
|
|
130
|
-
"If it is already installed, make sure you are running ccqa from the",
|
|
131
|
-
"project root (or via your package runner, e.g. `pnpm exec ccqa ...`)."
|
|
132
|
-
].join("\n");
|
|
133
|
-
}
|
|
134
|
-
//#endregion
|
|
135
|
-
//#region src/runtime/spawn-ab.ts
|
|
136
|
-
const AB = resolveAgentBrowserBin();
|
|
137
|
-
const EAGAIN_PATTERN = /Resource temporarily unavailable|os error 35/i;
|
|
138
|
-
const EAGAIN_TOTAL_BUDGET_MS = 3e4;
|
|
139
|
-
const EAGAIN_BACKOFF_MS = [
|
|
140
|
-
100,
|
|
141
|
-
200,
|
|
142
|
-
300,
|
|
143
|
-
500,
|
|
144
|
-
700,
|
|
145
|
-
1e3,
|
|
146
|
-
1500,
|
|
147
|
-
2e3,
|
|
148
|
-
2500,
|
|
149
|
-
3e3,
|
|
150
|
-
3e3,
|
|
151
|
-
3e3,
|
|
152
|
-
3e3,
|
|
153
|
-
3e3,
|
|
154
|
-
3e3
|
|
155
|
-
];
|
|
156
|
-
const PROCESS_HARD_TIMEOUT_MS = 35e3;
|
|
157
|
-
function sleepSync(ms) {
|
|
158
|
-
const buf = new SharedArrayBuffer(4);
|
|
159
|
-
Atomics.wait(new Int32Array(buf), 0, 0, ms);
|
|
160
|
-
}
|
|
161
|
-
function spawnABOnce(args) {
|
|
162
|
-
const result = spawnSync(AB, args, {
|
|
163
|
-
stdio: "pipe",
|
|
164
|
-
timeout: PROCESS_HARD_TIMEOUT_MS
|
|
165
|
-
});
|
|
166
|
-
const wedged = result.error?.code === "ETIMEDOUT";
|
|
167
|
-
return {
|
|
168
|
-
status: result.status,
|
|
169
|
-
stdout: result.stdout?.toString() ?? "",
|
|
170
|
-
stderr: (result.stderr?.toString() ?? "") + (wedged ? "\n[ccqa] agent-browser killed after hard timeout" : ""),
|
|
171
|
-
wedged
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Invoke `agent-browser` once and return its exit status/stdout/stderr,
|
|
176
|
-
* retrying internally up to ~30s while the daemon's state file is in the
|
|
177
|
-
* "Resource temporarily unavailable" race window. Used by both the test
|
|
178
|
-
* runtime (`test-helpers.ts`) and the post-trace replay validation
|
|
179
|
-
* (`replay-validate.ts`). Kept out of `test-helpers.ts` because that
|
|
180
|
-
* module is also the public surface for generated test scripts — exposing
|
|
181
|
-
* the raw spawner there would widen the contract for end users.
|
|
182
|
-
*/
|
|
183
|
-
function spawnAB(args) {
|
|
184
|
-
let result = spawnABOnce(args);
|
|
185
|
-
let elapsed = 0;
|
|
186
|
-
let attempt = 0;
|
|
187
|
-
while (result.status !== 0 && elapsed < EAGAIN_TOTAL_BUDGET_MS) {
|
|
188
|
-
const combined = `${result.stdout}\n${result.stderr}`;
|
|
189
|
-
if (!EAGAIN_PATTERN.test(combined)) return result;
|
|
190
|
-
const wait = EAGAIN_BACKOFF_MS[attempt] ?? 3e3;
|
|
191
|
-
sleepSync(wait);
|
|
192
|
-
elapsed += wait;
|
|
193
|
-
attempt++;
|
|
194
|
-
result = spawnABOnce(args);
|
|
195
|
-
}
|
|
196
|
-
return result;
|
|
197
|
-
}
|
|
198
|
-
//#endregion
|
|
199
|
-
export { formatAgentBrowserUnavailableMessage as a, assertAgentBrowserAvailable as i, spawnAB as n, pathWithAgentBrowserShim as o, AgentBrowserUnavailableError as r, resolveAgentBrowserBin as s, sleepSync as t };
|
|
File without changes
|