ccqa 0.13.1 → 1.1.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.
@@ -9,13 +9,15 @@ import { z } from "zod";
9
9
  * gets the same types.
10
10
  */
11
11
  /**
12
- * A run's outcome. The hub never executes a run is created when a client
13
- * pushes the report of an already-finished `ccqa run`, so the only two
14
- * terminal states that reach the hub are "passed" and "failed".
12
+ * A run's outcome. "running" is non-terminal/mutablean opened run
13
+ * (`POST /runs/open`) sits in this state while it's patched incrementally.
14
+ * "passed"/"failed" are terminal/immutable: a pushed run (`POST /runs`)
15
+ * starts there directly, and an opened run ends there once done.
15
16
  */
16
17
  declare const RunStatusSchema: z.ZodEnum<{
17
18
  passed: "passed";
18
19
  failed: "failed";
20
+ running: "running";
19
21
  }>;
20
22
  type RunStatus = z.infer<typeof RunStatusSchema>;
21
23
  /**
@@ -30,6 +32,7 @@ declare const RunSchema: z.ZodObject<{
30
32
  status: z.ZodEnum<{
31
33
  passed: "passed";
32
34
  failed: "failed";
35
+ running: "running";
33
36
  }>;
34
37
  kind: z.ZodDefault<z.ZodEnum<{
35
38
  run: "run";
@@ -133,7 +136,9 @@ type PutActualCauseRequest = z.infer<typeof PutActualCauseRequestSchema>;
133
136
  *
134
137
  * Two kinds share one namespace:
135
138
  * - "guidance": the record/live prompt bundle — `.user.md` (human-maintained)
136
- * and `.agent.md` (auto-rewritten by `ccqa run --update-agent-prompt`).
139
+ * and `.agent.md` (auto-rewritten by `ccqa run --update-agent-prompt`)
140
+ * plus `triage.user`, the human-maintained guidance injected into the
141
+ * failure-analysis (triage) prompt.
137
142
  * - "custom-prompt": `analysis-custom-prompt` — Claude-written calibration guidance
138
143
  * learned from graded triage cases, injected into the failure-analysis
139
144
  * prompt at run time.
@@ -142,10 +147,304 @@ type PutActualCauseRequest = z.infer<typeof PutActualCauseRequestSchema>;
142
147
  * extensions; `PROMPT_LOCAL_PATHS` is the single mapping every caller (push,
143
148
  * pull, learn, UI) goes through so the two never drift.
144
149
  */
145
- declare const PROMPT_NAMES: readonly ["record.user", "record.agent", "live.user", "live.agent", "analysis-custom-prompt"];
150
+ declare const PROMPT_NAMES: readonly ["record.user", "record.agent", "live.user", "live.agent", "playwright.user", "playwright.agent", "runn.user", "runn.agent", "triage.user", "analysis-custom-prompt"];
146
151
  type PromptName = (typeof PROMPT_NAMES)[number];
147
152
  //#endregion
148
153
  //#region src/report/schema.d.ts
154
+ declare const ReportSpecResultSchema: z.ZodObject<{
155
+ feature: z.ZodString;
156
+ spec: z.ZodString;
157
+ title: z.ZodNullable<z.ZodString>;
158
+ target: z.ZodOptional<z.ZodString>;
159
+ status: z.ZodEnum<{
160
+ passed: "passed";
161
+ failed: "failed";
162
+ skipped: "skipped";
163
+ }>;
164
+ skipReason: z.ZodOptional<z.ZodString>;
165
+ testCounts: z.ZodNullable<z.ZodObject<{
166
+ total: z.ZodNumber;
167
+ passed: z.ZodNumber;
168
+ failed: z.ZodNumber;
169
+ }, z.core.$strip>>;
170
+ durationMs: z.ZodNullable<z.ZodNumber>;
171
+ assertions: z.ZodNullable<z.ZodArray<z.ZodObject<{
172
+ name: z.ZodString;
173
+ status: z.ZodEnum<{
174
+ passed: "passed";
175
+ failed: "failed";
176
+ skipped: "skipped";
177
+ }>;
178
+ durationMs: z.ZodNullable<z.ZodNumber>;
179
+ }, z.core.$strip>>>;
180
+ analysis: z.ZodNullable<z.ZodObject<{
181
+ label: z.ZodEnum<{
182
+ TEST_DRIFT: "TEST_DRIFT";
183
+ SPEC_CHANGE: "SPEC_CHANGE";
184
+ PRODUCT_BUG: "PRODUCT_BUG";
185
+ UNKNOWN: "UNKNOWN";
186
+ }>;
187
+ confidence: z.ZodNumber;
188
+ subDiagnosis: z.ZodOptional<z.ZodEnum<{
189
+ TIMING_ISSUE: "TIMING_ISSUE";
190
+ OVER_ASSERTION: "OVER_ASSERTION";
191
+ SELECTOR_DRIFT: "SELECTOR_DRIFT";
192
+ DATA_MISSING: "DATA_MISSING";
193
+ NONE: "NONE";
194
+ }>>;
195
+ headline: z.ZodDefault<z.ZodString>;
196
+ recommendation: z.ZodDefault<z.ZodString>;
197
+ evidence: z.ZodArray<z.ZodObject<{
198
+ file: z.ZodOptional<z.ZodString>;
199
+ detail: z.ZodString;
200
+ }, z.core.$strip>>;
201
+ reasoning: z.ZodString;
202
+ }, z.core.$strip>>;
203
+ analysisSkipped: z.ZodNullable<z.ZodString>;
204
+ driftIssues: z.ZodNullable<z.ZodArray<z.ZodObject<{
205
+ severity: z.ZodEnum<{
206
+ OK: "OK";
207
+ WARN: "WARN";
208
+ ERROR: "ERROR";
209
+ }>;
210
+ category: z.ZodEnum<{
211
+ assertable: "assertable";
212
+ blocks: "blocks";
213
+ granularity: "granularity";
214
+ unimplemented: "unimplemented";
215
+ }>;
216
+ stepId: z.ZodNullable<z.ZodString>;
217
+ message: z.ZodString;
218
+ detail: z.ZodOptional<z.ZodNullable<z.ZodString>>;
219
+ }, z.core.$strip>>>;
220
+ failureLogExcerpt: z.ZodNullable<z.ZodString>;
221
+ diffExcerpt: z.ZodNullable<z.ZodString>;
222
+ specYaml: z.ZodNullable<z.ZodString>;
223
+ evidence: z.ZodNullable<z.ZodArray<z.ZodObject<{
224
+ stepId: z.ZodString;
225
+ source: z.ZodString;
226
+ pngPath: z.ZodString;
227
+ url: z.ZodNullable<z.ZodString>;
228
+ title: z.ZodNullable<z.ZodString>;
229
+ capturedAt: z.ZodNullable<z.ZodString>;
230
+ description: z.ZodNullable<z.ZodString>;
231
+ status: z.ZodDefault<z.ZodEnum<{
232
+ passed: "passed";
233
+ failed: "failed";
234
+ }>>;
235
+ failureSummary: z.ZodDefault<z.ZodNullable<z.ZodString>>;
236
+ }, z.core.$strip>>>;
237
+ artifacts: z.ZodOptional<z.ZodArray<z.ZodObject<{
238
+ name: z.ZodString;
239
+ path: z.ZodString;
240
+ kind: z.ZodEnum<{
241
+ image: "image";
242
+ text: "text";
243
+ json: "json";
244
+ binary: "binary";
245
+ }>;
246
+ sizeBytes: z.ZodNumber;
247
+ }, z.core.$strip>>>;
248
+ liveRun: z.ZodNullable<z.ZodObject<{
249
+ runId: z.ZodString;
250
+ sessionName: z.ZodString;
251
+ startedAt: z.ZodString;
252
+ durationMs: z.ZodNumber;
253
+ steps: z.ZodArray<z.ZodObject<{
254
+ stepId: z.ZodString;
255
+ source: z.ZodString;
256
+ instruction: z.ZodString;
257
+ expected: z.ZodString;
258
+ status: z.ZodEnum<{
259
+ passed: "passed";
260
+ failed: "failed";
261
+ skipped: "skipped";
262
+ }>;
263
+ reasoning: z.ZodString;
264
+ beforePng: z.ZodNullable<z.ZodString>;
265
+ afterPng: z.ZodNullable<z.ZodString>;
266
+ durationMs: z.ZodNumber;
267
+ cost: z.ZodObject<{
268
+ totalCostUsd: z.ZodNullable<z.ZodNumber>;
269
+ durationApiMs: z.ZodNullable<z.ZodNumber>;
270
+ numTurns: z.ZodNullable<z.ZodNumber>;
271
+ inputTokens: z.ZodNullable<z.ZodNumber>;
272
+ cacheCreationInputTokens: z.ZodNullable<z.ZodNumber>;
273
+ cacheReadInputTokens: z.ZodNullable<z.ZodNumber>;
274
+ outputTokens: z.ZodNullable<z.ZodNumber>;
275
+ models: z.ZodArray<z.ZodString>;
276
+ }, z.core.$strip>;
277
+ commands: z.ZodOptional<z.ZodArray<z.ZodString>>;
278
+ }, z.core.$strip>>;
279
+ cost: z.ZodObject<{
280
+ totalCostUsd: z.ZodNullable<z.ZodNumber>;
281
+ durationApiMs: z.ZodNullable<z.ZodNumber>;
282
+ numTurns: z.ZodNullable<z.ZodNumber>;
283
+ inputTokens: z.ZodNullable<z.ZodNumber>;
284
+ cacheCreationInputTokens: z.ZodNullable<z.ZodNumber>;
285
+ cacheReadInputTokens: z.ZodNullable<z.ZodNumber>;
286
+ outputTokens: z.ZodNullable<z.ZodNumber>;
287
+ models: z.ZodArray<z.ZodString>;
288
+ }, z.core.$strip>;
289
+ }, z.core.$strip>>;
290
+ }, z.core.$strip>;
291
+ type ReportSpecResult = z.infer<typeof ReportSpecResultSchema>;
292
+ declare const RunReportDataSchema: z.ZodObject<{
293
+ schemaVersion: z.ZodLiteral<1>;
294
+ kind: z.ZodDefault<z.ZodEnum<{
295
+ run: "run";
296
+ drift: "drift";
297
+ }>>;
298
+ createdAt: z.ZodString;
299
+ runId: z.ZodNullable<z.ZodString>;
300
+ git: z.ZodObject<{
301
+ head: z.ZodNullable<z.ZodString>;
302
+ base: z.ZodNullable<z.ZodString>;
303
+ }, z.core.$strip>;
304
+ model: z.ZodNullable<z.ZodString>;
305
+ language: z.ZodDefault<z.ZodNullable<z.ZodString>>;
306
+ promptVersion: z.ZodString;
307
+ customPromptVersion: z.ZodDefault<z.ZodNullable<z.ZodString>>;
308
+ triageUserPromptHash: z.ZodOptional<z.ZodString>;
309
+ results: z.ZodArray<z.ZodObject<{
310
+ feature: z.ZodString;
311
+ spec: z.ZodString;
312
+ title: z.ZodNullable<z.ZodString>;
313
+ target: z.ZodOptional<z.ZodString>;
314
+ status: z.ZodEnum<{
315
+ passed: "passed";
316
+ failed: "failed";
317
+ skipped: "skipped";
318
+ }>;
319
+ skipReason: z.ZodOptional<z.ZodString>;
320
+ testCounts: z.ZodNullable<z.ZodObject<{
321
+ total: z.ZodNumber;
322
+ passed: z.ZodNumber;
323
+ failed: z.ZodNumber;
324
+ }, z.core.$strip>>;
325
+ durationMs: z.ZodNullable<z.ZodNumber>;
326
+ assertions: z.ZodNullable<z.ZodArray<z.ZodObject<{
327
+ name: z.ZodString;
328
+ status: z.ZodEnum<{
329
+ passed: "passed";
330
+ failed: "failed";
331
+ skipped: "skipped";
332
+ }>;
333
+ durationMs: z.ZodNullable<z.ZodNumber>;
334
+ }, z.core.$strip>>>;
335
+ analysis: z.ZodNullable<z.ZodObject<{
336
+ label: z.ZodEnum<{
337
+ TEST_DRIFT: "TEST_DRIFT";
338
+ SPEC_CHANGE: "SPEC_CHANGE";
339
+ PRODUCT_BUG: "PRODUCT_BUG";
340
+ UNKNOWN: "UNKNOWN";
341
+ }>;
342
+ confidence: z.ZodNumber;
343
+ subDiagnosis: z.ZodOptional<z.ZodEnum<{
344
+ TIMING_ISSUE: "TIMING_ISSUE";
345
+ OVER_ASSERTION: "OVER_ASSERTION";
346
+ SELECTOR_DRIFT: "SELECTOR_DRIFT";
347
+ DATA_MISSING: "DATA_MISSING";
348
+ NONE: "NONE";
349
+ }>>;
350
+ headline: z.ZodDefault<z.ZodString>;
351
+ recommendation: z.ZodDefault<z.ZodString>;
352
+ evidence: z.ZodArray<z.ZodObject<{
353
+ file: z.ZodOptional<z.ZodString>;
354
+ detail: z.ZodString;
355
+ }, z.core.$strip>>;
356
+ reasoning: z.ZodString;
357
+ }, z.core.$strip>>;
358
+ analysisSkipped: z.ZodNullable<z.ZodString>;
359
+ driftIssues: z.ZodNullable<z.ZodArray<z.ZodObject<{
360
+ severity: z.ZodEnum<{
361
+ OK: "OK";
362
+ WARN: "WARN";
363
+ ERROR: "ERROR";
364
+ }>;
365
+ category: z.ZodEnum<{
366
+ assertable: "assertable";
367
+ blocks: "blocks";
368
+ granularity: "granularity";
369
+ unimplemented: "unimplemented";
370
+ }>;
371
+ stepId: z.ZodNullable<z.ZodString>;
372
+ message: z.ZodString;
373
+ detail: z.ZodOptional<z.ZodNullable<z.ZodString>>;
374
+ }, z.core.$strip>>>;
375
+ failureLogExcerpt: z.ZodNullable<z.ZodString>;
376
+ diffExcerpt: z.ZodNullable<z.ZodString>;
377
+ specYaml: z.ZodNullable<z.ZodString>;
378
+ evidence: z.ZodNullable<z.ZodArray<z.ZodObject<{
379
+ stepId: z.ZodString;
380
+ source: z.ZodString;
381
+ pngPath: z.ZodString;
382
+ url: z.ZodNullable<z.ZodString>;
383
+ title: z.ZodNullable<z.ZodString>;
384
+ capturedAt: z.ZodNullable<z.ZodString>;
385
+ description: z.ZodNullable<z.ZodString>;
386
+ status: z.ZodDefault<z.ZodEnum<{
387
+ passed: "passed";
388
+ failed: "failed";
389
+ }>>;
390
+ failureSummary: z.ZodDefault<z.ZodNullable<z.ZodString>>;
391
+ }, z.core.$strip>>>;
392
+ artifacts: z.ZodOptional<z.ZodArray<z.ZodObject<{
393
+ name: z.ZodString;
394
+ path: z.ZodString;
395
+ kind: z.ZodEnum<{
396
+ image: "image";
397
+ text: "text";
398
+ json: "json";
399
+ binary: "binary";
400
+ }>;
401
+ sizeBytes: z.ZodNumber;
402
+ }, z.core.$strip>>>;
403
+ liveRun: z.ZodNullable<z.ZodObject<{
404
+ runId: z.ZodString;
405
+ sessionName: z.ZodString;
406
+ startedAt: z.ZodString;
407
+ durationMs: z.ZodNumber;
408
+ steps: z.ZodArray<z.ZodObject<{
409
+ stepId: z.ZodString;
410
+ source: z.ZodString;
411
+ instruction: z.ZodString;
412
+ expected: z.ZodString;
413
+ status: z.ZodEnum<{
414
+ passed: "passed";
415
+ failed: "failed";
416
+ skipped: "skipped";
417
+ }>;
418
+ reasoning: z.ZodString;
419
+ beforePng: z.ZodNullable<z.ZodString>;
420
+ afterPng: z.ZodNullable<z.ZodString>;
421
+ durationMs: z.ZodNumber;
422
+ cost: z.ZodObject<{
423
+ totalCostUsd: z.ZodNullable<z.ZodNumber>;
424
+ durationApiMs: z.ZodNullable<z.ZodNumber>;
425
+ numTurns: z.ZodNullable<z.ZodNumber>;
426
+ inputTokens: z.ZodNullable<z.ZodNumber>;
427
+ cacheCreationInputTokens: z.ZodNullable<z.ZodNumber>;
428
+ cacheReadInputTokens: z.ZodNullable<z.ZodNumber>;
429
+ outputTokens: z.ZodNullable<z.ZodNumber>;
430
+ models: z.ZodArray<z.ZodString>;
431
+ }, z.core.$strip>;
432
+ commands: z.ZodOptional<z.ZodArray<z.ZodString>>;
433
+ }, z.core.$strip>>;
434
+ cost: z.ZodObject<{
435
+ totalCostUsd: z.ZodNullable<z.ZodNumber>;
436
+ durationApiMs: z.ZodNullable<z.ZodNumber>;
437
+ numTurns: z.ZodNullable<z.ZodNumber>;
438
+ inputTokens: z.ZodNullable<z.ZodNumber>;
439
+ cacheCreationInputTokens: z.ZodNullable<z.ZodNumber>;
440
+ cacheReadInputTokens: z.ZodNullable<z.ZodNumber>;
441
+ outputTokens: z.ZodNullable<z.ZodNumber>;
442
+ models: z.ZodArray<z.ZodString>;
443
+ }, z.core.$strip>;
444
+ }, z.core.$strip>>;
445
+ }, z.core.$strip>>;
446
+ }, z.core.$strip>;
447
+ type RunReportData = z.infer<typeof RunReportDataSchema>;
149
448
  declare const LabelsExportSchema: z.ZodObject<{
150
449
  schemaVersion: z.ZodLiteral<1>;
151
450
  runId: z.ZodNullable<z.ZodString>;
@@ -170,7 +469,30 @@ declare const LabelsExportSchema: z.ZodObject<{
170
469
  }, z.core.$strip>;
171
470
  type LabelsExport = z.infer<typeof LabelsExportSchema>;
172
471
  //#endregion
472
+ //#region src/run/incremental-report.d.ts
473
+ /**
474
+ * The report envelope fields that don't change spec-to-spec — everything in
475
+ * `RunReportData` except `results`. Captured once when the writer is created
476
+ * so each incremental flush produces the same envelope the final batch write
477
+ * used to.
478
+ */
479
+ type ReportEnvelope = Omit<RunReportData, "results">;
480
+ //#endregion
173
481
  //#region src/hub-client/index.d.ts
482
+ /**
483
+ * Body of a `PATCH /runs/:id` incremental push: the newly-finished spec rows,
484
+ * their evidence PNGs (posix relPath → base64), the report envelope metadata
485
+ * (filled once the real git diff is known), and — on the last patch — `done`
486
+ * with the terminal status.
487
+ */
488
+ interface PatchRunRequest {
489
+ rows: ReportSpecResult[];
490
+ /** reportDir-relative posix path → base64 PNG bytes. */
491
+ evidence?: Record<string, string>;
492
+ done?: boolean;
493
+ finalStatus?: "passed" | "failed";
494
+ reportMeta?: Partial<ReportEnvelope>;
495
+ }
174
496
  /**
175
497
  * TypeScript client for the ccqa hub's public REST API (docs/hub-api.md).
176
498
  * Uses the global `fetch` only — no `node:*` imports — so this same module
@@ -215,6 +537,19 @@ interface HubClient {
215
537
  profile?: string;
216
538
  kind?: "run" | "drift";
217
539
  }): Promise<Run>;
540
+ /**
541
+ * Open a `running` run to push results into incrementally. Returns the new
542
+ * run's id. Non-retryable (a dropped response after the server committed
543
+ * would leave a second open run); callers degrade to local-only on failure.
544
+ */
545
+ openRun(meta: {
546
+ project: string;
547
+ branch?: string;
548
+ profile?: string;
549
+ kind?: "run" | "drift";
550
+ }): Promise<Run>;
551
+ /** Add finished spec rows (+ evidence) to a running run; `done` closes it. */
552
+ patchRun(id: string, body: PatchRunRequest): Promise<Run>;
218
553
  listRuns(q?: {
219
554
  project?: string;
220
555
  branch?: string;
@@ -257,7 +592,17 @@ interface HubClient {
257
592
  getPrompt(project: string, name: PromptName): Promise<string | null>;
258
593
  listPrompts(project: string): Promise<HubPromptMeta[]>;
259
594
  deletePrompt(project: string, name: PromptName): Promise<void>;
595
+ /** The project's perspectives document (coverage inventory), stored hub-only. */
596
+ putPerspectives(project: string, doc: unknown): Promise<void>;
597
+ getPerspectives(project: string): Promise<unknown | null>;
598
+ /** Set (or clear, with an empty string) the human note on one spec's entry. */
599
+ patchPerspectivesNote(project: string, c: {
600
+ feature: string;
601
+ spec: string;
602
+ note: string;
603
+ }): Promise<void>;
604
+ deletePerspectives(project: string): Promise<void>;
260
605
  }
261
606
  declare function createHubClient(opts: HubClientOptions): HubClient;
262
607
  //#endregion
263
- export { HubApiError, HubClient, HubClientOptions, HubPromptMeta, HubVariable, createHubClient };
608
+ export { HubApiError, HubClient, HubClientOptions, HubPromptMeta, HubVariable, PatchRunRequest, createHubClient };
@@ -104,6 +104,20 @@ function createHubClient(opts) {
104
104
  body: toBodyInit(archive)
105
105
  });
106
106
  },
107
+ openRun(meta) {
108
+ const params = new URLSearchParams({ project: meta.project });
109
+ if (meta.branch) params.set("branch", meta.branch);
110
+ if (meta.profile) params.set("profile", meta.profile);
111
+ if (meta.kind) params.set("kind", meta.kind);
112
+ return json(`/api/v1/runs/open?${params}`, { method: "POST" });
113
+ },
114
+ patchRun(id, body) {
115
+ return json(`/api/v1/runs/${encodeURIComponent(id)}`, {
116
+ method: "PATCH",
117
+ headers: { "Content-Type": "application/json" },
118
+ body: JSON.stringify(body)
119
+ });
120
+ },
107
121
  async listRuns(q = {}) {
108
122
  const { runs } = await json(`/api/v1/runs?${queryString(q)}`);
109
123
  return runs;
@@ -186,6 +200,27 @@ function createHubClient(opts) {
186
200
  },
187
201
  deletePrompt(project, name) {
188
202
  return noBody(`${promptsPath(project)}/${encodeURIComponent(name)}`, "DELETE");
203
+ },
204
+ putPerspectives(project, doc) {
205
+ return putJson(perspectivesPath(project), doc);
206
+ },
207
+ patchPerspectivesNote(project, c) {
208
+ return request(perspectivesPath(project), {
209
+ method: "PATCH",
210
+ headers: { "Content-Type": "application/json" },
211
+ body: JSON.stringify(c)
212
+ }).then(() => void 0);
213
+ },
214
+ async getPerspectives(project) {
215
+ try {
216
+ return await json(perspectivesPath(project));
217
+ } catch (err) {
218
+ if (err instanceof HubApiError && err.status === 404) return null;
219
+ throw err;
220
+ }
221
+ },
222
+ deletePerspectives(project) {
223
+ return noBody(perspectivesPath(project), "DELETE");
189
224
  }
190
225
  };
191
226
  }
@@ -197,6 +232,10 @@ function scopePath(project, kind, profile) {
197
232
  function promptsPath(project) {
198
233
  return `/api/v1/projects/${encodeURIComponent(project)}/prompts`;
199
234
  }
235
+ /** Perspectives are one document per project: `/api/v1/projects/<project>/perspectives`. */
236
+ function perspectivesPath(project) {
237
+ return `/api/v1/projects/${encodeURIComponent(project)}/perspectives`;
238
+ }
200
239
  function queryString(params) {
201
240
  const out = new URLSearchParams();
202
241
  for (const [key, value] of Object.entries(params)) if (value !== void 0) out.set(key, String(value));
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccqa",
3
- "version": "0.13.1",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "description": "Browser test recorder powered by Claude Code and agent-browser",
6
6
  "repository": {
@@ -1,4 +1,4 @@
1
- import { i as FAILURE_STEP_ID, n as spawnAB, r as FAILURE_SOURCE, t as sleepSync } from "../spawn-ab-Ja8NRRab.mjs";
1
+ import { i as FAILURE_STEP_ID, n as spawnAB, r as FAILURE_SOURCE, t as sleepSync } from "../spawn-ab-Cr967J2N.mjs";
2
2
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
3
3
  import { dirname, isAbsolute, join, resolve } from "node:path";
4
4
  //#region src/runtime/test-helpers.ts
@@ -14,7 +14,8 @@ const FAILURE_STEP_ID = "failure";
14
14
  const FAILURE_SOURCE = "failed";
15
15
  //#endregion
16
16
  //#region src/runtime/spawn-ab.ts
17
- const AB = createRequire(import.meta.url).resolve("agent-browser/bin/agent-browser.js");
17
+ const require = createRequire(import.meta.url);
18
+ const AB = process.env["CCQA_AB_BIN"] ?? require.resolve("agent-browser/bin/agent-browser.js");
18
19
  const EAGAIN_PATTERN = /Resource temporarily unavailable|os error 35/i;
19
20
  const EAGAIN_TOTAL_BUDGET_MS = 3e4;
20
21
  const EAGAIN_BACKOFF_MS = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccqa",
3
- "version": "0.13.1",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "description": "Browser test recorder powered by Claude Code and agent-browser",
6
6
  "repository": {