@crouton-kit/capture 1.4.22 → 1.4.23

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/bin/capture CHANGED
@@ -9095,6 +9095,347 @@ var init_console_recorder = __esm({
9095
9095
  }
9096
9096
  });
9097
9097
 
9098
+ // src/output/artifact.ts
9099
+ function isUrlRef(ref) {
9100
+ return /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(ref);
9101
+ }
9102
+ function stripTrailingSep(p) {
9103
+ return p.length > 1 && (p.endsWith("/") || p.endsWith(path5.sep)) ? p.slice(0, -1) : p;
9104
+ }
9105
+ function resolveRefFromPath(ref, config3) {
9106
+ const resolved = assertUnderCaptureRoot(stripTrailingSep(ref));
9107
+ const metaPath = path5.join(resolved, "meta.json");
9108
+ if (!fs5.existsSync(metaPath)) {
9109
+ throw new ArtifactResolutionError(
9110
+ ref,
9111
+ [resolved],
9112
+ `no ${config3.label} found at this path (missing meta.json)`,
9113
+ config3.creatingCommand
9114
+ );
9115
+ }
9116
+ return { kind: config3.kind, id: path5.basename(resolved), dir: resolved };
9117
+ }
9118
+ function resolveRefFromId(ref, config3) {
9119
+ const active = getActiveSession();
9120
+ if (!active) {
9121
+ throw new ArtifactResolutionError(
9122
+ ref,
9123
+ [],
9124
+ `no active capture session \u2014 a bare id resolves against the active session's ${config3.idSegments.join("/")} directory`,
9125
+ `capture session start (then re-run), or pass the absolute ${config3.label} path printed by ${config3.creatingCommand}`
9126
+ );
9127
+ }
9128
+ const dir = path5.join(active.dir, ...config3.idSegments, ref);
9129
+ const resolved = assertUnderCaptureRoot(dir);
9130
+ const metaPath = path5.join(resolved, "meta.json");
9131
+ if (!fs5.existsSync(metaPath)) {
9132
+ throw new ArtifactResolutionError(
9133
+ ref,
9134
+ [resolved],
9135
+ `no ${config3.label} named ${JSON.stringify(ref)} found in the active session (${active.sessionId})`,
9136
+ config3.creatingCommand
9137
+ );
9138
+ }
9139
+ return { kind: config3.kind, id: ref, dir: resolved };
9140
+ }
9141
+ function rejectRelativePath(ref) {
9142
+ if (!path5.isAbsolute(ref) && (ref.includes("/") || ref.includes(path5.sep))) {
9143
+ throw new ArtifactResolutionError(
9144
+ ref,
9145
+ [],
9146
+ "relative paths are not accepted \u2014 pass an absolute artifact path (as printed by the capturing command) or a bare id"
9147
+ );
9148
+ }
9149
+ }
9150
+ async function resolveSnapRef(ref, options = {}) {
9151
+ if (isUrlRef(ref)) {
9152
+ if (!options.onUrl) {
9153
+ throw new ArtifactResolutionError(ref, [], "this command does not accept a URL target \u2014 pass a snapshot id or path instead");
9154
+ }
9155
+ const result = await options.onUrl(ref);
9156
+ return { kind: "snap", id: result.id, dir: assertUnderCaptureRoot(result.dir) };
9157
+ }
9158
+ if (path5.isAbsolute(ref)) return resolveRefFromPath(ref, SNAP_CONFIG);
9159
+ rejectRelativePath(ref);
9160
+ return resolveRefFromId(ref, SNAP_CONFIG);
9161
+ }
9162
+ function resolveRecRef(ref) {
9163
+ if (isUrlRef(ref)) {
9164
+ throw new ArtifactResolutionError(
9165
+ ref,
9166
+ [],
9167
+ "a recording ref cannot be a URL \u2014 record one first with `capture motion rec <url> --do <action>` or `--start`/`--stop`"
9168
+ );
9169
+ }
9170
+ if (path5.isAbsolute(ref)) return resolveRefFromPath(ref, REC_CONFIG);
9171
+ rejectRelativePath(ref);
9172
+ return resolveRefFromId(ref, REC_CONFIG);
9173
+ }
9174
+ function resolveTraceRef(ref) {
9175
+ if (isUrlRef(ref)) {
9176
+ throw new ArtifactResolutionError(ref, [], "a trace ref cannot be a URL \u2014 record one first with `capture perf trace <url>` or `--start`/`--stop`");
9177
+ }
9178
+ const resolved = path5.isAbsolute(ref) ? resolveRefFromPath(ref, TRACE_CONFIG) : (rejectRelativePath(ref), resolveRefFromId(ref, TRACE_CONFIG));
9179
+ let meta;
9180
+ try {
9181
+ meta = JSON.parse(readPrivateFile(path5.join(resolved.dir, "meta.json")).toString("utf8"));
9182
+ } catch (error4) {
9183
+ throw new ArtifactResolutionError(ref, [path5.join(resolved.dir, "meta.json")], `could not read trace metadata: ${error4 instanceof Error ? error4.message : String(error4)}`, TRACE_CONFIG.creatingCommand);
9184
+ }
9185
+ const completion = meta.completion;
9186
+ if (completion !== "complete" && completion !== "partial" && completion !== "orphaned") {
9187
+ throw new ArtifactResolutionError(ref, [path5.join(resolved.dir, "meta.json")], "trace metadata has no valid completion state", TRACE_CONFIG.creatingCommand);
9188
+ }
9189
+ const inventory = meta.files;
9190
+ const inventoryMalformed = !Array.isArray(inventory);
9191
+ const inventoryMismatch = !inventoryMalformed && inventory.some((entry) => {
9192
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) return true;
9193
+ const item = entry;
9194
+ const name = item.name;
9195
+ const bytes = item.bytes;
9196
+ if (typeof name !== "string" || typeof bytes !== "number" || !Number.isSafeInteger(bytes) || bytes < 0 || name.includes(path5.sep)) return true;
9197
+ try {
9198
+ return fs5.statSync(path5.join(resolved.dir, name)).size !== bytes;
9199
+ } catch {
9200
+ return true;
9201
+ }
9202
+ });
9203
+ const truncatedReason = inventoryMalformed ? "artifact_inventory_missing_or_malformed" : inventoryMismatch ? "artifact_inventory_mismatch" : void 0;
9204
+ return { ...resolved, completion: truncatedReason ? "truncated" : completion, ...truncatedReason ? { reason: truncatedReason } : typeof meta.reason === "string" ? { reason: meta.reason } : {} };
9205
+ }
9206
+ function creatingCommandFor(filename) {
9207
+ return CREATING_COMMAND_BY_FILE[filename];
9208
+ }
9209
+ function artifactPath(ref, filename, opts = {}) {
9210
+ const filePath = assertUnderCaptureRoot(path5.join(ref.dir, filename));
9211
+ const mustExist = opts.mustExist ?? true;
9212
+ if (mustExist && !fs5.existsSync(filePath)) {
9213
+ throw new ArtifactResolutionError(
9214
+ ref.id,
9215
+ [filePath],
9216
+ `${filename} is not present in ${ref.kind === "snap" ? "snapshot" : ref.kind === "rec" ? "recording" : "trace"} ${ref.id}`,
9217
+ creatingCommandFor(filename)
9218
+ );
9219
+ }
9220
+ return filePath;
9221
+ }
9222
+ function artifactExists(ref, filename) {
9223
+ return fs5.existsSync(assertUnderCaptureRoot(path5.join(ref.dir, filename)));
9224
+ }
9225
+ function readJsonArtifactFile(ref, filename) {
9226
+ const filePath = artifactPath(ref, filename);
9227
+ let raw;
9228
+ try {
9229
+ raw = fs5.readFileSync(filePath, "utf-8");
9230
+ } catch (err) {
9231
+ throw new ArtifactResolutionError(
9232
+ ref.id,
9233
+ [filePath],
9234
+ `could not read ${filename}: ${err instanceof Error ? err.message : String(err)}`,
9235
+ creatingCommandFor(filename)
9236
+ );
9237
+ }
9238
+ try {
9239
+ return JSON.parse(raw);
9240
+ } catch (err) {
9241
+ throw new ArtifactResolutionError(
9242
+ ref.id,
9243
+ [filePath],
9244
+ `${filename} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
9245
+ creatingCommandFor(filename)
9246
+ );
9247
+ }
9248
+ }
9249
+ function readNdjsonArtifactFile(ref, filename) {
9250
+ const filePath = artifactPath(ref, filename);
9251
+ let raw;
9252
+ try {
9253
+ raw = fs5.readFileSync(filePath, "utf-8");
9254
+ } catch (err) {
9255
+ throw new ArtifactResolutionError(
9256
+ ref.id,
9257
+ [filePath],
9258
+ `could not read ${filename}: ${err instanceof Error ? err.message : String(err)}`,
9259
+ creatingCommandFor(filename)
9260
+ );
9261
+ }
9262
+ const lines = raw.split("\n").filter((l) => l.trim().length > 0);
9263
+ return lines.map((line4, i) => {
9264
+ try {
9265
+ return JSON.parse(line4);
9266
+ } catch (err) {
9267
+ throw new ArtifactResolutionError(
9268
+ ref.id,
9269
+ [filePath],
9270
+ `${filename} line ${i + 1} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
9271
+ creatingCommandFor(filename)
9272
+ );
9273
+ }
9274
+ });
9275
+ }
9276
+ function assertKind(ref, kind, fnName) {
9277
+ if (ref.kind !== kind) {
9278
+ throw new TypeError(`${fnName} expects a ${kind} ref, got a ${ref.kind} ref (${ref.id})`);
9279
+ }
9280
+ }
9281
+ function readMeta(ref) {
9282
+ return readJsonArtifactFile(ref, "meta.json");
9283
+ }
9284
+ function readGeometry(ref) {
9285
+ assertKind(ref, "snap", "readGeometry");
9286
+ return readJsonArtifactFile(ref, "geometry.json");
9287
+ }
9288
+ function readStyles(ref) {
9289
+ assertKind(ref, "snap", "readStyles");
9290
+ return readJsonArtifactFile(ref, "styles.json");
9291
+ }
9292
+ function readHittest(ref) {
9293
+ assertKind(ref, "snap", "readHittest");
9294
+ return readJsonArtifactFile(ref, "hittest.json");
9295
+ }
9296
+ function readText(ref) {
9297
+ assertKind(ref, "snap", "readText");
9298
+ return readJsonArtifactFile(ref, "text.json");
9299
+ }
9300
+ function readForms(ref) {
9301
+ assertKind(ref, "snap", "readForms");
9302
+ return readJsonArtifactFile(ref, "forms.json");
9303
+ }
9304
+ function readAnimation(ref) {
9305
+ assertKind(ref, "snap", "readAnimation");
9306
+ return readJsonArtifactFile(ref, "animation.json");
9307
+ }
9308
+ function readAx(ref) {
9309
+ assertKind(ref, "snap", "readAx");
9310
+ return readJsonArtifactFile(ref, "ax.json");
9311
+ }
9312
+ function readQueries(ref) {
9313
+ assertKind(ref, "snap", "readQueries");
9314
+ return readJsonArtifactFile(ref, "queries.json");
9315
+ }
9316
+ function readFocus(ref) {
9317
+ assertKind(ref, "snap", "readFocus");
9318
+ return readJsonArtifactFile(ref, "focus.json");
9319
+ }
9320
+ function readScroll(ref) {
9321
+ assertKind(ref, "snap", "readScroll");
9322
+ return readJsonArtifactFile(ref, "scroll.json");
9323
+ }
9324
+ function readLayers(ref) {
9325
+ assertKind(ref, "snap", "readLayers");
9326
+ return readJsonArtifactFile(ref, "layers.json");
9327
+ }
9328
+ function readStates(ref) {
9329
+ assertKind(ref, "snap", "readStates");
9330
+ return readJsonArtifactFile(ref, "states.json");
9331
+ }
9332
+ function readPixels(ref) {
9333
+ assertKind(ref, "snap", "readPixels");
9334
+ return readJsonArtifactFile(ref, "pixels.json");
9335
+ }
9336
+ function readMedia(ref) {
9337
+ assertKind(ref, "snap", "readMedia");
9338
+ return readJsonArtifactFile(ref, "media.json");
9339
+ }
9340
+ function readRects(ref) {
9341
+ assertKind(ref, "rec", "readRects");
9342
+ return readNdjsonArtifactFile(ref, "rects.jsonl");
9343
+ }
9344
+ function readEvents(ref) {
9345
+ assertKind(ref, "rec", "readEvents");
9346
+ return readNdjsonArtifactFile(ref, "events.jsonl");
9347
+ }
9348
+ function readMarkers(ref) {
9349
+ assertKind(ref, "rec", "readMarkers");
9350
+ return readJsonArtifactFile(ref, "markers.json");
9351
+ }
9352
+ function unstableRegionsFor(ref) {
9353
+ const geometry = readGeometry(ref);
9354
+ return geometry.unstableRegions ?? [];
9355
+ }
9356
+ function rectsOverlap(a, b) {
9357
+ return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
9358
+ }
9359
+ function annotateUnstableFacts(facts, regions) {
9360
+ return facts.map((fact4) => {
9361
+ const caveats = [];
9362
+ for (const region of regions) {
9363
+ const byElement = fact4.elementId !== void 0 && (region.elementIds?.includes(fact4.elementId) ?? false);
9364
+ const byRect = fact4.rect !== void 0 && region.rect !== void 0 && rectsOverlap(fact4.rect, region.rect);
9365
+ if (byElement || byRect) {
9366
+ caveats.push({ regionId: region.id, selector: region.selector, reason: region.reason });
9367
+ }
9368
+ }
9369
+ return { fact: fact4, caveats };
9370
+ });
9371
+ }
9372
+ var fs5, path5, ArtifactResolutionError, SNAP_CONFIG, REC_CONFIG, TRACE_CONFIG, CREATING_COMMAND_BY_FILE;
9373
+ var init_artifact = __esm({
9374
+ "src/output/artifact.ts"() {
9375
+ fs5 = __toESM(require("fs"));
9376
+ path5 = __toESM(require("path"));
9377
+ init_artifacts();
9378
+ init_session_context();
9379
+ ArtifactResolutionError = class extends Error {
9380
+ ref;
9381
+ searched;
9382
+ creatingCommand;
9383
+ constructor(ref, searched, detail, creatingCommand) {
9384
+ const parts = [`could not resolve ref ${JSON.stringify(ref)}: ${detail}`];
9385
+ if (searched.length) parts.push(`looked at: ${searched.join(", ")}`);
9386
+ if (creatingCommand) parts.push(`create it with: ${creatingCommand}`);
9387
+ super(parts.join(" \u2014 "));
9388
+ this.name = "ArtifactResolutionError";
9389
+ this.ref = ref;
9390
+ this.searched = searched;
9391
+ this.creatingCommand = creatingCommand;
9392
+ }
9393
+ };
9394
+ SNAP_CONFIG = {
9395
+ kind: "snap",
9396
+ idSegments: ["measure", "snaps"],
9397
+ creatingCommand: "capture measure snap",
9398
+ label: "snapshot"
9399
+ };
9400
+ REC_CONFIG = {
9401
+ kind: "rec",
9402
+ idSegments: ["motion", "recs"],
9403
+ creatingCommand: "capture motion rec",
9404
+ label: "recording"
9405
+ };
9406
+ TRACE_CONFIG = {
9407
+ kind: "trace",
9408
+ idSegments: ["perf", "traces"],
9409
+ creatingCommand: "capture perf trace",
9410
+ label: "trace"
9411
+ };
9412
+ CREATING_COMMAND_BY_FILE = {
9413
+ "geometry.json": "capture measure snap",
9414
+ "styles.json": "capture measure snap",
9415
+ "hittest.json": "capture measure snap",
9416
+ "text.json": "capture measure snap",
9417
+ "forms.json": "capture measure snap",
9418
+ "animation.json": "capture measure snap",
9419
+ "ax.json": "capture measure snap",
9420
+ "media.json": "capture measure snap",
9421
+ "queries.json": "capture measure snap",
9422
+ "focus.json": "capture measure snap",
9423
+ "scroll.json": "capture measure snap",
9424
+ "layers.json": "capture measure snap",
9425
+ "states.json": "capture measure snap --state <state>",
9426
+ "churn.json": "capture measure snap (written only when the page did not settle)",
9427
+ "pixels.json": "capture measure snap --pixels",
9428
+ "dom.html": "capture measure snap",
9429
+ "screenshot.png": "capture measure snap",
9430
+ "meta.json": "capture measure snap (or capture motion rec for a recording)",
9431
+ "rects.jsonl": "capture motion rec",
9432
+ "events.jsonl": "capture motion rec",
9433
+ "markers.json": "capture motion rec",
9434
+ "trace.json": "capture perf trace"
9435
+ };
9436
+ }
9437
+ });
9438
+
9098
9439
  // src/cdp/leaf-grammar.ts
9099
9440
  function isParseableUrl(value) {
9100
9441
  try {
@@ -9793,6 +10134,36 @@ function needsEndpointResolution(parsed) {
9793
10134
  if (parsed.command === "tab" && (parsed.positional[0] === "launch" || parsed.positional[0] === "quit")) return false;
9794
10135
  return true;
9795
10136
  }
10137
+ function mayReachBrowser(parsed) {
10138
+ const [leaf, sub] = parsed.positional;
10139
+ switch (parsed.command) {
10140
+ case "tab":
10141
+ return leaf !== "list";
10142
+ case "motion":
10143
+ return leaf === "rec";
10144
+ case "perf":
10145
+ return leaf === "trace";
10146
+ case "heap":
10147
+ return leaf === "snapshot";
10148
+ case "measure": {
10149
+ if (leaf === "diff" || leaf === "explain" || leaf === "text" || leaf === "map" && sub === "paint") return false;
10150
+ if (leaf === "check" || leaf === "census" || leaf === "map") return parsed.url !== void 0 || parsed.positional.some(isUrlRef);
10151
+ return true;
10152
+ }
10153
+ default:
10154
+ return true;
10155
+ }
10156
+ }
10157
+ function refuseUnownedEndpoint(parsed, sessionStart) {
10158
+ if (!process.env.CRTR_NODE_ID?.trim() || sessionStart || !mayReachBrowser(parsed)) return;
10159
+ if (parsed.port !== void 0 || parsed.targetSource === "flag" || parsed.targetSource === "session") return;
10160
+ const command = leafCommand(parsed.command, parsed.positional);
10161
+ throw captureError(
10162
+ "precondition",
10163
+ "no_owned_browser",
10164
+ `\`capture ${command}\` was not run: this node (CRTR_NODE_ID) has no browser of its own. With no --port, no --target and no active session holding a browser, capture will not use CDP_PORT, CDP_TARGET or auto-discovery, because the browser it found could belong to another node. Do one of: (1) \`capture session start --url <url>\` to open and hold your own browser tab, or \`capture tab launch\` then \`capture session start --port <port>\`; (2) pass \`--port <port>\` (see \`capture tab list\`); (3) pass \`--target <tabId>\`.`
10165
+ );
10166
+ }
9796
10167
  function resolveCliContext(parsed) {
9797
10168
  if (!needsEndpointResolution(parsed)) return parsed;
9798
10169
  const sessionStart = parsed.command === "session" && parsed.positional[0] === "start";
@@ -9808,6 +10179,7 @@ function resolveCliContext(parsed) {
9808
10179
  parsed.portSource = "session";
9809
10180
  }
9810
10181
  }
10182
+ refuseUnownedEndpoint(parsed, sessionStart);
9811
10183
  if (parsed.port === void 0 && process.env.CDP_PORT !== void 0) {
9812
10184
  parsed.port = schemaInteger(leafCommand(parsed.command, parsed.positional), process.env.CDP_PORT, "CDP_PORT", 1, 65535);
9813
10185
  parsed.portSource = "env";
@@ -9824,6 +10196,7 @@ var init_args = __esm({
9824
10196
  "src/cdp/args.ts"() {
9825
10197
  init_errors();
9826
10198
  init_session_context();
10199
+ init_artifact();
9827
10200
  init_leaf_grammar();
9828
10201
  VALUE_FLAGS = /* @__PURE__ */ new Set([
9829
10202
  "--port",
@@ -9909,7 +10282,7 @@ var init_gate_guard = __esm({
9909
10282
 
9910
10283
  // src/session/coordinator.ts
9911
10284
  function scopeLifecycleLockPath() {
9912
- return path5.join(CAPTURE_ROOT, `.session-lifecycle-${process.env.CRTR_NODE_ID ?? "default"}`);
10285
+ return path6.join(CAPTURE_ROOT, `.session-lifecycle-${process.env.CRTR_NODE_ID ?? "default"}`);
9913
10286
  }
9914
10287
  async function withSessionScopeLifecycle(action) {
9915
10288
  const lock = await acquirePrivateLock(scopeLifecycleLockPath(), {
@@ -9923,10 +10296,10 @@ async function withSessionScopeLifecycle(action) {
9923
10296
  }
9924
10297
  }
9925
10298
  function statePath(sessionDir2) {
9926
- return path5.join(assertUnderCaptureRoot(sessionDir2), ".operations.json");
10299
+ return path6.join(assertUnderCaptureRoot(sessionDir2), ".operations.json");
9927
10300
  }
9928
10301
  function lockPath(sessionDir2) {
9929
- return path5.join(assertUnderCaptureRoot(sessionDir2), ".operations.lock");
10302
+ return path6.join(assertUnderCaptureRoot(sessionDir2), ".operations.lock");
9930
10303
  }
9931
10304
  function isBirth(value) {
9932
10305
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
@@ -9985,7 +10358,7 @@ async function admitSessionOperation(sessionDir2) {
9985
10358
  };
9986
10359
  }
9987
10360
  async function beginSessionStop(sessionDir2) {
9988
- const stopLock = await acquirePrivateLock(path5.join(assertUnderCaptureRoot(sessionDir2), ".stop.lock"), {
10361
+ const stopLock = await acquirePrivateLock(path6.join(assertUnderCaptureRoot(sessionDir2), ".stop.lock"), {
9989
10362
  acquireTimeoutMs: 12e4,
9990
10363
  leaseMs: 1e3
9991
10364
  });
@@ -10025,7 +10398,7 @@ async function beginSessionStop(sessionDir2) {
10025
10398
  };
10026
10399
  }
10027
10400
  async function withSessionLifecycle(sessionDir2, action, seams2 = {}) {
10028
- const lock = await acquirePrivateLock(path5.join(assertUnderCaptureRoot(sessionDir2), ".lifecycle.lock"), {
10401
+ const lock = await acquirePrivateLock(path6.join(assertUnderCaptureRoot(sessionDir2), ".lifecycle.lock"), {
10029
10402
  acquireTimeoutMs: seams2.acquireTimeoutMs ?? 3e4,
10030
10403
  leaseMs: seams2.leaseMs ?? 3e4,
10031
10404
  pidBirthProvider: seams2.pidBirthProvider,
@@ -10039,11 +10412,11 @@ async function withSessionLifecycle(sessionDir2, action, seams2 = {}) {
10039
10412
  lock.release();
10040
10413
  }
10041
10414
  }
10042
- var crypto3, path5;
10415
+ var crypto3, path6;
10043
10416
  var init_coordinator = __esm({
10044
10417
  "src/session/coordinator.ts"() {
10045
10418
  crypto3 = __toESM(require("node:crypto"));
10046
- path5 = __toESM(require("node:path"));
10419
+ path6 = __toESM(require("node:path"));
10047
10420
  init_errors();
10048
10421
  init_artifacts();
10049
10422
  }
@@ -10571,7 +10944,7 @@ function validateHarAppendBatch(value, source) {
10571
10944
  return { entries, incompleteLifecycles };
10572
10945
  }
10573
10946
  function captureVersion() {
10574
- const version = "1.4.22";
10947
+ const version = "1.4.23";
10575
10948
  return typeof version === "string" && version.length > 0 ? version : "development";
10576
10949
  }
10577
10950
  function token() {
@@ -10589,15 +10962,15 @@ function vStoreHeader(value, source) {
10589
10962
  return { format: "capture-live-har-ndjson", version: 1, creator: { name: "capture", version } };
10590
10963
  }
10591
10964
  function harFilePath(id) {
10592
- if (path6.isAbsolute(id)) return assertUnderCaptureRoot(id);
10593
- if (path6.basename(id) === id) return assertUnderCaptureRoot(path6.resolve(CAPTURE_ROOT, id));
10594
- if (id.endsWith(".json") && id.includes(path6.sep)) return assertUnderCaptureRoot(id);
10595
- return assertUnderCaptureRoot(path6.resolve(id));
10965
+ if (path7.isAbsolute(id)) return assertUnderCaptureRoot(id);
10966
+ if (path7.basename(id) === id) return assertUnderCaptureRoot(path7.resolve(CAPTURE_ROOT, id));
10967
+ if (id.endsWith(".json") && id.includes(path7.sep)) return assertUnderCaptureRoot(id);
10968
+ return assertUnderCaptureRoot(path7.resolve(id));
10596
10969
  }
10597
10970
  async function createHarRecording(sessionDir2 = CAPTURE_ROOT) {
10598
- const sessionAbsolute = path6.resolve(sessionDir2);
10971
+ const sessionAbsolute = path7.resolve(sessionDir2);
10599
10972
  assertUnderCaptureRoot(sessionAbsolute);
10600
- const harDir = path6.join(sessionAbsolute, ".har");
10973
+ const harDir = path7.join(sessionAbsolute, ".har");
10601
10974
  ensurePrivateDir(harDir);
10602
10975
  const header = {
10603
10976
  format: "capture-live-har-ndjson",
@@ -10607,7 +10980,7 @@ async function createHarRecording(sessionDir2 = CAPTURE_ROOT) {
10607
10980
  const headerLine = `${JSON.stringify(header)}
10608
10981
  `;
10609
10982
  for (; ; ) {
10610
- const candidate = path6.join(harDir, `${token()}.json`);
10983
+ const candidate = path7.join(harDir, `${token()}.json`);
10611
10984
  try {
10612
10985
  createPrivateFile(candidate, headerLine);
10613
10986
  } catch (error4) {
@@ -10670,39 +11043,39 @@ async function appendToHarRecording(id, batch) {
10670
11043
  async function deleteHarRecording(id) {
10671
11044
  unlinkPrivateFile(harFilePath(id));
10672
11045
  }
10673
- var import_node_crypto, path6, MAX_BODY_BYTES, HAR_DIR;
11046
+ var import_node_crypto, path7, MAX_BODY_BYTES, HAR_DIR;
10674
11047
  var init_har_manager = __esm({
10675
11048
  "src/har-manager.ts"() {
10676
11049
  import_node_crypto = __toESM(require("node:crypto"));
10677
- path6 = __toESM(require("path"));
11050
+ path7 = __toESM(require("path"));
10678
11051
  init_artifacts();
10679
11052
  MAX_BODY_BYTES = 262144;
10680
- HAR_DIR = path6.join(CAPTURE_ROOT, ".har");
11053
+ HAR_DIR = path7.join(CAPTURE_ROOT, ".har");
10681
11054
  }
10682
11055
  });
10683
11056
 
10684
11057
  // src/cdp/bridge/spawn.ts
10685
11058
  function bridgeSocketPath(sessionDir2) {
10686
- return path7.join(sessionDir2, "bridge.sock");
11059
+ return path8.join(sessionDir2, "bridge.sock");
10687
11060
  }
10688
11061
  function collectorSocketDir() {
10689
11062
  const dir = "/tmp/capture-sockets";
10690
11063
  try {
10691
- fs5.mkdirSync(dir, { mode: 448 });
11064
+ fs6.mkdirSync(dir, { mode: 448 });
10692
11065
  } catch (error4) {
10693
11066
  if (error4.code !== "EEXIST") throw error4;
10694
11067
  }
10695
- const stat = fs5.lstatSync(dir);
11068
+ const stat = fs6.lstatSync(dir);
10696
11069
  if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error(`collector socket directory is not a real directory: ${dir}`);
10697
11070
  if (typeof process.getuid === "function" && stat.uid !== process.getuid()) throw new Error(`collector socket directory is not owned by this user: ${dir}`);
10698
- fs5.chmodSync(dir, 448);
11071
+ fs6.chmodSync(dir, 448);
10699
11072
  return dir;
10700
11073
  }
10701
11074
  function shortSocketName(scopeDir) {
10702
- return crypto5.createHash("sha1").update(path7.resolve(scopeDir)).digest("hex").slice(0, 16);
11075
+ return crypto5.createHash("sha1").update(path8.resolve(scopeDir)).digest("hex").slice(0, 16);
10703
11076
  }
10704
11077
  function collectorHostSocketPath(sessionDir2) {
10705
- return path7.join(collectorSocketDir(), `host-${shortSocketName(sessionDir2)}.sock`);
11078
+ return path8.join(collectorSocketDir(), `host-${shortSocketName(sessionDir2)}.sock`);
10706
11079
  }
10707
11080
  async function startBridge(sessionDir2, port, timeoutMs = 5e3) {
10708
11081
  const socketPath = bridgeSocketPath(sessionDir2);
@@ -10718,7 +11091,7 @@ async function startBridge(sessionDir2, port, timeoutMs = 5e3) {
10718
11091
  throw new Error("Failed to spawn CDP bridge process.");
10719
11092
  }
10720
11093
  const start2 = Date.now();
10721
- while (!fs5.existsSync(socketPath)) {
11094
+ while (!fs6.existsSync(socketPath)) {
10722
11095
  if (Date.now() - start2 > timeoutMs) {
10723
11096
  stopBridge(pid, socketPath);
10724
11097
  throw new Error(
@@ -10734,7 +11107,7 @@ async function startCollectorHost(socketPath, port, targetId, sessionDir2, timeo
10734
11107
  child.unref();
10735
11108
  if (!child.pid) throw new Error("Failed to spawn collector host process.");
10736
11109
  const started = Date.now();
10737
- while (!fs5.existsSync(socketPath)) {
11110
+ while (!fs6.existsSync(socketPath)) {
10738
11111
  if (Date.now() - started > timeoutMs) {
10739
11112
  stopBridge(child.pid, socketPath);
10740
11113
  throw new Error(`Collector host (pid ${child.pid}) did not come up within ${timeoutMs}ms.`);
@@ -10752,18 +11125,18 @@ function stopBridge(pid, socketPath) {
10752
11125
  }
10753
11126
  if (socketPath) {
10754
11127
  try {
10755
- if (fs5.lstatSync(socketPath).isSocket()) fs5.unlinkSync(socketPath);
11128
+ if (fs6.lstatSync(socketPath).isSocket()) fs6.unlinkSync(socketPath);
10756
11129
  } catch {
10757
11130
  }
10758
11131
  }
10759
11132
  }
10760
- var import_child_process2, crypto5, fs5, path7;
11133
+ var import_child_process2, crypto5, fs6, path8;
10761
11134
  var init_spawn = __esm({
10762
11135
  "src/cdp/bridge/spawn.ts"() {
10763
11136
  import_child_process2 = require("child_process");
10764
11137
  crypto5 = __toESM(require("crypto"));
10765
- fs5 = __toESM(require("fs"));
10766
- path7 = __toESM(require("path"));
11138
+ fs6 = __toESM(require("fs"));
11139
+ path8 = __toESM(require("path"));
10767
11140
  }
10768
11141
  });
10769
11142
 
@@ -10783,7 +11156,7 @@ function socketDir(create) {
10783
11156
  }
10784
11157
  function socketPathForToken(token2, createDir) {
10785
11158
  if (!LOG_TAILER_SOCKET_TOKEN.test(token2)) throw new Error("invalid log tailer socket token");
10786
- return path8.join(socketDir(createDir), `${token2}.sock`);
11159
+ return path9.join(socketDir(createDir), `${token2}.sock`);
10787
11160
  }
10788
11161
  function requireRegisteredLogTailer(value) {
10789
11162
  const record5 = parseRegisteredLogTailer(value);
@@ -10804,23 +11177,23 @@ function ownership(record5, provider) {
10804
11177
  return observed.status === "found" && sameBirth(observed.identity, record5.birth) ? "same" : "gone";
10805
11178
  }
10806
11179
  function socketIdentity(socketPath) {
10807
- const stat = fs6.lstatSync(socketPath, { bigint: true });
11180
+ const stat = fs7.lstatSync(socketPath, { bigint: true });
10808
11181
  if (!stat.isSocket()) throw new Error(`log tailer control path is not a socket: ${socketPath}`);
10809
11182
  return { dev: stat.dev.toString(), ino: stat.ino.toString() };
10810
11183
  }
10811
11184
  function unlinkRecordedSocket(record5) {
10812
11185
  let current;
10813
11186
  try {
10814
- current = fs6.lstatSync(record5.socketPath, { bigint: true });
11187
+ current = fs7.lstatSync(record5.socketPath, { bigint: true });
10815
11188
  } catch (error4) {
10816
11189
  if (error4.code === "ENOENT") return;
10817
11190
  throw error4;
10818
11191
  }
10819
11192
  if (!current.isSocket() || current.dev.toString() !== record5.socketDev || current.ino.toString() !== record5.socketIno) return;
10820
- fs6.unlinkSync(record5.socketPath);
11193
+ fs7.unlinkSync(record5.socketPath);
10821
11194
  }
10822
11195
  function closeOwnedFd(fd) {
10823
- if (fd !== void 0) fs6.closeSync(fd);
11196
+ if (fd !== void 0) fs7.closeSync(fd);
10824
11197
  return void 0;
10825
11198
  }
10826
11199
  function awaitChildExit(child, timeoutMs) {
@@ -10907,8 +11280,8 @@ function awaitReadiness(child, timeoutMs) {
10907
11280
  });
10908
11281
  }
10909
11282
  async function startSessionLogTailer(options) {
10910
- const logsDir = ensurePrivateDir(path8.join(options.sessionDir, "logs"));
10911
- const destPath = path8.join(logsDir, `${options.name}.log`);
11283
+ const logsDir = ensurePrivateDir(path9.join(options.sessionDir, "logs"));
11284
+ const destPath = path9.join(logsDir, `${options.name}.log`);
10912
11285
  const socketToken = crypto6.randomBytes(8).toString("hex");
10913
11286
  const socketPath = socketPathForToken(socketToken, true);
10914
11287
  const nonce = crypto6.randomBytes(24).toString("hex");
@@ -11133,7 +11506,7 @@ function nonceMatches(expected, candidate) {
11133
11506
  }
11134
11507
  function writeReadiness(message) {
11135
11508
  try {
11136
- fs6.writeSync(READINESS_FD, message);
11509
+ fs7.writeSync(READINESS_FD, message);
11137
11510
  return true;
11138
11511
  } catch {
11139
11512
  return false;
@@ -11263,7 +11636,7 @@ async function runLogTailer(argv) {
11263
11636
  const owned = ownedSocket;
11264
11637
  orphanCheck = setInterval(() => {
11265
11638
  try {
11266
- const current = fs6.lstatSync(ownedSocketPath, { bigint: true });
11639
+ const current = fs7.lstatSync(ownedSocketPath, { bigint: true });
11267
11640
  if (current.isSocket() && current.dev.toString() === owned.dev && current.ino.toString() === owned.ino) return;
11268
11641
  } catch (error4) {
11269
11642
  if (error4.code !== "ENOENT") return;
@@ -11284,7 +11657,7 @@ async function runLogTailer(argv) {
11284
11657
  });
11285
11658
  reader.on("line", (line4) => {
11286
11659
  try {
11287
- fs6.writeSync(1, `${(/* @__PURE__ */ new Date()).toISOString()} ${line4}
11660
+ fs7.writeSync(1, `${(/* @__PURE__ */ new Date()).toISOString()} ${line4}
11288
11661
  `);
11289
11662
  } catch {
11290
11663
  requestStop?.("fatal");
@@ -11301,7 +11674,7 @@ async function runLogTailer(argv) {
11301
11674
  if (!writeReadiness("ready\n")) throw new Error("parent closed the readiness channel");
11302
11675
  ready = true;
11303
11676
  try {
11304
- fs6.closeSync(READINESS_FD);
11677
+ fs7.closeSync(READINESS_FD);
11305
11678
  } catch {
11306
11679
  }
11307
11680
  confirmDeadline = setTimeout(() => {
@@ -11336,7 +11709,7 @@ async function runLogTailer(argv) {
11336
11709
  if (socketPath && ownedSocket) {
11337
11710
  try {
11338
11711
  const current = socketIdentity(socketPath);
11339
- if (current.dev === ownedSocket.dev && current.ino === ownedSocket.ino) fs6.unlinkSync(socketPath);
11712
+ if (current.dev === ownedSocket.dev && current.ino === ownedSocket.ino) fs7.unlinkSync(socketPath);
11340
11713
  } catch (error4) {
11341
11714
  if (error4.code !== "ENOENT") exitCode = 1;
11342
11715
  }
@@ -11344,14 +11717,14 @@ async function runLogTailer(argv) {
11344
11717
  process.exitCode = exitCode;
11345
11718
  }
11346
11719
  }
11347
- var import_node_child_process3, crypto6, fs6, net2, path8, readline, READINESS_FD, WORKER_MS, CONTROL_MESSAGE_LIMIT, WORKER_CHILD_EXIT_TIMEOUT_MS, productionWorld, world;
11720
+ var import_node_child_process3, crypto6, fs7, net2, path9, readline, READINESS_FD, WORKER_MS, CONTROL_MESSAGE_LIMIT, WORKER_CHILD_EXIT_TIMEOUT_MS, productionWorld, world;
11348
11721
  var init_log_tailer = __esm({
11349
11722
  "src/session/log-tailer.ts"() {
11350
11723
  import_node_child_process3 = require("node:child_process");
11351
11724
  crypto6 = __toESM(require("node:crypto"));
11352
- fs6 = __toESM(require("node:fs"));
11725
+ fs7 = __toESM(require("node:fs"));
11353
11726
  net2 = __toESM(require("node:net"));
11354
- path8 = __toESM(require("node:path"));
11727
+ path9 = __toESM(require("node:path"));
11355
11728
  readline = __toESM(require("node:readline"));
11356
11729
  init_errors();
11357
11730
  init_artifacts();
@@ -11373,16 +11746,16 @@ var init_log_tailer = __esm({
11373
11746
 
11374
11747
  // src/cdp/host/collectors/heap.ts
11375
11748
  function reconstructHeapSnapshot(dir) {
11376
- const snapshot = path9.join(dir, "snapshot.heapsnapshot");
11377
- if (!fs7.existsSync(snapshot)) return { summary: { bytes: 0 }, files: [] };
11378
- const bytes = fs7.statSync(snapshot).size;
11749
+ const snapshot = path10.join(dir, "snapshot.heapsnapshot");
11750
+ if (!fs8.existsSync(snapshot)) return { summary: { bytes: 0 }, files: [] };
11751
+ const bytes = fs8.statSync(snapshot).size;
11379
11752
  return { summary: { bytes }, files: [{ name: "snapshot.heapsnapshot", bytes }] };
11380
11753
  }
11381
- var fs7, path9, HeapCollector;
11754
+ var fs8, path10, HeapCollector;
11382
11755
  var init_heap = __esm({
11383
11756
  "src/cdp/host/collectors/heap.ts"() {
11384
- fs7 = __toESM(require("node:fs"));
11385
- path9 = __toESM(require("node:path"));
11757
+ fs8 = __toESM(require("node:fs"));
11758
+ path10 = __toESM(require("node:path"));
11386
11759
  HeapCollector = class {
11387
11760
  kind = "heap";
11388
11761
  claims = ["heap-snapshot"];
@@ -11571,10 +11944,10 @@ function matchRule(rules, request) {
11571
11944
  function headerEntries(headers2) {
11572
11945
  return Object.entries(headers2).map(([name, value]) => ({ name, value }));
11573
11946
  }
11574
- var path10, MAX_BODY_BYTES2, RESOURCE_TYPES, FAIL_REASONS, InterceptCollector;
11947
+ var path11, MAX_BODY_BYTES2, RESOURCE_TYPES, FAIL_REASONS, InterceptCollector;
11575
11948
  var init_intercept = __esm({
11576
11949
  "src/cdp/host/collectors/intercept.ts"() {
11577
- path10 = __toESM(require("node:path"));
11950
+ path11 = __toESM(require("node:path"));
11578
11951
  init_artifacts();
11579
11952
  MAX_BODY_BYTES2 = 10 * 1024 * 1024;
11580
11953
  RESOURCE_TYPES = /* @__PURE__ */ new Set(["Document", "Stylesheet", "Image", "Media", "Font", "Script", "TextTrack", "XHR", "Fetch", "Prefetch", "EventSource", "WebSocket", "Manifest", "SignedExchange", "Ping", "CSPViolationReport", "Preflight", "Other"]);
@@ -11607,8 +11980,8 @@ var init_intercept = __esm({
11607
11980
  if (!config3) throw new Error("intercept collector has no rule configuration");
11608
11981
  this.context = context;
11609
11982
  this.ruleMatches = Array.from({ length: config3.rules.length }, () => 0);
11610
- writePrivateFile(path10.join(context.dir, "rules.json"), config3.source);
11611
- writePrivateFile(path10.join(context.dir, "interceptions.jsonl"), "");
11983
+ writePrivateFile(path11.join(context.dir, "rules.json"), config3.source);
11984
+ writePrivateFile(path11.join(context.dir, "interceptions.jsonl"), "");
11612
11985
  context.client.on("Fetch.requestPaused", this.onPaused);
11613
11986
  try {
11614
11987
  await context.client.send("Fetch.enable", { patterns: [{ urlPattern: "*", requestStage: "Request" }] });
@@ -11816,10 +12189,10 @@ async function runBridgeServer(socketPath, port) {
11816
12189
  installProcessCleanup(cleanup, client);
11817
12190
  }
11818
12191
  function prepareSocketPath(socketPath) {
11819
- fs8.mkdirSync(path11.dirname(socketPath), { recursive: true });
12192
+ fs9.mkdirSync(path12.dirname(socketPath), { recursive: true });
11820
12193
  try {
11821
- if (!fs8.lstatSync(socketPath).isSocket()) throw new Error(`socket path is not a Unix socket: ${socketPath}`);
11822
- fs8.unlinkSync(socketPath);
12194
+ if (!fs9.lstatSync(socketPath).isSocket()) throw new Error(`socket path is not a Unix socket: ${socketPath}`);
12195
+ fs9.unlinkSync(socketPath);
11823
12196
  } catch (error4) {
11824
12197
  if (error4.code !== "ENOENT") throw error4;
11825
12198
  }
@@ -11850,7 +12223,7 @@ function closeNdjsonSocket(server2, socketPath) {
11850
12223
  } catch {
11851
12224
  }
11852
12225
  try {
11853
- if (fs8.lstatSync(socketPath).isSocket()) fs8.unlinkSync(socketPath);
12226
+ if (fs9.lstatSync(socketPath).isSocket()) fs9.unlinkSync(socketPath);
11854
12227
  } catch {
11855
12228
  }
11856
12229
  }
@@ -11873,12 +12246,12 @@ async function resolvePort(port) {
11873
12246
  const { detectCdpPort: detectCdpPort2 } = await Promise.resolve().then(() => (init_detect(), detect_exports));
11874
12247
  return detectCdpPort2();
11875
12248
  }
11876
- var fs8, net3, path11, EventBroker;
12249
+ var fs9, net3, path12, EventBroker;
11877
12250
  var init_server = __esm({
11878
12251
  "src/cdp/bridge/server.ts"() {
11879
- fs8 = __toESM(require("fs"));
12252
+ fs9 = __toESM(require("fs"));
11880
12253
  net3 = __toESM(require("net"));
11881
- path11 = __toESM(require("path"));
12254
+ path12 = __toESM(require("path"));
11882
12255
  init_targets();
11883
12256
  EventBroker = class {
11884
12257
  constructor(client) {
@@ -12673,7 +13046,7 @@ function wallTime(value) {
12673
13046
  }
12674
13047
  }
12675
13048
  function captureVersion2() {
12676
- const version = "1.4.22";
13049
+ const version = "1.4.23";
12677
13050
  return typeof version === "string" && version.length > 0 ? version : "development";
12678
13051
  }
12679
13052
  function deepFreeze(value) {
@@ -13429,11 +13802,11 @@ function buildTakeRectElementsExpression(nonce, frameIndex) {
13429
13802
  function buildTakeResizeTargetsExpression(nonce, seq) {
13430
13803
  return `(function(){var k='__captureRecorder_'+${JSON.stringify(nonce)};var h=window[k];return h&&h.takeResizeTargets?h.takeResizeTargets(${JSON.stringify(seq)}):[];})()`;
13431
13804
  }
13432
- var crypto7, path12, RECORDER_BINDING_NAME, OBSERVER_INSTALLED_SENTINEL, TRACE_CATEGORIES, SCREENCAST_OPTIONS, MAX_BINDING_PAYLOAD_BYTES, BINDING_RATE_LIMIT_PER_SECOND, MAX_RECT_ELEMENTS, MAX_RECT_GEOMETRY_CANDIDATES, MAX_RECT_TAG_LENGTH, MAX_RECT_STRING_LENGTH, MAX_RECTS_SERIALIZED_BYTES, MAX_RECT_IDENTITY_RESOLUTIONS, MAX_TRACE_EVENTS_PER_BATCH, MAX_TRACE_SERIALIZED_BYTES, BINDING_FIELD_SANITIZERS, RecorderSession, MotionCollector;
13805
+ var crypto7, path13, RECORDER_BINDING_NAME, OBSERVER_INSTALLED_SENTINEL, TRACE_CATEGORIES, SCREENCAST_OPTIONS, MAX_BINDING_PAYLOAD_BYTES, BINDING_RATE_LIMIT_PER_SECOND, MAX_RECT_ELEMENTS, MAX_RECT_GEOMETRY_CANDIDATES, MAX_RECT_TAG_LENGTH, MAX_RECT_STRING_LENGTH, MAX_RECTS_SERIALIZED_BYTES, MAX_RECT_IDENTITY_RESOLUTIONS, MAX_TRACE_EVENTS_PER_BATCH, MAX_TRACE_SERIALIZED_BYTES, BINDING_FIELD_SANITIZERS, RecorderSession, MotionCollector;
13433
13806
  var init_motion = __esm({
13434
13807
  "src/cdp/host/collectors/motion.ts"() {
13435
13808
  crypto7 = __toESM(require("crypto"));
13436
- path12 = __toESM(require("path"));
13809
+ path13 = __toESM(require("path"));
13437
13810
  init_domains();
13438
13811
  init_timing();
13439
13812
  init_server();
@@ -13543,9 +13916,9 @@ var init_motion = __esm({
13543
13916
  constructor(opts) {
13544
13917
  this.client = opts.client;
13545
13918
  this.recDir = ensurePrivateDir(opts.recDir);
13546
- this.framesDir = ensurePrivateDir(path12.join(this.recDir, "frames"));
13547
- this.eventsPath = path12.join(this.recDir, "events.jsonl");
13548
- this.rectsPath = path12.join(this.recDir, "rects.jsonl");
13919
+ this.framesDir = ensurePrivateDir(path13.join(this.recDir, "frames"));
13920
+ this.eventsPath = path13.join(this.recDir, "events.jsonl");
13921
+ this.rectsPath = path13.join(this.recDir, "rects.jsonl");
13549
13922
  this.events = new EventBroker(this.client);
13550
13923
  }
13551
13924
  async start() {
@@ -14006,7 +14379,7 @@ var init_motion = __esm({
14006
14379
  this.updateBaselinesPending();
14007
14380
  }
14008
14381
  const frameName = `frame-${String(frameIndex).padStart(6, "0")}.png`;
14009
- writeBinaryPrivate(path12.join(this.framesDir, frameName), Buffer.from(params.data, "base64"));
14382
+ writeBinaryPrivate(path13.join(this.framesDir, frameName), Buffer.from(params.data, "base64"));
14010
14383
  try {
14011
14384
  const sample = await this.sampleRects(frameIndex);
14012
14385
  const elements = this.sanitizeRectSample(sample.facts, sample.backendNodeIds);
@@ -14406,10 +14779,10 @@ var init_motion = __esm({
14406
14779
  if (config3.viewport) {
14407
14780
  const { width, height } = config3.viewport;
14408
14781
  if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height) || width <= 0 || height <= 0) throw new Error("motion viewport must contain positive integer width and height");
14409
- writeJsonPrivate(path12.join(context.dir, "viewport-override.json"), { phase: "attempting", targetId: context.targetId, retainOnStop: true });
14782
+ writeJsonPrivate(path13.join(context.dir, "viewport-override.json"), { phase: "attempting", targetId: context.targetId, retainOnStop: true });
14410
14783
  await context.client.send("Emulation.setDeviceMetricsOverride", { width, height, deviceScaleFactor: 1, mobile: false });
14411
14784
  this.viewportApplied = true;
14412
- writeJsonPrivate(path12.join(context.dir, "viewport-override.json"), { phase: "applied", targetId: context.targetId, retainOnStop: true });
14785
+ writeJsonPrivate(path13.join(context.dir, "viewport-override.json"), { phase: "applied", targetId: context.targetId, retainOnStop: true });
14413
14786
  }
14414
14787
  this.har = new HARRecorder(context.client, (batch) => appendToHarRecording(config3.harId, batch));
14415
14788
  await this.har.start();
@@ -14422,9 +14795,9 @@ var init_motion = __esm({
14422
14795
  try {
14423
14796
  await context.client.send("Emulation.clearDeviceMetricsOverride");
14424
14797
  this.viewportApplied = false;
14425
- removeArtifactTree(path12.join(context.dir, "viewport-override.json"));
14798
+ removeArtifactTree(path13.join(context.dir, "viewport-override.json"));
14426
14799
  } catch (cleanupError) {
14427
- writeJsonPrivate(path12.join(context.dir, "viewport-override.json"), {
14800
+ writeJsonPrivate(path13.join(context.dir, "viewport-override.json"), {
14428
14801
  phase: "restore-failed",
14429
14802
  targetId: context.targetId,
14430
14803
  retainOnStop: true,
@@ -14468,13 +14841,13 @@ var init_motion = __esm({
14468
14841
  if (this.viewportApplied && cause.trigger !== "explicit") {
14469
14842
  try {
14470
14843
  await this.context.client.send("Emulation.clearDeviceMetricsOverride");
14471
- removeArtifactTree(path12.join(this.context.dir, "viewport-override.json"));
14844
+ removeArtifactTree(path13.join(this.context.dir, "viewport-override.json"));
14472
14845
  viewportRestored = true;
14473
14846
  } catch {
14474
14847
  viewportRestored = false;
14475
14848
  }
14476
14849
  }
14477
- writeJsonPrivate(path12.join(this.context.dir, "markers.json"), stopped.markers);
14850
+ writeJsonPrivate(path13.join(this.context.dir, "markers.json"), stopped.markers);
14478
14851
  return { summary: { frames: stopped.frameCount, durationMs: stopped.durationMs, eventCount: stopped.eventCount, markers: stopped.markers, state: stopped.frameCount > 0 ? "finalized" : "partial", viewportRestored, viewportRetained: this.viewportApplied && cause.trigger === "explicit" }, files: [] };
14479
14852
  }
14480
14853
  onDispatch(notice) {
@@ -14593,26 +14966,26 @@ var init_trace = __esm({
14593
14966
 
14594
14967
  // src/cdp/host/kinds.ts
14595
14968
  function motionReconstruct(dir) {
14596
- const framesDir = path13.join(dir, "frames");
14597
- const frames2 = fs9.existsSync(framesDir) ? fs9.readdirSync(framesDir).filter((name) => name.endsWith(".png")).length : 0;
14969
+ const framesDir = path14.join(dir, "frames");
14970
+ const frames2 = fs10.existsSync(framesDir) ? fs10.readdirSync(framesDir).filter((name) => name.endsWith(".png")).length : 0;
14598
14971
  const bytes = (name) => {
14599
14972
  try {
14600
- return fs9.statSync(path13.join(dir, name)).size;
14973
+ return fs10.statSync(path14.join(dir, name)).size;
14601
14974
  } catch {
14602
14975
  return 0;
14603
14976
  }
14604
14977
  };
14605
- const files = ["events.jsonl", "rects.jsonl", "markers.json"].filter((name) => fs9.existsSync(path13.join(dir, name))).map((name) => ({ name, bytes: bytes(name) }));
14978
+ const files = ["events.jsonl", "rects.jsonl", "markers.json"].filter((name) => fs10.existsSync(path14.join(dir, name))).map((name) => ({ name, bytes: bytes(name) }));
14606
14979
  return { summary: { frames: frames2, state: "orphaned-finalized" }, files };
14607
14980
  }
14608
14981
  function collectorKind(kind) {
14609
14982
  return COLLECTOR_KINDS[kind];
14610
14983
  }
14611
- var fs9, path13, motion, trace, heap, intercept, COLLECTOR_KINDS;
14984
+ var fs10, path14, motion, trace, heap, intercept, COLLECTOR_KINDS;
14612
14985
  var init_kinds = __esm({
14613
14986
  "src/cdp/host/kinds.ts"() {
14614
- fs9 = __toESM(require("node:fs"));
14615
- path13 = __toESM(require("node:path"));
14987
+ fs10 = __toESM(require("node:fs"));
14988
+ path14 = __toESM(require("node:path"));
14616
14989
  init_heap();
14617
14990
  init_intercept();
14618
14991
  init_motion();
@@ -14646,8 +15019,8 @@ var init_kinds = __esm({
14646
15019
  return new TraceCollector();
14647
15020
  },
14648
15021
  reconstruct(dir) {
14649
- const file = path13.join(dir, "trace.json");
14650
- const bytes = fs9.existsSync(file) ? fs9.statSync(file).size : 0;
15022
+ const file = path14.join(dir, "trace.json");
15023
+ const bytes = fs10.existsSync(file) ? fs10.statSync(file).size : 0;
14651
15024
  return { summary: { events: 0, windowMs: 0, navigations: 0, categories: "devtools-default" }, files: bytes ? [{ name: "trace.json", bytes }] : [] };
14652
15025
  }
14653
15026
  };
@@ -14677,12 +15050,12 @@ var init_kinds = __esm({
14677
15050
  reconstruct(dir) {
14678
15051
  const bytes = (name) => {
14679
15052
  try {
14680
- return fs9.statSync(path13.join(dir, name)).size;
15053
+ return fs10.statSync(path14.join(dir, name)).size;
14681
15054
  } catch {
14682
15055
  return 0;
14683
15056
  }
14684
15057
  };
14685
- const files = ["rules.json", "interceptions.jsonl"].filter((name) => fs9.existsSync(path13.join(dir, name))).map((name) => ({ name, bytes: bytes(name) }));
15058
+ const files = ["rules.json", "interceptions.jsonl"].filter((name) => fs10.existsSync(path14.join(dir, name))).map((name) => ({ name, bytes: bytes(name) }));
14686
15059
  return { summary: { state: "orphaned-finalized" }, files };
14687
15060
  }
14688
15061
  };
@@ -14697,7 +15070,7 @@ var init_kinds = __esm({
14697
15070
 
14698
15071
  // src/cdp/host/handle.ts
14699
15072
  function collectorHostPath(sessionDir2) {
14700
- return path14.join(assertUnderCaptureRoot(sessionDir2), ".collector-host.json");
15073
+ return path15.join(assertUnderCaptureRoot(sessionDir2), ".collector-host.json");
14701
15074
  }
14702
15075
  function parseHandle(raw, sessionDir2) {
14703
15076
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
@@ -14721,7 +15094,7 @@ function scanCollectorHost(sessionDir2) {
14721
15094
  if (read.status === "unknown") return { classification: "unknown", handle, path: file };
14722
15095
  if (read.status === "found" && sameBirth(read.identity, handle.birth)) {
14723
15096
  try {
14724
- if (!fs10.lstatSync(handle.socketPath).isSocket()) return { classification: "malformed", handle: null, path: file };
15097
+ if (!fs11.lstatSync(handle.socketPath).isSocket()) return { classification: "malformed", handle: null, path: file };
14725
15098
  } catch {
14726
15099
  return { classification: "malformed", handle: null, path: file };
14727
15100
  }
@@ -14729,11 +15102,11 @@ function scanCollectorHost(sessionDir2) {
14729
15102
  }
14730
15103
  return { classification: "dead", handle, path: file };
14731
15104
  }
14732
- var fs10, path14;
15105
+ var fs11, path15;
14733
15106
  var init_handle = __esm({
14734
15107
  "src/cdp/host/handle.ts"() {
14735
- fs10 = __toESM(require("node:fs"));
14736
- path14 = __toESM(require("node:path"));
15108
+ fs11 = __toESM(require("node:fs"));
15109
+ path15 = __toESM(require("node:path"));
14737
15110
  init_spawn();
14738
15111
  init_artifacts();
14739
15112
  init_artifacts();
@@ -14746,7 +15119,7 @@ function isCollectorKind(value) {
14746
15119
  }
14747
15120
  function readCollecting(dir) {
14748
15121
  try {
14749
- const raw = JSON.parse(readPrivateFile(path15.join(dir, "collecting.json")).toString("utf8"));
15122
+ const raw = JSON.parse(readPrivateFile(path16.join(dir, "collecting.json")).toString("utf8"));
14750
15123
  if (typeof raw.id !== "string" || !isCollectorKind(raw.kind) || typeof raw.startedAt !== "string") return null;
14751
15124
  return { id: raw.id, kind: raw.kind, startedAt: raw.startedAt };
14752
15125
  } catch {
@@ -14758,12 +15131,12 @@ function collectingDirectories(root) {
14758
15131
  const walk2 = (dir) => {
14759
15132
  let entries;
14760
15133
  try {
14761
- entries = fs11.readdirSync(dir, { withFileTypes: true });
15134
+ entries = fs12.readdirSync(dir, { withFileTypes: true });
14762
15135
  } catch {
14763
15136
  return;
14764
15137
  }
14765
15138
  if (entries.some((entry) => entry.isFile() && entry.name === "collecting.json")) dirs.push(dir);
14766
- for (const entry of entries) if (entry.isDirectory()) walk2(path15.join(dir, entry.name));
15139
+ for (const entry of entries) if (entry.isDirectory()) walk2(path16.join(dir, entry.name));
14767
15140
  };
14768
15141
  walk2(root);
14769
15142
  return dirs;
@@ -14776,7 +15149,7 @@ function reapOrphanedCollectors(sessionDir2) {
14776
15149
  try {
14777
15150
  const reconstructed = collectorKind(collecting.kind).reconstruct(dir);
14778
15151
  const summary = reconstructed.summary && typeof reconstructed.summary === "object" && !Array.isArray(reconstructed.summary) ? reconstructed.summary : { value: reconstructed.summary };
14779
- writeJsonPrivate(path15.join(dir, "meta.json"), {
15152
+ writeJsonPrivate(path16.join(dir, "meta.json"), {
14780
15153
  id: collecting.id,
14781
15154
  kind: collecting.kind,
14782
15155
  completion: "orphaned",
@@ -14787,7 +15160,7 @@ function reapOrphanedCollectors(sessionDir2) {
14787
15160
  summary,
14788
15161
  ...summary
14789
15162
  });
14790
- unlinkPrivateFile(path15.join(dir, "collecting.json"));
15163
+ unlinkPrivateFile(path16.join(dir, "collecting.json"));
14791
15164
  outcomes.push({ status: "reaped", id: collecting.id, kind: collecting.kind, dir });
14792
15165
  } catch (error4) {
14793
15166
  outcomes.push({ status: "terminal", id: collecting.id, kind: collecting.kind, dir, error: error4 instanceof Error ? error4.message : String(error4) });
@@ -14833,11 +15206,11 @@ async function stopAndReapCollectorHostAtSessionStop(sessionDir2) {
14833
15206
  return terminal(error4 instanceof Error ? error4.message : String(error4));
14834
15207
  }
14835
15208
  }
14836
- var fs11, path15;
15209
+ var fs12, path16;
14837
15210
  var init_lifecycle = __esm({
14838
15211
  "src/cdp/host/lifecycle.ts"() {
14839
- fs11 = __toESM(require("node:fs"));
14840
- path15 = __toESM(require("node:path"));
15212
+ fs12 = __toESM(require("node:fs"));
15213
+ path16 = __toESM(require("node:path"));
14841
15214
  init_spawn();
14842
15215
  init_artifacts();
14843
15216
  init_kinds();
@@ -14992,20 +15365,20 @@ function readBundleManifest(bundlePath) {
14992
15365
  return parsed;
14993
15366
  }
14994
15367
  function sessionDir(id, artifactRoot = CAPTURE_ROOT) {
14995
- return path16.join(artifactRoot, id);
15368
+ return path17.join(artifactRoot, id);
14996
15369
  }
14997
15370
  function isSessionStopped(session) {
14998
- return Boolean(session.stoppedAt) || fs12.existsSync(path16.join(session.dir, "bundle.json"));
15371
+ return Boolean(session.stoppedAt) || fs13.existsSync(path17.join(session.dir, "bundle.json"));
14999
15372
  }
15000
15373
  function createOneshotSession(kind, artifactDir) {
15001
15374
  const id = `oneshot-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
15002
15375
  const dir = sessionDir(id, artifactDir === void 0 ? CAPTURE_ROOT : registerArtifactRoot(artifactDir));
15003
- const artifactsDir = kind === "measure" ? path16.join(dir, "measure", "snaps") : kind === "motion" ? path16.join(dir, "motion", "recs") : path16.join(dir, "page");
15376
+ const artifactsDir = kind === "measure" ? path17.join(dir, "measure", "snaps") : kind === "motion" ? path17.join(dir, "motion", "recs") : path17.join(dir, "page");
15004
15377
  ensurePrivateDir(artifactsDir);
15005
15378
  return { id, dir, kind, artifactsDir };
15006
15379
  }
15007
15380
  function readSnapMeta(snapDir, fallbackId) {
15008
- const meta = JSON.parse(fs12.readFileSync(path16.join(snapDir, "meta.json"), "utf-8"));
15381
+ const meta = JSON.parse(fs13.readFileSync(path17.join(snapDir, "meta.json"), "utf-8"));
15009
15382
  return {
15010
15383
  id: meta.id ?? fallbackId,
15011
15384
  path: snapDir,
@@ -15016,7 +15389,7 @@ function readSnapMeta(snapDir, fallbackId) {
15016
15389
  };
15017
15390
  }
15018
15391
  function readRecMeta(recDir, fallbackId) {
15019
- const meta = JSON.parse(fs12.readFileSync(path16.join(recDir, "meta.json"), "utf-8"));
15392
+ const meta = JSON.parse(fs13.readFileSync(path17.join(recDir, "meta.json"), "utf-8"));
15020
15393
  return {
15021
15394
  id: meta.id ?? fallbackId,
15022
15395
  path: recDir,
@@ -15028,40 +15401,40 @@ function readRecMeta(recDir, fallbackId) {
15028
15401
  };
15029
15402
  }
15030
15403
  function collectSnaps(dir) {
15031
- const snapsRoot = path16.join(dir, "measure", "snaps");
15032
- if (!fs12.existsSync(snapsRoot)) return [];
15033
- return fs12.readdirSync(snapsRoot).filter((name) => fs12.existsSync(path16.join(snapsRoot, name, "meta.json"))).map((name) => readSnapMeta(path16.join(snapsRoot, name), name));
15404
+ const snapsRoot = path17.join(dir, "measure", "snaps");
15405
+ if (!fs13.existsSync(snapsRoot)) return [];
15406
+ return fs13.readdirSync(snapsRoot).filter((name) => fs13.existsSync(path17.join(snapsRoot, name, "meta.json"))).map((name) => readSnapMeta(path17.join(snapsRoot, name), name));
15034
15407
  }
15035
15408
  function collectRecs(dir) {
15036
- const recsRoot = path16.join(dir, "motion", "recs");
15037
- if (!fs12.existsSync(recsRoot)) return [];
15038
- return fs12.readdirSync(recsRoot).filter((name) => fs12.existsSync(path16.join(recsRoot, name, "meta.json"))).map((name) => readRecMeta(path16.join(recsRoot, name), name));
15409
+ const recsRoot = path17.join(dir, "motion", "recs");
15410
+ if (!fs13.existsSync(recsRoot)) return [];
15411
+ return fs13.readdirSync(recsRoot).filter((name) => fs13.existsSync(path17.join(recsRoot, name, "meta.json"))).map((name) => readRecMeta(path17.join(recsRoot, name), name));
15039
15412
  }
15040
15413
  function collectMocks(dir) {
15041
- const mocksRoot = path16.join(dir, "network", "mocks");
15042
- if (!fs12.existsSync(mocksRoot)) return [];
15043
- return fs12.readdirSync(mocksRoot).flatMap((name) => {
15044
- const mockDir = path16.join(mocksRoot, name);
15045
- const metaPath = path16.join(mockDir, "meta.json");
15046
- if (!fs12.existsSync(metaPath)) return [];
15047
- const meta = JSON.parse(fs12.readFileSync(metaPath, "utf8"));
15414
+ const mocksRoot = path17.join(dir, "network", "mocks");
15415
+ if (!fs13.existsSync(mocksRoot)) return [];
15416
+ return fs13.readdirSync(mocksRoot).flatMap((name) => {
15417
+ const mockDir = path17.join(mocksRoot, name);
15418
+ const metaPath = path17.join(mockDir, "meta.json");
15419
+ if (!fs13.existsSync(metaPath)) return [];
15420
+ const meta = JSON.parse(fs13.readFileSync(metaPath, "utf8"));
15048
15421
  return [{ id: typeof meta.id === "string" ? meta.id : name, path: mockDir, completion: typeof meta.completion === "string" ? meta.completion : "partial", rules: typeof meta.rules === "number" ? meta.rules : 0, paused: typeof meta.paused === "number" ? meta.paused : 0, matched: typeof meta.matched === "number" ? meta.matched : 0, releasedUnmatched: typeof meta.releasedUnmatched === "number" ? meta.releasedUnmatched : 0 }];
15049
15422
  });
15050
15423
  }
15051
15424
  function readLighthouseReportMeta(reportDir, fallbackId) {
15052
- const meta = JSON.parse(fs12.readFileSync(path16.join(reportDir, "meta.json"), "utf-8"));
15425
+ const meta = JSON.parse(fs13.readFileSync(path17.join(reportDir, "meta.json"), "utf-8"));
15053
15426
  if (typeof meta.url !== "string" || typeof meta.finalUrl !== "string" || meta.preset !== "mobile" && meta.preset !== "desktop" || !Array.isArray(meta.categories) || !meta.categories.every((category) => typeof category === "string") || meta.lighthouse !== null && typeof meta.lighthouse !== "string" || meta.axe !== null && typeof meta.axe !== "string") {
15054
15427
  throw captureError("artifact", "invalid_lighthouse_report_metadata", `Lighthouse report metadata at ${reportDir} is invalid.`);
15055
15428
  }
15056
15429
  return { id: typeof meta.id === "string" ? meta.id : fallbackId, path: reportDir, url: meta.url, finalUrl: meta.finalUrl, preset: meta.preset, categories: meta.categories, lighthouse: meta.lighthouse, axe: meta.axe };
15057
15430
  }
15058
15431
  function collectLighthouseReports(dir) {
15059
- const reportsRoot = path16.join(dir, "lighthouse", "reports");
15060
- if (!fs12.existsSync(reportsRoot)) return [];
15061
- return fs12.readdirSync(reportsRoot).filter((name) => fs12.existsSync(path16.join(reportsRoot, name, "meta.json"))).map((name) => readLighthouseReportMeta(path16.join(reportsRoot, name), name));
15432
+ const reportsRoot = path17.join(dir, "lighthouse", "reports");
15433
+ if (!fs13.existsSync(reportsRoot)) return [];
15434
+ return fs13.readdirSync(reportsRoot).filter((name) => fs13.existsSync(path17.join(reportsRoot, name, "meta.json"))).map((name) => readLighthouseReportMeta(path17.join(reportsRoot, name), name));
15062
15435
  }
15063
15436
  function sessionMetaPath2(id, artifactRoot = CAPTURE_ROOT) {
15064
- return path16.join(sessionDir(id, artifactRoot), ".session.json");
15437
+ return path17.join(sessionDir(id, artifactRoot), ".session.json");
15065
15438
  }
15066
15439
  function tryReadSession(id, artifactRoot = CAPTURE_ROOT) {
15067
15440
  const metaPath = sessionMetaPath2(id, artifactRoot);
@@ -15183,7 +15556,7 @@ async function start(parsed) {
15183
15556
  let selectedBrowser = null;
15184
15557
  let endpointSelectionReason = null;
15185
15558
  try {
15186
- ensurePrivateDir(path16.join(dir, "shots"));
15559
+ ensurePrivateDir(path17.join(dir, "shots"));
15187
15560
  acquired.push({ label: "artifact tree", release: () => removeArtifactTree(dir) });
15188
15561
  harId = await startWorld.createHar(dir);
15189
15562
  const acquiredHarId = harId;
@@ -15323,8 +15696,8 @@ async function logTail(parsed) {
15323
15696
  process.exitCode = 1;
15324
15697
  return;
15325
15698
  }
15326
- const source = path16.resolve(sourcePath);
15327
- const name = parsed.name ?? path16.basename(source, path16.extname(source));
15699
+ const source = path17.resolve(sourcePath);
15700
+ const name = parsed.name ?? path17.basename(source, path17.extname(source));
15328
15701
  const labelReason = rejectLogLabel(name);
15329
15702
  if (labelReason) {
15330
15703
  const labelHint = parsed.name === void 0 && labelReason === `exceeds ${MAX_LOG_LABEL_BYTES} bytes` ? " Pass `--name <short>` to choose a shorter destination label." : "";
@@ -15336,7 +15709,7 @@ async function logTail(parsed) {
15336
15709
  process.exitCode = 1;
15337
15710
  return;
15338
15711
  }
15339
- if (!fs12.existsSync(source)) {
15712
+ if (!fs13.existsSync(source)) {
15340
15713
  emitResult({
15341
15714
  tag: "error",
15342
15715
  attrs: { command: "session log", code: "log_file_not_found" },
@@ -15420,9 +15793,9 @@ async function logTail(parsed) {
15420
15793
  }
15421
15794
  }
15422
15795
  async function locateSessionHar(session) {
15423
- const stopped = fs12.existsSync(path16.join(session.dir, "bundle.json"));
15796
+ const stopped = fs13.existsSync(path17.join(session.dir, "bundle.json"));
15424
15797
  if (stopped) {
15425
- const harPath = path16.join(session.dir, "har.json");
15798
+ const harPath = path17.join(session.dir, "har.json");
15426
15799
  let raw;
15427
15800
  try {
15428
15801
  raw = readPrivateFile(harPath).toString("utf-8");
@@ -15666,7 +16039,7 @@ function exportHarBody(id, located, parsed) {
15666
16039
  return true;
15667
16040
  }
15668
16041
  try {
15669
- fs12.writeFileSync(parsed.out, bytes);
16042
+ fs13.writeFileSync(parsed.out, bytes);
15670
16043
  } catch (cause) {
15671
16044
  emitResult({ tag: "error", attrs: { command: "session har", code: "body_write_failed" }, summary: fact`The selected response body was not written to ${parsed.out}: ${cause instanceof Error ? cause.message : String(cause)}` }, { json: parsed.json });
15672
16045
  process.exitCode = 1;
@@ -15830,7 +16203,7 @@ function bundleRows(manifest) {
15830
16203
  function emitStoppedFromManifest(id, sessionDir2, manifest, parsed) {
15831
16204
  emitResult({
15832
16205
  tag: "session-stopped",
15833
- attrs: { id, path: path16.join(sessionDir2, "bundle.json") },
16206
+ attrs: { id, path: path17.join(sessionDir2, "bundle.json") },
15834
16207
  summary: fact`Session ${id} stopped after ${manifest.duration}ms; bundle manifest written.`,
15835
16208
  sections: [lineList(bundleRows(manifest))],
15836
16209
  followUp: fact`capture session view ${id}`
@@ -15871,7 +16244,7 @@ async function stop(parsed) {
15871
16244
  let committed = false;
15872
16245
  try {
15873
16246
  session = readSession(id);
15874
- const bundlePath = path16.join(session.dir, "bundle.json");
16247
+ const bundlePath = path17.join(session.dir, "bundle.json");
15875
16248
  try {
15876
16249
  const manifest2 = readBundleManifest(bundlePath);
15877
16250
  if (session.harId) {
@@ -15900,19 +16273,19 @@ async function stop(parsed) {
15900
16273
  if (hostTeardown.status === "terminal") {
15901
16274
  throw new Error(`collector host teardown failed: ${hostTeardown.error}`);
15902
16275
  }
15903
- const shotsDir = path16.join(session.dir, "shots");
15904
- const shots = fs12.existsSync(shotsDir) ? fs12.readdirSync(shotsDir).filter((name) => name.endsWith(".png") || name.endsWith(".jpg")).map((name) => ({ name, path: path16.join(shotsDir, name) })) : [];
16276
+ const shotsDir = path17.join(session.dir, "shots");
16277
+ const shots = fs13.existsSync(shotsDir) ? fs13.readdirSync(shotsDir).filter((name) => name.endsWith(".png") || name.endsWith(".jpg")).map((name) => ({ name, path: path17.join(shotsDir, name) })) : [];
15905
16278
  let har2 = null;
15906
16279
  if (session.harId) {
15907
16280
  const harData = await readHarRecording(session.harId);
15908
- const harPath = path16.join(session.dir, "har.json");
16281
+ const harPath = path17.join(session.dir, "har.json");
15909
16282
  writeJsonPrivate(harPath, harData);
15910
16283
  har2 = { id: session.harId, path: harPath, entryCount: harData.log.entries.length };
15911
16284
  }
15912
- const logsDir = path16.join(session.dir, "logs");
15913
- const logs = fs12.existsSync(logsDir) ? fs12.readdirSync(logsDir).filter((name) => name.endsWith(".log")).map((name) => {
15914
- const filePath = path16.join(logsDir, name);
15915
- const content = fs12.readFileSync(filePath, "utf-8");
16285
+ const logsDir = path17.join(session.dir, "logs");
16286
+ const logs = fs13.existsSync(logsDir) ? fs13.readdirSync(logsDir).filter((name) => name.endsWith(".log")).map((name) => {
16287
+ const filePath = path17.join(logsDir, name);
16288
+ const content = fs13.readFileSync(filePath, "utf-8");
15916
16289
  return { name, path: filePath, lines: content ? content.split("\n").filter(Boolean).length : 0 };
15917
16290
  }) : [];
15918
16291
  const snaps = collectSnaps(session.dir);
@@ -15921,7 +16294,7 @@ async function stop(parsed) {
15921
16294
  const reports = collectLighthouseReports(session.dir);
15922
16295
  const knownDirs = /* @__PURE__ */ new Set(["shots", "logs", "lighthouse", "measure", "motion", "network", ".har", ".stop.lock", ".operations.lock"]);
15923
16296
  const knownFiles = /* @__PURE__ */ new Set([".session.json", ".operations.json", "har.json", "bundle.json", "bridge.sock"]);
15924
- const other = fs12.readdirSync(session.dir).filter((name) => !knownDirs.has(name) && !knownFiles.has(name)).map((name) => ({ name, path: path16.join(session.dir, name) }));
16297
+ const other = fs13.readdirSync(session.dir).filter((name) => !knownDirs.has(name) && !knownFiles.has(name)).map((name) => ({ name, path: path17.join(session.dir, name) }));
15925
16298
  const manifest = {
15926
16299
  id: session.sessionId,
15927
16300
  startedAt: session.startedAt ?? "",
@@ -15973,7 +16346,7 @@ function list(parsed) {
15973
16346
  return;
15974
16347
  }
15975
16348
  if (rejectSurplusPositionals(parsed, "list", 0, "capture session list")) return;
15976
- const reads = registeredArtifactRoots().flatMap((artifactRoot) => fs12.existsSync(artifactRoot) ? fs12.readdirSync(artifactRoot).filter((d) => fs12.existsSync(sessionMetaPath2(d, artifactRoot))).flatMap((d) => {
16349
+ const reads = registeredArtifactRoots().flatMap((artifactRoot) => fs13.existsSync(artifactRoot) ? fs13.readdirSync(artifactRoot).filter((d) => fs13.existsSync(sessionMetaPath2(d, artifactRoot))).flatMap((d) => {
15977
16350
  try {
15978
16351
  return [tryReadSession(d, artifactRoot)];
15979
16352
  } catch {
@@ -15983,7 +16356,7 @@ function list(parsed) {
15983
16356
  const sessions = reads.flatMap((read) => {
15984
16357
  if (!read.ok) return [];
15985
16358
  const session = read.session;
15986
- const hasBundled = fs12.existsSync(path16.join(session.dir, "bundle.json"));
16359
+ const hasBundled = fs13.existsSync(path17.join(session.dir, "bundle.json"));
15987
16360
  return [{ id: session.sessionId, startedAt: session.startedAt ?? "", url: session.url ?? null, status: hasBundled ? "stopped" : "active" }];
15988
16361
  });
15989
16362
  const unreadable = reads.flatMap((read) => read.ok ? [] : [read]);
@@ -16061,7 +16434,7 @@ function view(parsed) {
16061
16434
  process.exitCode = 1;
16062
16435
  return;
16063
16436
  }
16064
- const bundlePath = path16.join(session.dir, "bundle.json");
16437
+ const bundlePath = path17.join(session.dir, "bundle.json");
16065
16438
  let manifest;
16066
16439
  try {
16067
16440
  manifest = readBundleManifest(bundlePath);
@@ -16144,11 +16517,11 @@ async function sessionMain(parsed, _args) {
16144
16517
  return;
16145
16518
  }
16146
16519
  }
16147
- var fs12, path16, VIEW_FILTERS, SECTION_LABELS, SESSION_TAB_ATTACH_TIMEOUT_MS, COMMAND_BLOCK, START_USAGE, STOP_USAGE, LIST_USAGE, VIEW_USAGE, DEFAULT_HAR_ROWS, DEFAULT_FULL_HAR_ROWS, HAR_USAGE, LOG_USAGE, productionStartWorld, startWorld;
16520
+ var fs13, path17, VIEW_FILTERS, SECTION_LABELS, SESSION_TAB_ATTACH_TIMEOUT_MS, COMMAND_BLOCK, START_USAGE, STOP_USAGE, LIST_USAGE, VIEW_USAGE, DEFAULT_HAR_ROWS, DEFAULT_FULL_HAR_ROWS, HAR_USAGE, LOG_USAGE, productionStartWorld, startWorld;
16148
16521
  var init_commands = __esm({
16149
16522
  "src/session/commands.ts"() {
16150
- fs12 = __toESM(require("fs"));
16151
- path16 = __toESM(require("path"));
16523
+ fs13 = __toESM(require("fs"));
16524
+ path17 = __toESM(require("path"));
16152
16525
  init_har_manager();
16153
16526
  init_session_context();
16154
16527
  init_spawn();
@@ -16507,7 +16880,7 @@ async function startCollectors(parsed, client) {
16507
16880
  let harRecorder;
16508
16881
  if (parsed.har && !routed) {
16509
16882
  const harPath = harFilePath(parsed.har);
16510
- if (!fs13.existsSync(harPath)) {
16883
+ if (!fs14.existsSync(harPath)) {
16511
16884
  throw captureError(
16512
16885
  "precondition",
16513
16886
  "har_missing",
@@ -16589,10 +16962,10 @@ async function withPageAction(parsed, opts, fn) {
16589
16962
  client.close();
16590
16963
  }
16591
16964
  }
16592
- var fs13, import_node_perf_hooks2, seams;
16965
+ var fs14, import_node_perf_hooks2, seams;
16593
16966
  var init_connection = __esm({
16594
16967
  "src/cdp/connection.ts"() {
16595
- fs13 = __toESM(require("fs"));
16968
+ fs14 = __toESM(require("fs"));
16596
16969
  import_node_perf_hooks2 = require("node:perf_hooks");
16597
16970
  init_client();
16598
16971
  init_detect();
@@ -228542,22 +228915,22 @@ var init_exec_expression = __esm({
228542
228915
  // src/vault/paths.ts
228543
228916
  function vaultLibsDir() {
228544
228917
  const candidates = [
228545
- path17.resolve(__dirname, "..", "vault", "libs"),
228918
+ path18.resolve(__dirname, "..", "vault", "libs"),
228546
228919
  // built bin: bin/../vault/libs
228547
- path17.resolve(__dirname, "..", "..", "vault", "libs")
228920
+ path18.resolve(__dirname, "..", "..", "vault", "libs")
228548
228921
  // tsx dev: src/vault/../../vault/libs
228549
228922
  ];
228550
228923
  const hit = candidates.find(
228551
- (p) => fs14.existsSync(path17.join(p, "_runtime", "index.ts"))
228924
+ (p) => fs15.existsSync(path18.join(p, "_runtime", "index.ts"))
228552
228925
  );
228553
228926
  if (!hit) throw new Error(DEV_ONLY_MSG);
228554
228927
  return hit;
228555
228928
  }
228556
228929
  function vaultRepoRoot() {
228557
- return path17.dirname(vaultLibsDir());
228930
+ return path18.dirname(vaultLibsDir());
228558
228931
  }
228559
228932
  function listLibs() {
228560
- return fs14.readdirSync(vaultLibsDir(), { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith("_")).map((e) => e.name).sort();
228933
+ return fs15.readdirSync(vaultLibsDir(), { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith("_")).map((e) => e.name).sort();
228561
228934
  }
228562
228935
  function unknownLibMsg(name) {
228563
228936
  return `Unknown lib "${name}". Available: ${listLibs().join(", ")}`;
@@ -228570,11 +228943,11 @@ async function loadEsbuild() {
228570
228943
  throw new Error(DEV_ONLY_MSG);
228571
228944
  });
228572
228945
  }
228573
- var fs14, path17, DEV_ONLY_MSG, VALID_LIB_NAME;
228946
+ var fs15, path18, DEV_ONLY_MSG, VALID_LIB_NAME;
228574
228947
  var init_paths = __esm({
228575
228948
  "src/vault/paths.ts"() {
228576
- fs14 = __toESM(require("fs"));
228577
- path17 = __toESM(require("path"));
228949
+ fs15 = __toESM(require("fs"));
228950
+ path18 = __toESM(require("path"));
228578
228951
  DEV_ONLY_MSG = "This is a dev-only feature of capture \u2014 run it from a capture checkout\n(vault/ source + `pnpm install`). Not available in the published package.";
228579
228952
  VALID_LIB_NAME = /^[A-Za-z0-9_.-]+$/;
228580
228953
  }
@@ -228601,7 +228974,7 @@ function preflightLibs(imports) {
228601
228974
  let m;
228602
228975
  while ((m = LIB_SPECIFIER_RE.exec(imports)) !== null) {
228603
228976
  const name = m[2];
228604
- if (!isValidLibName(name) || !fs15.existsSync(path18.join(libs, name, "index.ts"))) {
228977
+ if (!isValidLibName(name) || !fs16.existsSync(path19.join(libs, name, "index.ts"))) {
228605
228978
  throw new Error(unknownLibMsg(name));
228606
228979
  }
228607
228980
  }
@@ -228617,7 +228990,7 @@ function buildEntry(code) {
228617
228990
  }
228618
228991
  function vaultResolver() {
228619
228992
  const libs = vaultLibsDir();
228620
- const runtime = path18.join(libs, "_runtime", "index.ts");
228993
+ const runtime = path19.join(libs, "_runtime", "index.ts");
228621
228994
  return {
228622
228995
  name: "vault",
228623
228996
  setup(build) {
@@ -228629,8 +229002,8 @@ function vaultResolver() {
228629
229002
  if (!isValidLibName(name)) {
228630
229003
  return { errors: [{ text: unknownLibMsg(name) }] };
228631
229004
  }
228632
- const entry = path18.join(libs, name, "index.ts");
228633
- return fs15.existsSync(entry) ? { path: entry } : { errors: [{ text: unknownLibMsg(name) }] };
229005
+ const entry = path19.join(libs, name, "index.ts");
229006
+ return fs16.existsSync(entry) ? { path: entry } : { errors: [{ text: unknownLibMsg(name) }] };
228634
229007
  });
228635
229008
  }
228636
229009
  };
@@ -228699,11 +229072,11 @@ async function bundleExec(code) {
228699
229072
  }
228700
229073
  return applyReturnInsert(result.outputFiles[0].text);
228701
229074
  }
228702
- var fs15, path18, SENTINEL, IMPORT_STMT_RE, LIB_SPECIFIER_RE;
229075
+ var fs16, path19, SENTINEL, IMPORT_STMT_RE, LIB_SPECIFIER_RE;
228703
229076
  var init_bundle = __esm({
228704
229077
  "src/vault/bundle.ts"() {
228705
- fs15 = __toESM(require("fs"));
228706
- path18 = __toESM(require("path"));
229078
+ fs16 = __toESM(require("fs"));
229079
+ path19 = __toESM(require("path"));
228707
229080
  init_paths();
228708
229081
  init_exec_expression();
228709
229082
  SENTINEL = "__CAPTURE_RESULT";
@@ -228723,7 +229096,7 @@ function emitExecError(parsed, code, summary, followUp) {
228723
229096
  function spillWholeResult(payload, artifactDir) {
228724
229097
  const name = `exec-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}.json`;
228725
229098
  const session = deps3.getActiveSession();
228726
- const filePath = session ? path19.join(session.dir, "page", name) : path19.join(deps3.createOneshotSession("page", artifactDir).artifactsDir, name);
229099
+ const filePath = session ? path20.join(session.dir, "page", name) : path20.join(deps3.createOneshotSession("page", artifactDir).artifactsDir, name);
228727
229100
  writePrivateFile(filePath, payload);
228728
229101
  return filePath;
228729
229102
  }
@@ -228817,7 +229190,7 @@ async function cmdPageExec(parsed, _args) {
228817
229190
  );
228818
229191
  }
228819
229192
  try {
228820
- code = fs16.readFileSync(parsed.file, "utf-8");
229193
+ code = fs17.readFileSync(parsed.file, "utf-8");
228821
229194
  } catch (err) {
228822
229195
  return emitExecError(
228823
229196
  parsed,
@@ -228915,11 +229288,11 @@ async function cmdPageExec(parsed, _args) {
228915
229288
  { json: parsed.json }
228916
229289
  );
228917
229290
  }
228918
- var fs16, path19, GENEROUS_RESULT_CAP, DEFAULT_SETTLE_MS2, DEFAULT_EVALUATION_TIMEOUT_MS, TERMINATE_TIMEOUT_MS, USAGE5, deps3, ExecDeadlineError;
229291
+ var fs17, path20, GENEROUS_RESULT_CAP, DEFAULT_SETTLE_MS2, DEFAULT_EVALUATION_TIMEOUT_MS, TERMINATE_TIMEOUT_MS, USAGE5, deps3, ExecDeadlineError;
228919
229292
  var init_exec = __esm({
228920
229293
  "src/cdp/commands/page/exec.ts"() {
228921
- fs16 = __toESM(require("fs"));
228922
- path19 = __toESM(require("path"));
229294
+ fs17 = __toESM(require("fs"));
229295
+ path20 = __toESM(require("path"));
228923
229296
  init_connection();
228924
229297
  init_errors();
228925
229298
  init_exec_expression();
@@ -229031,7 +229404,7 @@ async function resolveOutPath(parsed) {
229031
229404
  if (parsed.out) return parsed.out;
229032
229405
  const sessionPath = await deps4.nextStepPath("shot", "manual");
229033
229406
  if (sessionPath) return sessionPath;
229034
- return path20.join(deps4.createOneshotSession("page", parsed.artifactDir).artifactsDir, "shot.png");
229407
+ return path21.join(deps4.createOneshotSession("page", parsed.artifactDir).artifactsDir, "shot.png");
229035
229408
  }
229036
229409
  function round6(value) {
229037
229410
  return Math.round(value * 1e6) / 1e6;
@@ -229269,10 +229642,10 @@ async function cmdPageShot(parsed, _args) {
229269
229642
  }
229270
229643
  emitResult(buildScreenshotResult({ ...result, emulation: parsed.fullPage ? "full-page" : viewport2 ? "viewport" : "none", viewport: viewport2, colorScheme }), { json: parsed.json });
229271
229644
  }
229272
- var path20, USAGE6, deps4, PNG_SIGNATURE, LEGIBILITY_SCALE_THRESHOLD;
229645
+ var path21, USAGE6, deps4, PNG_SIGNATURE, LEGIBILITY_SCALE_THRESHOLD;
229273
229646
  var init_shot = __esm({
229274
229647
  "src/cdp/commands/page/shot.ts"() {
229275
- path20 = __toESM(require("path"));
229648
+ path21 = __toESM(require("path"));
229276
229649
  init_connection();
229277
229650
  init_screenshot();
229278
229651
  init_viewport();
@@ -230497,7 +230870,7 @@ async function cmdTabNetwork(parsed, _args) {
230497
230870
  `session ${session.sessionId} has no target to emulate on; open one with \`capture session start --hold --url <url>\` or \`capture tab reset <url>\`.`
230498
230871
  );
230499
230872
  }
230500
- if (!session.bridgeSocket || !fs17.existsSync(session.bridgeSocket)) {
230873
+ if (!session.bridgeSocket || !fs18.existsSync(session.bridgeSocket)) {
230501
230874
  throw captureError(
230502
230875
  "precondition",
230503
230876
  "no_held_bridge",
@@ -230561,10 +230934,10 @@ async function cmdTabNetwork(parsed, _args) {
230561
230934
  }
230562
230935
  emitResult(buildNetworkResult(mode, targetId), { json: parsed.json });
230563
230936
  }
230564
- var fs17, USAGE15;
230937
+ var fs18, USAGE15;
230565
230938
  var init_network = __esm({
230566
230939
  "src/cdp/commands/tab/network.ts"() {
230567
- fs17 = __toESM(require("node:fs"));
230940
+ fs18 = __toESM(require("node:fs"));
230568
230941
  init_errors();
230569
230942
  init_session_context();
230570
230943
  init_coordinator();
@@ -230675,12 +231048,12 @@ var init_lifecycle2 = __esm({
230675
231048
 
230676
231049
  // src/cdp/commands/tab/mock/start.ts
230677
231050
  function stagedRulesPath() {
230678
- return path21.join(CAPTURE_ROOT, ".mock-inputs", `rules-${crypto8.randomBytes(12).toString("hex")}.json`);
231051
+ return path22.join(CAPTURE_ROOT, ".mock-inputs", `rules-${crypto8.randomBytes(12).toString("hex")}.json`);
230679
231052
  }
230680
231053
  function sourceFor(pathname) {
230681
231054
  let source;
230682
231055
  try {
230683
- source = fs18.readFileSync(pathname);
231056
+ source = fs19.readFileSync(pathname);
230684
231057
  } catch (error4) {
230685
231058
  throw captureError("artifact", "mock_rules_unreadable", `could not read mock rules at ${pathname}: ${error4 instanceof Error ? error4.message : String(error4)}`);
230686
231059
  }
@@ -230724,12 +231097,12 @@ async function cmdTabMockStart(parsed) {
230724
231097
  }
230725
231098
  }
230726
231099
  }
230727
- var crypto8, fs18, path21, HELP;
231100
+ var crypto8, fs19, path22, HELP;
230728
231101
  var init_start = __esm({
230729
231102
  "src/cdp/commands/tab/mock/start.ts"() {
230730
231103
  crypto8 = __toESM(require("node:crypto"));
230731
- fs18 = __toESM(require("node:fs"));
230732
- path21 = __toESM(require("node:path"));
231104
+ fs19 = __toESM(require("node:fs"));
231105
+ path22 = __toESM(require("node:path"));
230733
231106
  init_errors();
230734
231107
  init_artifacts();
230735
231108
  init_render();
@@ -230890,13 +231263,13 @@ function isAgentVisible(doc) {
230890
231263
  return AGENT_VISIBLE_VISIBILITIES.has(doc.visibility);
230891
231264
  }
230892
231265
  function cacheDir() {
230893
- return path22.join(vaultRepoRoot(), ".cache", "docs");
231266
+ return path23.join(vaultRepoRoot(), ".cache", "docs");
230894
231267
  }
230895
231268
  function schemasPathFor(svc) {
230896
- return path22.join(vaultLibsDir(), svc, "schemas.ts");
231269
+ return path23.join(vaultLibsDir(), svc, "schemas.ts");
230897
231270
  }
230898
231271
  function srcSha(svc) {
230899
- return (0, import_crypto.createHash)("sha256").update(fs19.readFileSync(schemasPathFor(svc), "utf8")).digest("hex");
231272
+ return (0, import_crypto.createHash)("sha256").update(fs20.readFileSync(schemasPathFor(svc), "utf8")).digest("hex");
230900
231273
  }
230901
231274
  function toJsonSchema(zodSchema) {
230902
231275
  const s = zodSchema;
@@ -230949,14 +231322,14 @@ async function buildDoc(svc) {
230949
231322
  return doc;
230950
231323
  }
230951
231324
  async function getDoc(name) {
230952
- if (!isValidLibName(name) || !fs19.existsSync(schemasPathFor(name))) {
231325
+ if (!isValidLibName(name) || !fs20.existsSync(schemasPathFor(name))) {
230953
231326
  throw new Error(unknownLibMsg(name));
230954
231327
  }
230955
231328
  const sha = srcSha(name);
230956
- const cacheFile = path22.join(cacheDir(), `${name}.json`);
230957
- if (fs19.existsSync(cacheFile)) {
231329
+ const cacheFile = path23.join(cacheDir(), `${name}.json`);
231330
+ if (fs20.existsSync(cacheFile)) {
230958
231331
  try {
230959
- const cached = JSON.parse(fs19.readFileSync(cacheFile, "utf8"));
231332
+ const cached = JSON.parse(fs20.readFileSync(cacheFile, "utf8"));
230960
231333
  if (cached._srcSha === sha) {
230961
231334
  delete cached._srcSha;
230962
231335
  return cached;
@@ -230965,8 +231338,8 @@ async function getDoc(name) {
230965
231338
  }
230966
231339
  }
230967
231340
  const doc = await buildDoc(name);
230968
- fs19.mkdirSync(cacheDir(), { recursive: true });
230969
- fs19.writeFileSync(
231341
+ fs20.mkdirSync(cacheDir(), { recursive: true });
231342
+ fs20.writeFileSync(
230970
231343
  cacheFile,
230971
231344
  JSON.stringify({ ...doc, _srcSha: sha }, null, 2)
230972
231345
  );
@@ -230988,11 +231361,11 @@ async function listDocs() {
230988
231361
  }
230989
231362
  return out;
230990
231363
  }
230991
- var fs19, path22, import_crypto, AGENT_VISIBLE_VISIBILITIES, cjsRequire;
231364
+ var fs20, path23, import_crypto, AGENT_VISIBLE_VISIBILITIES, cjsRequire;
230992
231365
  var init_docs = __esm({
230993
231366
  "src/vault/docs.ts"() {
230994
- fs19 = __toESM(require("fs"));
230995
- path22 = __toESM(require("path"));
231367
+ fs20 = __toESM(require("fs"));
231368
+ path23 = __toESM(require("path"));
230996
231369
  import_crypto = require("crypto");
230997
231370
  init_paths();
230998
231371
  AGENT_VISIBLE_VISIBILITIES = /* @__PURE__ */ new Set(["public", "chat"]);
@@ -231300,10 +231673,10 @@ async function cmdLib(parsed, _args) {
231300
231673
  throw e;
231301
231674
  }
231302
231675
  }
231303
- var path23, COMMAND_BLOCK4, LIB_USAGE, LEAF_USAGE, FUNCTION_NAME_EXACT, FUNCTION_NAME_PREFIX, FUNCTION_NAME_SUBSTRING, SERVICE_NAME_MATCH, FUNCTION_DESC_SUBSTRING, SERVICE_DESC_SUBSTRING, svcName, cap, srcPathFor;
231676
+ var path24, COMMAND_BLOCK4, LIB_USAGE, LEAF_USAGE, FUNCTION_NAME_EXACT, FUNCTION_NAME_PREFIX, FUNCTION_NAME_SUBSTRING, SERVICE_NAME_MATCH, FUNCTION_DESC_SUBSTRING, SERVICE_DESC_SUBSTRING, svcName, cap, srcPathFor;
231304
231677
  var init_lib = __esm({
231305
231678
  "src/cdp/commands/lib.ts"() {
231306
- path23 = __toESM(require("path"));
231679
+ path24 = __toESM(require("path"));
231307
231680
  init_errors();
231308
231681
  init_render();
231309
231682
  init_paths();
@@ -231349,7 +231722,7 @@ Effects: local reads only; no CDP connection. Dev checkout required.`
231349
231722
  SERVICE_DESC_SUBSTRING = 10;
231350
231723
  svcName = (doc) => doc.library.replace(/^@vallum\//, "");
231351
231724
  cap = (value, maxLength) => value.length > maxLength ? `${value.slice(0, maxLength - 1)}\u2026` : value;
231352
- srcPathFor = (name) => path23.join(vaultLibsDir(), name, "index.ts");
231725
+ srcPathFor = (name) => path24.join(vaultLibsDir(), name, "index.ts");
231353
231726
  }
231354
231727
  });
231355
231728
 
@@ -231433,7 +231806,7 @@ async function runBrowserScope(method, params, parsed, timeoutMs, deps23 = DEFAU
231433
231806
  const flagTarget = parsed.targetSource === "flag" ? parsed.target : void 0;
231434
231807
  const session = getActiveSession();
231435
231808
  const bridgeSocket = session?.bridgeSocket;
231436
- const heldBridgeActive = Boolean(bridgeSocket && fs20.existsSync(bridgeSocket));
231809
+ const heldBridgeActive = Boolean(bridgeSocket && fs21.existsSync(bridgeSocket));
231437
231810
  if (parsed.portSource !== "flag" && bridgeSocket && heldBridgeActive) {
231438
231811
  let resp;
231439
231812
  try {
@@ -231561,10 +231934,10 @@ function waitForEventOnce(client, eventName, timeoutMs) {
231561
231934
  });
231562
231935
  });
231563
231936
  }
231564
- var fs20, DEFAULT_TIMEOUT_MS, GENEROUS_RESULT_CAP2, COMMAND_BLOCK5, HELP3, DEFAULT_BROWSER_DEPS, TAB_SCOPE_RECOVERY;
231937
+ var fs21, DEFAULT_TIMEOUT_MS, GENEROUS_RESULT_CAP2, COMMAND_BLOCK5, HELP3, DEFAULT_BROWSER_DEPS, TAB_SCOPE_RECOVERY;
231565
231938
  var init_cdp = __esm({
231566
231939
  "src/cdp/commands/cdp.ts"() {
231567
- fs20 = __toESM(require("fs"));
231940
+ fs21 = __toESM(require("fs"));
231568
231941
  init_errors();
231569
231942
  init_session_context();
231570
231943
  init_connection();
@@ -231604,12 +231977,12 @@ Effects:
231604
231977
  });
231605
231978
 
231606
231979
  // src/cdp/host/core.ts
231607
- var crypto9, fs21, path24, import_node_perf_hooks3, CollectorHost;
231980
+ var crypto9, fs22, path25, import_node_perf_hooks3, CollectorHost;
231608
231981
  var init_core = __esm({
231609
231982
  "src/cdp/host/core.ts"() {
231610
231983
  crypto9 = __toESM(require("node:crypto"));
231611
- fs21 = __toESM(require("node:fs"));
231612
- path24 = __toESM(require("node:path"));
231984
+ fs22 = __toESM(require("node:fs"));
231985
+ path25 = __toESM(require("node:path"));
231613
231986
  import_node_perf_hooks3 = require("node:perf_hooks");
231614
231987
  init_artifacts();
231615
231988
  init_artifacts();
@@ -231683,7 +232056,7 @@ var init_core = __esm({
231683
232056
  this.refuseClaims(collector.claims);
231684
232057
  const id = `${entry.idPrefix}-${crypto9.randomBytes(5).toString("hex")}`;
231685
232058
  this.starting.set(id, collector.claims);
231686
- const dir = path24.join(this.sessionDir, ...entry.idSegments, id);
232059
+ const dir = path25.join(this.sessionDir, ...entry.idSegments, id);
231687
232060
  try {
231688
232061
  ensurePrivateDir(dir);
231689
232062
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -231695,13 +232068,13 @@ var init_core = __esm({
231695
232068
  id,
231696
232069
  targetId: this.identity.targetId,
231697
232070
  config: config3,
231698
- appendRecord: (file, record5) => appendNdjsonPrivate(path24.join(dir, file), record5),
231699
- openChunkFile: (file) => openPrivateChunkWriter(path24.join(dir, file)),
232071
+ appendRecord: (file, record5) => appendNdjsonPrivate(path25.join(dir, file), record5),
232072
+ openChunkFile: (file) => openPrivateChunkWriter(path25.join(dir, file)),
231700
232073
  noteLoss: (reason, detail) => {
231701
232074
  live.losses.push({ reason, detail });
231702
232075
  }
231703
232076
  };
231704
- writeJsonPrivate(path24.join(dir, "collecting.json"), { kind, id, startedAt, hostPid: this.identity.pid, hostBirth: this.identity.birth });
232077
+ writeJsonPrivate(path25.join(dir, "collecting.json"), { kind, id, startedAt, hostPid: this.identity.pid, hostBirth: this.identity.birth });
231705
232078
  await collector.start(context);
231706
232079
  this.starting.delete(id);
231707
232080
  this.live.set(id, live);
@@ -231723,10 +232096,10 @@ var init_core = __esm({
231723
232096
  finalizedFiles(dir) {
231724
232097
  const files = [];
231725
232098
  const walk2 = (relative2) => {
231726
- for (const entry of fs21.readdirSync(path24.join(dir, relative2), { withFileTypes: true })) {
231727
- const name = path24.join(relative2, entry.name);
232099
+ for (const entry of fs22.readdirSync(path25.join(dir, relative2), { withFileTypes: true })) {
232100
+ const name = path25.join(relative2, entry.name);
231728
232101
  if (entry.isDirectory()) walk2(name);
231729
- else if (entry.isFile()) files.push({ name, bytes: readPrivateFile(path24.join(dir, name)).length });
232102
+ else if (entry.isFile()) files.push({ name, bytes: readPrivateFile(path25.join(dir, name)).length });
231730
232103
  }
231731
232104
  };
231732
232105
  walk2("");
@@ -231738,12 +232111,12 @@ var init_core = __esm({
231738
232111
  }
231739
232112
  async drain(live, cause) {
231740
232113
  const outcome = await live.collector.drain(cause);
231741
- unlinkPrivateFile(path24.join(live.row.dir, "collecting.json"));
232114
+ unlinkPrivateFile(path25.join(live.row.dir, "collecting.json"));
231742
232115
  const files = this.finalizedFiles(live.row.dir);
231743
232116
  const loss = live.losses[0];
231744
232117
  const completion = loss || !cause.clientUsable ? "partial" : "complete";
231745
232118
  const summary = outcome.summary && typeof outcome.summary === "object" && !Array.isArray(outcome.summary) ? outcome.summary : { value: outcome.summary };
231746
- writeJsonPrivate(path24.join(live.row.dir, "meta.json"), {
232119
+ writeJsonPrivate(path25.join(live.row.dir, "meta.json"), {
231747
232120
  id: live.row.id,
231748
232121
  kind: live.row.kind,
231749
232122
  completion,
@@ -231844,7 +232217,7 @@ async function runCollectorHost(options) {
231844
232217
  if (session.targetId !== options.targetId) throw new Error("collector host target does not match session state");
231845
232218
  await applyActiveSessionNetworkConditions(client, session, options.targetId);
231846
232219
  let server2;
231847
- const handlePath = path25.join(options.sessionDir, ".collector-host.json");
232220
+ const handlePath = path26.join(options.sessionDir, ".collector-host.json");
231848
232221
  const publish = (snapshot) => writeJsonPrivate(handlePath, {
231849
232222
  pid: process.pid,
231850
232223
  birth: birth.identity,
@@ -231941,11 +232314,11 @@ async function runCollectorHost(options) {
231941
232314
  cleanup();
231942
232315
  }, client);
231943
232316
  }
231944
- var crypto10, path25;
232317
+ var crypto10, path26;
231945
232318
  var init_server2 = __esm({
231946
232319
  "src/cdp/host/server.ts"() {
231947
232320
  crypto10 = __toESM(require("node:crypto"));
231948
- path25 = __toESM(require("node:path"));
232321
+ path26 = __toESM(require("node:path"));
231949
232322
  init_client();
231950
232323
  init_targets();
231951
232324
  init_server();
@@ -238193,8 +238566,8 @@ function round32(value) {
238193
238566
  return Math.round(value * 1e3) / 1e3;
238194
238567
  }
238195
238568
  async function captureStateRaster(ctx, client, state, id, borderRect2) {
238196
- const baselinePath = path26.join(ctx.dir, "screenshot.png");
238197
- if (!fs22.existsSync(baselinePath)) return void 0;
238569
+ const baselinePath = path27.join(ctx.dir, "screenshot.png");
238570
+ if (!fs23.existsSync(baselinePath)) return void 0;
238198
238571
  const response = await client.send("Page.captureScreenshot", { format: "png" });
238199
238572
  const statePng = Buffer.from(response.data, "base64");
238200
238573
  const imageName = `states/${sanitizeToken(state) || "state"}-${id}.png`;
@@ -238208,7 +238581,7 @@ async function captureStateRaster(ctx, client, state, id, borderRect2) {
238208
238581
  const scaleY = available ? stateImage.height / h : 1;
238209
238582
  const paddingCssPx = ctx.stateDiffPaddingCssPx ?? DEFAULT_STATE_DIFF_PADDING_CSS_PX;
238210
238583
  const raster = { screenshot: imageName, viewportScale: { available, innerWidth: available ? w : 0, innerHeight: available ? h : 0, scaleX: round32(scaleX), scaleY: round32(scaleY), ...available ? {} : { unavailableReason: "viewport-read-unavailable" } }, diffCrop: { paddingCssPx, borderRect: borderRect2 } };
238211
- const baseline = import_pngjs3.PNG.sync.read(fs22.readFileSync(baselinePath));
238584
+ const baseline = import_pngjs3.PNG.sync.read(fs23.readFileSync(baselinePath));
238212
238585
  if (baseline.width !== stateImage.width || baseline.height !== stateImage.height) return { ...raster, diffUnavailableReason: "dimension-mismatch" };
238213
238586
  const x0 = Math.max(0, Math.floor((borderRect2.x - paddingCssPx) * scaleX));
238214
238587
  const y0 = Math.max(0, Math.floor((borderRect2.y - paddingCssPx) * scaleY));
@@ -238431,11 +238804,11 @@ async function captureOneElement(ctx, client, selector, index, nodeId, identity3
238431
238804
  ...raster ? { raster } : {}
238432
238805
  };
238433
238806
  }
238434
- var fs22, path26, import_pngjs3, MAX_SELECTOR_MATCHES, MAX_AUTO_ELEMENTS, MAX_STRING_LEN, MAX_AFFECTED_ELEMENTS, DEFAULT_STATE_DIFF_PADDING_CSS_PX, STATE_CAPTURE_SETTLE_FRAMES, CONCRETE_STATES, CONCRETE_STATE_SET, INTERACTIVE_SELECTOR, ELIGIBLE_SELECTORS, PSEUDO_CLASS_MAP, STYLE_PROPS, collectStates;
238807
+ var fs23, path27, import_pngjs3, MAX_SELECTOR_MATCHES, MAX_AUTO_ELEMENTS, MAX_STRING_LEN, MAX_AFFECTED_ELEMENTS, DEFAULT_STATE_DIFF_PADDING_CSS_PX, STATE_CAPTURE_SETTLE_FRAMES, CONCRETE_STATES, CONCRETE_STATE_SET, INTERACTIVE_SELECTOR, ELIGIBLE_SELECTORS, PSEUDO_CLASS_MAP, STYLE_PROPS, collectStates;
238435
238808
  var init_states = __esm({
238436
238809
  "src/cdp/measure/collectors/states.ts"() {
238437
- fs22 = __toESM(require("node:fs"));
238438
- path26 = __toESM(require("node:path"));
238810
+ fs23 = __toESM(require("node:fs"));
238811
+ path27 = __toESM(require("node:path"));
238439
238812
  import_pngjs3 = __toESM(require_png());
238440
238813
  init_pixelmatch();
238441
238814
  init_domains();
@@ -238861,9 +239234,9 @@ document.querySelectorAll('img, video, canvas, svg, iframe').length;`;
238861
239234
 
238862
239235
  // src/cdp/measure/snapshot.ts
238863
239236
  function resolveScopedArtifactPath(dir, filename) {
238864
- const target = path27.resolve(dir, filename);
238865
- const rel = path27.relative(dir, target);
238866
- const escapesDir = rel === ".." || rel.startsWith(`..${path27.sep}`) || path27.isAbsolute(rel);
239237
+ const target = path28.resolve(dir, filename);
239238
+ const rel = path28.relative(dir, target);
239239
+ const escapesDir = rel === ".." || rel.startsWith(`..${path28.sep}`) || path28.isAbsolute(rel);
238867
239240
  if (escapesDir) {
238868
239241
  throw new Error(`Refusing to write snapshot artifact "${filename}" outside its snap dir ${dir}`);
238869
239242
  }
@@ -238960,7 +239333,7 @@ function boundedClient(client, phase, partialPath, onTimeout, requestTimeoutCapM
238960
239333
  async function captureSnapshotSubstrate(options) {
238961
239334
  const client = options.target.client;
238962
239335
  const dir = ensurePrivateDir(options.path);
238963
- const snapId = options.snapId ?? path27.basename(dir);
239336
+ const snapId = options.snapId ?? path28.basename(dir);
238964
239337
  const settleTimeoutMs = options.settleTimeout ?? DEFAULT_SETTLE_TIMEOUT_MS;
238965
239338
  const quietThresholdMs = options.quietThresholdMs ?? DEFAULT_QUIET_THRESHOLD_MS;
238966
239339
  const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
@@ -239022,10 +239395,10 @@ async function captureSnapshotSubstrate(options) {
239022
239395
  const animationEvidence = await collectAnimationEvidence(phaseClient("settle-evidence"));
239023
239396
  const grouped = groupChurnEvidence(churnEvidenceRaw, animationEvidence, settleResult.elapsedMs, settleTimeoutMs);
239024
239397
  unstableRegions = grouped.unstableRegions;
239025
- writeJsonPrivate(path27.join(dir, "churn.json"), grouped.report);
239398
+ writeJsonPrivate(path28.join(dir, "churn.json"), grouped.report);
239026
239399
  artifacts.push("churn.json");
239027
239400
  if (!captured) {
239028
- writeJsonPrivate(path27.join(dir, "animation.json"), animationEvidence);
239401
+ writeJsonPrivate(path28.join(dir, "animation.json"), animationEvidence);
239029
239402
  artifacts.push("animation.json");
239030
239403
  }
239031
239404
  }
@@ -239077,7 +239450,7 @@ async function captureSnapshotSubstrate(options) {
239077
239450
  };
239078
239451
  for (const collector of baseline) collectorOutcomes.push(await runCollector(collector));
239079
239452
  const screenshotResponse = await phaseClient("baseline-screenshot").send("Page.captureScreenshot", { format: "png" });
239080
- writeBinaryPrivate(path27.join(dir, "screenshot.png"), Buffer.from(screenshotResponse.data, "base64"));
239453
+ writeBinaryPrivate(path28.join(dir, "screenshot.png"), Buffer.from(screenshotResponse.data, "base64"));
239081
239454
  artifacts.push("screenshot.png");
239082
239455
  let domHtmlValue;
239083
239456
  let domHtmlUnavailableReason;
@@ -239097,7 +239470,7 @@ async function captureSnapshotSubstrate(options) {
239097
239470
  if (domHtmlValue === void 0) {
239098
239471
  domHtmlFact = { available: false, unavailableReason: domHtmlUnavailableReason };
239099
239472
  } else {
239100
- writePrivateFile(path27.join(dir, "dom.html"), domHtmlValue);
239473
+ writePrivateFile(path28.join(dir, "dom.html"), domHtmlValue);
239101
239474
  artifacts.push("dom.html");
239102
239475
  domHtmlFact = { available: true };
239103
239476
  }
@@ -239144,7 +239517,7 @@ async function captureSnapshotSubstrate(options) {
239144
239517
  ...domHtmlFact ? { domHtml: domHtmlFact } : {},
239145
239518
  ...captured ? { collectors: collectorOutcomes } : {}
239146
239519
  };
239147
- writeJsonPrivate(path27.join(dir, "meta.json"), meta);
239520
+ writeJsonPrivate(path28.join(dir, "meta.json"), meta);
239148
239521
  artifacts.push("meta.json");
239149
239522
  return {
239150
239523
  id: snapId,
@@ -239163,7 +239536,7 @@ async function captureSnapshotSubstrate(options) {
239163
239536
  } catch (error4) {
239164
239537
  if (error4 instanceof SnapshotCaptureTimeout) {
239165
239538
  try {
239166
- writeJsonPrivate(path27.join(dir, "capture-timeout.json"), {
239539
+ writeJsonPrivate(path28.join(dir, "capture-timeout.json"), {
239167
239540
  schemaVersion: 1,
239168
239541
  id: snapId,
239169
239542
  status: "timeout",
@@ -239183,10 +239556,10 @@ async function captureSnapshotSubstrate(options) {
239183
239556
  await restoreAnimationsOnce();
239184
239557
  }
239185
239558
  }
239186
- var path27, COLLECTORS, COLLECTOR_NAMES, SNAPSHOT_REQUEST_TIMEOUT_MS, SCROLL_COLLECTOR_REQUEST_TIMEOUT_MS, SnapshotCaptureTimeout;
239559
+ var path28, COLLECTORS, COLLECTOR_NAMES, SNAPSHOT_REQUEST_TIMEOUT_MS, SCROLL_COLLECTOR_REQUEST_TIMEOUT_MS, SnapshotCaptureTimeout;
239187
239560
  var init_snapshot = __esm({
239188
239561
  "src/cdp/measure/snapshot.ts"() {
239189
- path27 = __toESM(require("path"));
239562
+ path28 = __toESM(require("path"));
239190
239563
  init_artifacts();
239191
239564
  init_domains();
239192
239565
  init_settle();
@@ -239238,347 +239611,6 @@ var init_snapshot = __esm({
239238
239611
  }
239239
239612
  });
239240
239613
 
239241
- // src/output/artifact.ts
239242
- function isUrlRef(ref) {
239243
- return /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(ref);
239244
- }
239245
- function stripTrailingSep(p) {
239246
- return p.length > 1 && (p.endsWith("/") || p.endsWith(path28.sep)) ? p.slice(0, -1) : p;
239247
- }
239248
- function resolveRefFromPath(ref, config3) {
239249
- const resolved = assertUnderCaptureRoot(stripTrailingSep(ref));
239250
- const metaPath = path28.join(resolved, "meta.json");
239251
- if (!fs23.existsSync(metaPath)) {
239252
- throw new ArtifactResolutionError(
239253
- ref,
239254
- [resolved],
239255
- `no ${config3.label} found at this path (missing meta.json)`,
239256
- config3.creatingCommand
239257
- );
239258
- }
239259
- return { kind: config3.kind, id: path28.basename(resolved), dir: resolved };
239260
- }
239261
- function resolveRefFromId(ref, config3) {
239262
- const active = getActiveSession();
239263
- if (!active) {
239264
- throw new ArtifactResolutionError(
239265
- ref,
239266
- [],
239267
- `no active capture session \u2014 a bare id resolves against the active session's ${config3.idSegments.join("/")} directory`,
239268
- `capture session start (then re-run), or pass the absolute ${config3.label} path printed by ${config3.creatingCommand}`
239269
- );
239270
- }
239271
- const dir = path28.join(active.dir, ...config3.idSegments, ref);
239272
- const resolved = assertUnderCaptureRoot(dir);
239273
- const metaPath = path28.join(resolved, "meta.json");
239274
- if (!fs23.existsSync(metaPath)) {
239275
- throw new ArtifactResolutionError(
239276
- ref,
239277
- [resolved],
239278
- `no ${config3.label} named ${JSON.stringify(ref)} found in the active session (${active.sessionId})`,
239279
- config3.creatingCommand
239280
- );
239281
- }
239282
- return { kind: config3.kind, id: ref, dir: resolved };
239283
- }
239284
- function rejectRelativePath(ref) {
239285
- if (!path28.isAbsolute(ref) && (ref.includes("/") || ref.includes(path28.sep))) {
239286
- throw new ArtifactResolutionError(
239287
- ref,
239288
- [],
239289
- "relative paths are not accepted \u2014 pass an absolute artifact path (as printed by the capturing command) or a bare id"
239290
- );
239291
- }
239292
- }
239293
- async function resolveSnapRef(ref, options = {}) {
239294
- if (isUrlRef(ref)) {
239295
- if (!options.onUrl) {
239296
- throw new ArtifactResolutionError(ref, [], "this command does not accept a URL target \u2014 pass a snapshot id or path instead");
239297
- }
239298
- const result = await options.onUrl(ref);
239299
- return { kind: "snap", id: result.id, dir: assertUnderCaptureRoot(result.dir) };
239300
- }
239301
- if (path28.isAbsolute(ref)) return resolveRefFromPath(ref, SNAP_CONFIG);
239302
- rejectRelativePath(ref);
239303
- return resolveRefFromId(ref, SNAP_CONFIG);
239304
- }
239305
- function resolveRecRef(ref) {
239306
- if (isUrlRef(ref)) {
239307
- throw new ArtifactResolutionError(
239308
- ref,
239309
- [],
239310
- "a recording ref cannot be a URL \u2014 record one first with `capture motion rec <url> --do <action>` or `--start`/`--stop`"
239311
- );
239312
- }
239313
- if (path28.isAbsolute(ref)) return resolveRefFromPath(ref, REC_CONFIG);
239314
- rejectRelativePath(ref);
239315
- return resolveRefFromId(ref, REC_CONFIG);
239316
- }
239317
- function resolveTraceRef(ref) {
239318
- if (isUrlRef(ref)) {
239319
- throw new ArtifactResolutionError(ref, [], "a trace ref cannot be a URL \u2014 record one first with `capture perf trace <url>` or `--start`/`--stop`");
239320
- }
239321
- const resolved = path28.isAbsolute(ref) ? resolveRefFromPath(ref, TRACE_CONFIG) : (rejectRelativePath(ref), resolveRefFromId(ref, TRACE_CONFIG));
239322
- let meta;
239323
- try {
239324
- meta = JSON.parse(readPrivateFile(path28.join(resolved.dir, "meta.json")).toString("utf8"));
239325
- } catch (error4) {
239326
- throw new ArtifactResolutionError(ref, [path28.join(resolved.dir, "meta.json")], `could not read trace metadata: ${error4 instanceof Error ? error4.message : String(error4)}`, TRACE_CONFIG.creatingCommand);
239327
- }
239328
- const completion = meta.completion;
239329
- if (completion !== "complete" && completion !== "partial" && completion !== "orphaned") {
239330
- throw new ArtifactResolutionError(ref, [path28.join(resolved.dir, "meta.json")], "trace metadata has no valid completion state", TRACE_CONFIG.creatingCommand);
239331
- }
239332
- const inventory = meta.files;
239333
- const inventoryMalformed = !Array.isArray(inventory);
239334
- const inventoryMismatch = !inventoryMalformed && inventory.some((entry) => {
239335
- if (!entry || typeof entry !== "object" || Array.isArray(entry)) return true;
239336
- const item = entry;
239337
- const name = item.name;
239338
- const bytes = item.bytes;
239339
- if (typeof name !== "string" || typeof bytes !== "number" || !Number.isSafeInteger(bytes) || bytes < 0 || name.includes(path28.sep)) return true;
239340
- try {
239341
- return fs23.statSync(path28.join(resolved.dir, name)).size !== bytes;
239342
- } catch {
239343
- return true;
239344
- }
239345
- });
239346
- const truncatedReason = inventoryMalformed ? "artifact_inventory_missing_or_malformed" : inventoryMismatch ? "artifact_inventory_mismatch" : void 0;
239347
- return { ...resolved, completion: truncatedReason ? "truncated" : completion, ...truncatedReason ? { reason: truncatedReason } : typeof meta.reason === "string" ? { reason: meta.reason } : {} };
239348
- }
239349
- function creatingCommandFor(filename) {
239350
- return CREATING_COMMAND_BY_FILE[filename];
239351
- }
239352
- function artifactPath(ref, filename, opts = {}) {
239353
- const filePath = assertUnderCaptureRoot(path28.join(ref.dir, filename));
239354
- const mustExist = opts.mustExist ?? true;
239355
- if (mustExist && !fs23.existsSync(filePath)) {
239356
- throw new ArtifactResolutionError(
239357
- ref.id,
239358
- [filePath],
239359
- `${filename} is not present in ${ref.kind === "snap" ? "snapshot" : ref.kind === "rec" ? "recording" : "trace"} ${ref.id}`,
239360
- creatingCommandFor(filename)
239361
- );
239362
- }
239363
- return filePath;
239364
- }
239365
- function artifactExists(ref, filename) {
239366
- return fs23.existsSync(assertUnderCaptureRoot(path28.join(ref.dir, filename)));
239367
- }
239368
- function readJsonArtifactFile(ref, filename) {
239369
- const filePath = artifactPath(ref, filename);
239370
- let raw;
239371
- try {
239372
- raw = fs23.readFileSync(filePath, "utf-8");
239373
- } catch (err) {
239374
- throw new ArtifactResolutionError(
239375
- ref.id,
239376
- [filePath],
239377
- `could not read ${filename}: ${err instanceof Error ? err.message : String(err)}`,
239378
- creatingCommandFor(filename)
239379
- );
239380
- }
239381
- try {
239382
- return JSON.parse(raw);
239383
- } catch (err) {
239384
- throw new ArtifactResolutionError(
239385
- ref.id,
239386
- [filePath],
239387
- `${filename} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
239388
- creatingCommandFor(filename)
239389
- );
239390
- }
239391
- }
239392
- function readNdjsonArtifactFile(ref, filename) {
239393
- const filePath = artifactPath(ref, filename);
239394
- let raw;
239395
- try {
239396
- raw = fs23.readFileSync(filePath, "utf-8");
239397
- } catch (err) {
239398
- throw new ArtifactResolutionError(
239399
- ref.id,
239400
- [filePath],
239401
- `could not read ${filename}: ${err instanceof Error ? err.message : String(err)}`,
239402
- creatingCommandFor(filename)
239403
- );
239404
- }
239405
- const lines = raw.split("\n").filter((l) => l.trim().length > 0);
239406
- return lines.map((line4, i) => {
239407
- try {
239408
- return JSON.parse(line4);
239409
- } catch (err) {
239410
- throw new ArtifactResolutionError(
239411
- ref.id,
239412
- [filePath],
239413
- `${filename} line ${i + 1} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
239414
- creatingCommandFor(filename)
239415
- );
239416
- }
239417
- });
239418
- }
239419
- function assertKind(ref, kind, fnName) {
239420
- if (ref.kind !== kind) {
239421
- throw new TypeError(`${fnName} expects a ${kind} ref, got a ${ref.kind} ref (${ref.id})`);
239422
- }
239423
- }
239424
- function readMeta(ref) {
239425
- return readJsonArtifactFile(ref, "meta.json");
239426
- }
239427
- function readGeometry(ref) {
239428
- assertKind(ref, "snap", "readGeometry");
239429
- return readJsonArtifactFile(ref, "geometry.json");
239430
- }
239431
- function readStyles(ref) {
239432
- assertKind(ref, "snap", "readStyles");
239433
- return readJsonArtifactFile(ref, "styles.json");
239434
- }
239435
- function readHittest(ref) {
239436
- assertKind(ref, "snap", "readHittest");
239437
- return readJsonArtifactFile(ref, "hittest.json");
239438
- }
239439
- function readText(ref) {
239440
- assertKind(ref, "snap", "readText");
239441
- return readJsonArtifactFile(ref, "text.json");
239442
- }
239443
- function readForms(ref) {
239444
- assertKind(ref, "snap", "readForms");
239445
- return readJsonArtifactFile(ref, "forms.json");
239446
- }
239447
- function readAnimation(ref) {
239448
- assertKind(ref, "snap", "readAnimation");
239449
- return readJsonArtifactFile(ref, "animation.json");
239450
- }
239451
- function readAx(ref) {
239452
- assertKind(ref, "snap", "readAx");
239453
- return readJsonArtifactFile(ref, "ax.json");
239454
- }
239455
- function readQueries(ref) {
239456
- assertKind(ref, "snap", "readQueries");
239457
- return readJsonArtifactFile(ref, "queries.json");
239458
- }
239459
- function readFocus(ref) {
239460
- assertKind(ref, "snap", "readFocus");
239461
- return readJsonArtifactFile(ref, "focus.json");
239462
- }
239463
- function readScroll(ref) {
239464
- assertKind(ref, "snap", "readScroll");
239465
- return readJsonArtifactFile(ref, "scroll.json");
239466
- }
239467
- function readLayers(ref) {
239468
- assertKind(ref, "snap", "readLayers");
239469
- return readJsonArtifactFile(ref, "layers.json");
239470
- }
239471
- function readStates(ref) {
239472
- assertKind(ref, "snap", "readStates");
239473
- return readJsonArtifactFile(ref, "states.json");
239474
- }
239475
- function readPixels(ref) {
239476
- assertKind(ref, "snap", "readPixels");
239477
- return readJsonArtifactFile(ref, "pixels.json");
239478
- }
239479
- function readMedia(ref) {
239480
- assertKind(ref, "snap", "readMedia");
239481
- return readJsonArtifactFile(ref, "media.json");
239482
- }
239483
- function readRects(ref) {
239484
- assertKind(ref, "rec", "readRects");
239485
- return readNdjsonArtifactFile(ref, "rects.jsonl");
239486
- }
239487
- function readEvents(ref) {
239488
- assertKind(ref, "rec", "readEvents");
239489
- return readNdjsonArtifactFile(ref, "events.jsonl");
239490
- }
239491
- function readMarkers(ref) {
239492
- assertKind(ref, "rec", "readMarkers");
239493
- return readJsonArtifactFile(ref, "markers.json");
239494
- }
239495
- function unstableRegionsFor(ref) {
239496
- const geometry = readGeometry(ref);
239497
- return geometry.unstableRegions ?? [];
239498
- }
239499
- function rectsOverlap(a, b) {
239500
- return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
239501
- }
239502
- function annotateUnstableFacts(facts, regions) {
239503
- return facts.map((fact4) => {
239504
- const caveats = [];
239505
- for (const region of regions) {
239506
- const byElement = fact4.elementId !== void 0 && (region.elementIds?.includes(fact4.elementId) ?? false);
239507
- const byRect = fact4.rect !== void 0 && region.rect !== void 0 && rectsOverlap(fact4.rect, region.rect);
239508
- if (byElement || byRect) {
239509
- caveats.push({ regionId: region.id, selector: region.selector, reason: region.reason });
239510
- }
239511
- }
239512
- return { fact: fact4, caveats };
239513
- });
239514
- }
239515
- var fs23, path28, ArtifactResolutionError, SNAP_CONFIG, REC_CONFIG, TRACE_CONFIG, CREATING_COMMAND_BY_FILE;
239516
- var init_artifact = __esm({
239517
- "src/output/artifact.ts"() {
239518
- fs23 = __toESM(require("fs"));
239519
- path28 = __toESM(require("path"));
239520
- init_artifacts();
239521
- init_session_context();
239522
- ArtifactResolutionError = class extends Error {
239523
- ref;
239524
- searched;
239525
- creatingCommand;
239526
- constructor(ref, searched, detail, creatingCommand) {
239527
- const parts = [`could not resolve ref ${JSON.stringify(ref)}: ${detail}`];
239528
- if (searched.length) parts.push(`looked at: ${searched.join(", ")}`);
239529
- if (creatingCommand) parts.push(`create it with: ${creatingCommand}`);
239530
- super(parts.join(" \u2014 "));
239531
- this.name = "ArtifactResolutionError";
239532
- this.ref = ref;
239533
- this.searched = searched;
239534
- this.creatingCommand = creatingCommand;
239535
- }
239536
- };
239537
- SNAP_CONFIG = {
239538
- kind: "snap",
239539
- idSegments: ["measure", "snaps"],
239540
- creatingCommand: "capture measure snap",
239541
- label: "snapshot"
239542
- };
239543
- REC_CONFIG = {
239544
- kind: "rec",
239545
- idSegments: ["motion", "recs"],
239546
- creatingCommand: "capture motion rec",
239547
- label: "recording"
239548
- };
239549
- TRACE_CONFIG = {
239550
- kind: "trace",
239551
- idSegments: ["perf", "traces"],
239552
- creatingCommand: "capture perf trace",
239553
- label: "trace"
239554
- };
239555
- CREATING_COMMAND_BY_FILE = {
239556
- "geometry.json": "capture measure snap",
239557
- "styles.json": "capture measure snap",
239558
- "hittest.json": "capture measure snap",
239559
- "text.json": "capture measure snap",
239560
- "forms.json": "capture measure snap",
239561
- "animation.json": "capture measure snap",
239562
- "ax.json": "capture measure snap",
239563
- "media.json": "capture measure snap",
239564
- "queries.json": "capture measure snap",
239565
- "focus.json": "capture measure snap",
239566
- "scroll.json": "capture measure snap",
239567
- "layers.json": "capture measure snap",
239568
- "states.json": "capture measure snap --state <state>",
239569
- "churn.json": "capture measure snap (written only when the page did not settle)",
239570
- "pixels.json": "capture measure snap --pixels",
239571
- "dom.html": "capture measure snap",
239572
- "screenshot.png": "capture measure snap",
239573
- "meta.json": "capture measure snap (or capture motion rec for a recording)",
239574
- "rects.jsonl": "capture motion rec",
239575
- "events.jsonl": "capture motion rec",
239576
- "markers.json": "capture motion rec",
239577
- "trace.json": "capture perf trace"
239578
- };
239579
- }
239580
- });
239581
-
239582
239614
  // src/cdp/commands/measure/snap.ts
239583
239615
  function generateSnapId() {
239584
239616
  return `snap-${Date.now().toString(36)}-${(0, import_crypto2.randomBytes)(4).toString("hex")}`;
@@ -267481,10 +267513,10 @@ Selecting a noun: session, page, and tab address the live browser; measure, moti
267481
267513
 
267482
267514
  I/O contract: flags and positionals on input; one rendered prose block on stdout. --json mirrors the same result as JSON, but the rendered block is the contract. stderr carries in-flight diagnostics only. --artifact-dir <path> selects the root for a session bundle (\`session start\`) or a sessionless artifact-producing leaf's one-shot bundle; leaf help states when it writes one. --out always names one caller-owned file, never a bundle directory.
267483
267515
 
267484
- A CDP-enabled browser must be running; \`capture tab list\` is the probe, and \`capture tab launch\` starts one capture owns and reaps when nothing is (never hand-roll a detached browser \u2014 nothing reaps that). Capture never signals a browser it did not start, so attaching to your own with --port is unchanged. CDP_PORT / CDP_TARGET pin the browser + tab for target-selecting commands \u2014 precedence: explicit flag > active session > env. \`session start\` adopts only an explicit --target.`;
267516
+ A CDP-enabled browser must be running; \`capture tab list\` is the probe, and \`capture tab launch\` starts one capture owns and reaps when nothing is (never hand-roll a detached browser \u2014 nothing reaps that). Capture never signals a browser it did not start, so attaching to your own with --port is unchanged. CDP_PORT / CDP_TARGET pin the browser + tab for target-selecting commands \u2014 precedence: explicit flag > active session > env. \`session start\` adopts only an explicit --target. When CRTR_NODE_ID is set (a crtr agent node), a browser command with no --port, no --target and no active session of that node is refused instead of falling back to CDP_PORT, CDP_TARGET or localhost discovery, because the browser found could belong to another node: run \`capture session start --url <url>\`, or pass --port or --target. \`capture tab list\` and commands that only read a recorded artifact are not refused.`;
267485
267517
  }
267486
267518
  function printVersion() {
267487
- const declared = "1.4.22";
267519
+ const declared = "1.4.23";
267488
267520
  if (declared) {
267489
267521
  console.log(declared);
267490
267522
  return;