@crouton-kit/capture 1.4.21 → 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 {
@@ -9413,7 +9754,12 @@ function validateCliInvocation(parsed) {
9413
9754
  }
9414
9755
  if (leaf === "launch" || leaf === "list") requireCount(values, 0, 0, `tab ${leaf}`);
9415
9756
  else if (leaf === "quit") requireCount(values, 0, 1, "tab quit");
9416
- else requireCount(values, 1, 1, `tab ${leaf}`);
9757
+ else if (leaf === "close" && parsed.target !== void 0) {
9758
+ requireCount(values, 0, 1, "tab close");
9759
+ if (values.length === 1 && values[0] !== parsed.target) {
9760
+ throw schemaInput("tab close", `<target>=${values[0]}, --target=${parsed.target}`, "one tab: the positional <target> or --target, not two different ids", "<target> or --target");
9761
+ }
9762
+ } else requireCount(values, 1, 1, `tab ${leaf}`);
9417
9763
  if (leaf === "network" && values[0] !== "offline" && values[0] !== "online") {
9418
9764
  throw schemaInput("tab network", values[0] ?? "(missing)", "offline or online", "<mode>");
9419
9765
  }
@@ -9788,6 +10134,36 @@ function needsEndpointResolution(parsed) {
9788
10134
  if (parsed.command === "tab" && (parsed.positional[0] === "launch" || parsed.positional[0] === "quit")) return false;
9789
10135
  return true;
9790
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
+ }
9791
10167
  function resolveCliContext(parsed) {
9792
10168
  if (!needsEndpointResolution(parsed)) return parsed;
9793
10169
  const sessionStart = parsed.command === "session" && parsed.positional[0] === "start";
@@ -9803,6 +10179,7 @@ function resolveCliContext(parsed) {
9803
10179
  parsed.portSource = "session";
9804
10180
  }
9805
10181
  }
10182
+ refuseUnownedEndpoint(parsed, sessionStart);
9806
10183
  if (parsed.port === void 0 && process.env.CDP_PORT !== void 0) {
9807
10184
  parsed.port = schemaInteger(leafCommand(parsed.command, parsed.positional), process.env.CDP_PORT, "CDP_PORT", 1, 65535);
9808
10185
  parsed.portSource = "env";
@@ -9819,6 +10196,7 @@ var init_args = __esm({
9819
10196
  "src/cdp/args.ts"() {
9820
10197
  init_errors();
9821
10198
  init_session_context();
10199
+ init_artifact();
9822
10200
  init_leaf_grammar();
9823
10201
  VALUE_FLAGS = /* @__PURE__ */ new Set([
9824
10202
  "--port",
@@ -9904,7 +10282,7 @@ var init_gate_guard = __esm({
9904
10282
 
9905
10283
  // src/session/coordinator.ts
9906
10284
  function scopeLifecycleLockPath() {
9907
- 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"}`);
9908
10286
  }
9909
10287
  async function withSessionScopeLifecycle(action) {
9910
10288
  const lock = await acquirePrivateLock(scopeLifecycleLockPath(), {
@@ -9918,10 +10296,10 @@ async function withSessionScopeLifecycle(action) {
9918
10296
  }
9919
10297
  }
9920
10298
  function statePath(sessionDir2) {
9921
- return path5.join(assertUnderCaptureRoot(sessionDir2), ".operations.json");
10299
+ return path6.join(assertUnderCaptureRoot(sessionDir2), ".operations.json");
9922
10300
  }
9923
10301
  function lockPath(sessionDir2) {
9924
- return path5.join(assertUnderCaptureRoot(sessionDir2), ".operations.lock");
10302
+ return path6.join(assertUnderCaptureRoot(sessionDir2), ".operations.lock");
9925
10303
  }
9926
10304
  function isBirth(value) {
9927
10305
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
@@ -9980,7 +10358,7 @@ async function admitSessionOperation(sessionDir2) {
9980
10358
  };
9981
10359
  }
9982
10360
  async function beginSessionStop(sessionDir2) {
9983
- const stopLock = await acquirePrivateLock(path5.join(assertUnderCaptureRoot(sessionDir2), ".stop.lock"), {
10361
+ const stopLock = await acquirePrivateLock(path6.join(assertUnderCaptureRoot(sessionDir2), ".stop.lock"), {
9984
10362
  acquireTimeoutMs: 12e4,
9985
10363
  leaseMs: 1e3
9986
10364
  });
@@ -10020,7 +10398,7 @@ async function beginSessionStop(sessionDir2) {
10020
10398
  };
10021
10399
  }
10022
10400
  async function withSessionLifecycle(sessionDir2, action, seams2 = {}) {
10023
- const lock = await acquirePrivateLock(path5.join(assertUnderCaptureRoot(sessionDir2), ".lifecycle.lock"), {
10401
+ const lock = await acquirePrivateLock(path6.join(assertUnderCaptureRoot(sessionDir2), ".lifecycle.lock"), {
10024
10402
  acquireTimeoutMs: seams2.acquireTimeoutMs ?? 3e4,
10025
10403
  leaseMs: seams2.leaseMs ?? 3e4,
10026
10404
  pidBirthProvider: seams2.pidBirthProvider,
@@ -10034,11 +10412,11 @@ async function withSessionLifecycle(sessionDir2, action, seams2 = {}) {
10034
10412
  lock.release();
10035
10413
  }
10036
10414
  }
10037
- var crypto3, path5;
10415
+ var crypto3, path6;
10038
10416
  var init_coordinator = __esm({
10039
10417
  "src/session/coordinator.ts"() {
10040
10418
  crypto3 = __toESM(require("node:crypto"));
10041
- path5 = __toESM(require("node:path"));
10419
+ path6 = __toESM(require("node:path"));
10042
10420
  init_errors();
10043
10421
  init_artifacts();
10044
10422
  }
@@ -10566,7 +10944,7 @@ function validateHarAppendBatch(value, source) {
10566
10944
  return { entries, incompleteLifecycles };
10567
10945
  }
10568
10946
  function captureVersion() {
10569
- const version = "1.4.21";
10947
+ const version = "1.4.23";
10570
10948
  return typeof version === "string" && version.length > 0 ? version : "development";
10571
10949
  }
10572
10950
  function token() {
@@ -10584,15 +10962,15 @@ function vStoreHeader(value, source) {
10584
10962
  return { format: "capture-live-har-ndjson", version: 1, creator: { name: "capture", version } };
10585
10963
  }
10586
10964
  function harFilePath(id) {
10587
- if (path6.isAbsolute(id)) return assertUnderCaptureRoot(id);
10588
- if (path6.basename(id) === id) return assertUnderCaptureRoot(path6.resolve(CAPTURE_ROOT, id));
10589
- if (id.endsWith(".json") && id.includes(path6.sep)) return assertUnderCaptureRoot(id);
10590
- 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));
10591
10969
  }
10592
10970
  async function createHarRecording(sessionDir2 = CAPTURE_ROOT) {
10593
- const sessionAbsolute = path6.resolve(sessionDir2);
10971
+ const sessionAbsolute = path7.resolve(sessionDir2);
10594
10972
  assertUnderCaptureRoot(sessionAbsolute);
10595
- const harDir = path6.join(sessionAbsolute, ".har");
10973
+ const harDir = path7.join(sessionAbsolute, ".har");
10596
10974
  ensurePrivateDir(harDir);
10597
10975
  const header = {
10598
10976
  format: "capture-live-har-ndjson",
@@ -10602,7 +10980,7 @@ async function createHarRecording(sessionDir2 = CAPTURE_ROOT) {
10602
10980
  const headerLine = `${JSON.stringify(header)}
10603
10981
  `;
10604
10982
  for (; ; ) {
10605
- const candidate = path6.join(harDir, `${token()}.json`);
10983
+ const candidate = path7.join(harDir, `${token()}.json`);
10606
10984
  try {
10607
10985
  createPrivateFile(candidate, headerLine);
10608
10986
  } catch (error4) {
@@ -10665,39 +11043,39 @@ async function appendToHarRecording(id, batch) {
10665
11043
  async function deleteHarRecording(id) {
10666
11044
  unlinkPrivateFile(harFilePath(id));
10667
11045
  }
10668
- var import_node_crypto, path6, MAX_BODY_BYTES, HAR_DIR;
11046
+ var import_node_crypto, path7, MAX_BODY_BYTES, HAR_DIR;
10669
11047
  var init_har_manager = __esm({
10670
11048
  "src/har-manager.ts"() {
10671
11049
  import_node_crypto = __toESM(require("node:crypto"));
10672
- path6 = __toESM(require("path"));
11050
+ path7 = __toESM(require("path"));
10673
11051
  init_artifacts();
10674
11052
  MAX_BODY_BYTES = 262144;
10675
- HAR_DIR = path6.join(CAPTURE_ROOT, ".har");
11053
+ HAR_DIR = path7.join(CAPTURE_ROOT, ".har");
10676
11054
  }
10677
11055
  });
10678
11056
 
10679
11057
  // src/cdp/bridge/spawn.ts
10680
11058
  function bridgeSocketPath(sessionDir2) {
10681
- return path7.join(sessionDir2, "bridge.sock");
11059
+ return path8.join(sessionDir2, "bridge.sock");
10682
11060
  }
10683
11061
  function collectorSocketDir() {
10684
11062
  const dir = "/tmp/capture-sockets";
10685
11063
  try {
10686
- fs5.mkdirSync(dir, { mode: 448 });
11064
+ fs6.mkdirSync(dir, { mode: 448 });
10687
11065
  } catch (error4) {
10688
11066
  if (error4.code !== "EEXIST") throw error4;
10689
11067
  }
10690
- const stat = fs5.lstatSync(dir);
11068
+ const stat = fs6.lstatSync(dir);
10691
11069
  if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error(`collector socket directory is not a real directory: ${dir}`);
10692
11070
  if (typeof process.getuid === "function" && stat.uid !== process.getuid()) throw new Error(`collector socket directory is not owned by this user: ${dir}`);
10693
- fs5.chmodSync(dir, 448);
11071
+ fs6.chmodSync(dir, 448);
10694
11072
  return dir;
10695
11073
  }
10696
11074
  function shortSocketName(scopeDir) {
10697
- 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);
10698
11076
  }
10699
11077
  function collectorHostSocketPath(sessionDir2) {
10700
- return path7.join(collectorSocketDir(), `host-${shortSocketName(sessionDir2)}.sock`);
11078
+ return path8.join(collectorSocketDir(), `host-${shortSocketName(sessionDir2)}.sock`);
10701
11079
  }
10702
11080
  async function startBridge(sessionDir2, port, timeoutMs = 5e3) {
10703
11081
  const socketPath = bridgeSocketPath(sessionDir2);
@@ -10713,7 +11091,7 @@ async function startBridge(sessionDir2, port, timeoutMs = 5e3) {
10713
11091
  throw new Error("Failed to spawn CDP bridge process.");
10714
11092
  }
10715
11093
  const start2 = Date.now();
10716
- while (!fs5.existsSync(socketPath)) {
11094
+ while (!fs6.existsSync(socketPath)) {
10717
11095
  if (Date.now() - start2 > timeoutMs) {
10718
11096
  stopBridge(pid, socketPath);
10719
11097
  throw new Error(
@@ -10729,7 +11107,7 @@ async function startCollectorHost(socketPath, port, targetId, sessionDir2, timeo
10729
11107
  child.unref();
10730
11108
  if (!child.pid) throw new Error("Failed to spawn collector host process.");
10731
11109
  const started = Date.now();
10732
- while (!fs5.existsSync(socketPath)) {
11110
+ while (!fs6.existsSync(socketPath)) {
10733
11111
  if (Date.now() - started > timeoutMs) {
10734
11112
  stopBridge(child.pid, socketPath);
10735
11113
  throw new Error(`Collector host (pid ${child.pid}) did not come up within ${timeoutMs}ms.`);
@@ -10747,18 +11125,18 @@ function stopBridge(pid, socketPath) {
10747
11125
  }
10748
11126
  if (socketPath) {
10749
11127
  try {
10750
- if (fs5.lstatSync(socketPath).isSocket()) fs5.unlinkSync(socketPath);
11128
+ if (fs6.lstatSync(socketPath).isSocket()) fs6.unlinkSync(socketPath);
10751
11129
  } catch {
10752
11130
  }
10753
11131
  }
10754
11132
  }
10755
- var import_child_process2, crypto5, fs5, path7;
11133
+ var import_child_process2, crypto5, fs6, path8;
10756
11134
  var init_spawn = __esm({
10757
11135
  "src/cdp/bridge/spawn.ts"() {
10758
11136
  import_child_process2 = require("child_process");
10759
11137
  crypto5 = __toESM(require("crypto"));
10760
- fs5 = __toESM(require("fs"));
10761
- path7 = __toESM(require("path"));
11138
+ fs6 = __toESM(require("fs"));
11139
+ path8 = __toESM(require("path"));
10762
11140
  }
10763
11141
  });
10764
11142
 
@@ -10778,7 +11156,7 @@ function socketDir(create) {
10778
11156
  }
10779
11157
  function socketPathForToken(token2, createDir) {
10780
11158
  if (!LOG_TAILER_SOCKET_TOKEN.test(token2)) throw new Error("invalid log tailer socket token");
10781
- return path8.join(socketDir(createDir), `${token2}.sock`);
11159
+ return path9.join(socketDir(createDir), `${token2}.sock`);
10782
11160
  }
10783
11161
  function requireRegisteredLogTailer(value) {
10784
11162
  const record5 = parseRegisteredLogTailer(value);
@@ -10799,23 +11177,23 @@ function ownership(record5, provider) {
10799
11177
  return observed.status === "found" && sameBirth(observed.identity, record5.birth) ? "same" : "gone";
10800
11178
  }
10801
11179
  function socketIdentity(socketPath) {
10802
- const stat = fs6.lstatSync(socketPath, { bigint: true });
11180
+ const stat = fs7.lstatSync(socketPath, { bigint: true });
10803
11181
  if (!stat.isSocket()) throw new Error(`log tailer control path is not a socket: ${socketPath}`);
10804
11182
  return { dev: stat.dev.toString(), ino: stat.ino.toString() };
10805
11183
  }
10806
11184
  function unlinkRecordedSocket(record5) {
10807
11185
  let current;
10808
11186
  try {
10809
- current = fs6.lstatSync(record5.socketPath, { bigint: true });
11187
+ current = fs7.lstatSync(record5.socketPath, { bigint: true });
10810
11188
  } catch (error4) {
10811
11189
  if (error4.code === "ENOENT") return;
10812
11190
  throw error4;
10813
11191
  }
10814
11192
  if (!current.isSocket() || current.dev.toString() !== record5.socketDev || current.ino.toString() !== record5.socketIno) return;
10815
- fs6.unlinkSync(record5.socketPath);
11193
+ fs7.unlinkSync(record5.socketPath);
10816
11194
  }
10817
11195
  function closeOwnedFd(fd) {
10818
- if (fd !== void 0) fs6.closeSync(fd);
11196
+ if (fd !== void 0) fs7.closeSync(fd);
10819
11197
  return void 0;
10820
11198
  }
10821
11199
  function awaitChildExit(child, timeoutMs) {
@@ -10902,8 +11280,8 @@ function awaitReadiness(child, timeoutMs) {
10902
11280
  });
10903
11281
  }
10904
11282
  async function startSessionLogTailer(options) {
10905
- const logsDir = ensurePrivateDir(path8.join(options.sessionDir, "logs"));
10906
- 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`);
10907
11285
  const socketToken = crypto6.randomBytes(8).toString("hex");
10908
11286
  const socketPath = socketPathForToken(socketToken, true);
10909
11287
  const nonce = crypto6.randomBytes(24).toString("hex");
@@ -11128,7 +11506,7 @@ function nonceMatches(expected, candidate) {
11128
11506
  }
11129
11507
  function writeReadiness(message) {
11130
11508
  try {
11131
- fs6.writeSync(READINESS_FD, message);
11509
+ fs7.writeSync(READINESS_FD, message);
11132
11510
  return true;
11133
11511
  } catch {
11134
11512
  return false;
@@ -11258,7 +11636,7 @@ async function runLogTailer(argv) {
11258
11636
  const owned = ownedSocket;
11259
11637
  orphanCheck = setInterval(() => {
11260
11638
  try {
11261
- const current = fs6.lstatSync(ownedSocketPath, { bigint: true });
11639
+ const current = fs7.lstatSync(ownedSocketPath, { bigint: true });
11262
11640
  if (current.isSocket() && current.dev.toString() === owned.dev && current.ino.toString() === owned.ino) return;
11263
11641
  } catch (error4) {
11264
11642
  if (error4.code !== "ENOENT") return;
@@ -11279,7 +11657,7 @@ async function runLogTailer(argv) {
11279
11657
  });
11280
11658
  reader.on("line", (line4) => {
11281
11659
  try {
11282
- fs6.writeSync(1, `${(/* @__PURE__ */ new Date()).toISOString()} ${line4}
11660
+ fs7.writeSync(1, `${(/* @__PURE__ */ new Date()).toISOString()} ${line4}
11283
11661
  `);
11284
11662
  } catch {
11285
11663
  requestStop?.("fatal");
@@ -11296,7 +11674,7 @@ async function runLogTailer(argv) {
11296
11674
  if (!writeReadiness("ready\n")) throw new Error("parent closed the readiness channel");
11297
11675
  ready = true;
11298
11676
  try {
11299
- fs6.closeSync(READINESS_FD);
11677
+ fs7.closeSync(READINESS_FD);
11300
11678
  } catch {
11301
11679
  }
11302
11680
  confirmDeadline = setTimeout(() => {
@@ -11331,7 +11709,7 @@ async function runLogTailer(argv) {
11331
11709
  if (socketPath && ownedSocket) {
11332
11710
  try {
11333
11711
  const current = socketIdentity(socketPath);
11334
- 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);
11335
11713
  } catch (error4) {
11336
11714
  if (error4.code !== "ENOENT") exitCode = 1;
11337
11715
  }
@@ -11339,14 +11717,14 @@ async function runLogTailer(argv) {
11339
11717
  process.exitCode = exitCode;
11340
11718
  }
11341
11719
  }
11342
- 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;
11343
11721
  var init_log_tailer = __esm({
11344
11722
  "src/session/log-tailer.ts"() {
11345
11723
  import_node_child_process3 = require("node:child_process");
11346
11724
  crypto6 = __toESM(require("node:crypto"));
11347
- fs6 = __toESM(require("node:fs"));
11725
+ fs7 = __toESM(require("node:fs"));
11348
11726
  net2 = __toESM(require("node:net"));
11349
- path8 = __toESM(require("node:path"));
11727
+ path9 = __toESM(require("node:path"));
11350
11728
  readline = __toESM(require("node:readline"));
11351
11729
  init_errors();
11352
11730
  init_artifacts();
@@ -11368,16 +11746,16 @@ var init_log_tailer = __esm({
11368
11746
 
11369
11747
  // src/cdp/host/collectors/heap.ts
11370
11748
  function reconstructHeapSnapshot(dir) {
11371
- const snapshot = path9.join(dir, "snapshot.heapsnapshot");
11372
- if (!fs7.existsSync(snapshot)) return { summary: { bytes: 0 }, files: [] };
11373
- 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;
11374
11752
  return { summary: { bytes }, files: [{ name: "snapshot.heapsnapshot", bytes }] };
11375
11753
  }
11376
- var fs7, path9, HeapCollector;
11754
+ var fs8, path10, HeapCollector;
11377
11755
  var init_heap = __esm({
11378
11756
  "src/cdp/host/collectors/heap.ts"() {
11379
- fs7 = __toESM(require("node:fs"));
11380
- path9 = __toESM(require("node:path"));
11757
+ fs8 = __toESM(require("node:fs"));
11758
+ path10 = __toESM(require("node:path"));
11381
11759
  HeapCollector = class {
11382
11760
  kind = "heap";
11383
11761
  claims = ["heap-snapshot"];
@@ -11566,10 +11944,10 @@ function matchRule(rules, request) {
11566
11944
  function headerEntries(headers2) {
11567
11945
  return Object.entries(headers2).map(([name, value]) => ({ name, value }));
11568
11946
  }
11569
- var path10, MAX_BODY_BYTES2, RESOURCE_TYPES, FAIL_REASONS, InterceptCollector;
11947
+ var path11, MAX_BODY_BYTES2, RESOURCE_TYPES, FAIL_REASONS, InterceptCollector;
11570
11948
  var init_intercept = __esm({
11571
11949
  "src/cdp/host/collectors/intercept.ts"() {
11572
- path10 = __toESM(require("node:path"));
11950
+ path11 = __toESM(require("node:path"));
11573
11951
  init_artifacts();
11574
11952
  MAX_BODY_BYTES2 = 10 * 1024 * 1024;
11575
11953
  RESOURCE_TYPES = /* @__PURE__ */ new Set(["Document", "Stylesheet", "Image", "Media", "Font", "Script", "TextTrack", "XHR", "Fetch", "Prefetch", "EventSource", "WebSocket", "Manifest", "SignedExchange", "Ping", "CSPViolationReport", "Preflight", "Other"]);
@@ -11602,8 +11980,8 @@ var init_intercept = __esm({
11602
11980
  if (!config3) throw new Error("intercept collector has no rule configuration");
11603
11981
  this.context = context;
11604
11982
  this.ruleMatches = Array.from({ length: config3.rules.length }, () => 0);
11605
- writePrivateFile(path10.join(context.dir, "rules.json"), config3.source);
11606
- 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"), "");
11607
11985
  context.client.on("Fetch.requestPaused", this.onPaused);
11608
11986
  try {
11609
11987
  await context.client.send("Fetch.enable", { patterns: [{ urlPattern: "*", requestStage: "Request" }] });
@@ -11811,10 +12189,10 @@ async function runBridgeServer(socketPath, port) {
11811
12189
  installProcessCleanup(cleanup, client);
11812
12190
  }
11813
12191
  function prepareSocketPath(socketPath) {
11814
- fs8.mkdirSync(path11.dirname(socketPath), { recursive: true });
12192
+ fs9.mkdirSync(path12.dirname(socketPath), { recursive: true });
11815
12193
  try {
11816
- if (!fs8.lstatSync(socketPath).isSocket()) throw new Error(`socket path is not a Unix socket: ${socketPath}`);
11817
- fs8.unlinkSync(socketPath);
12194
+ if (!fs9.lstatSync(socketPath).isSocket()) throw new Error(`socket path is not a Unix socket: ${socketPath}`);
12195
+ fs9.unlinkSync(socketPath);
11818
12196
  } catch (error4) {
11819
12197
  if (error4.code !== "ENOENT") throw error4;
11820
12198
  }
@@ -11845,7 +12223,7 @@ function closeNdjsonSocket(server2, socketPath) {
11845
12223
  } catch {
11846
12224
  }
11847
12225
  try {
11848
- if (fs8.lstatSync(socketPath).isSocket()) fs8.unlinkSync(socketPath);
12226
+ if (fs9.lstatSync(socketPath).isSocket()) fs9.unlinkSync(socketPath);
11849
12227
  } catch {
11850
12228
  }
11851
12229
  }
@@ -11868,12 +12246,12 @@ async function resolvePort(port) {
11868
12246
  const { detectCdpPort: detectCdpPort2 } = await Promise.resolve().then(() => (init_detect(), detect_exports));
11869
12247
  return detectCdpPort2();
11870
12248
  }
11871
- var fs8, net3, path11, EventBroker;
12249
+ var fs9, net3, path12, EventBroker;
11872
12250
  var init_server = __esm({
11873
12251
  "src/cdp/bridge/server.ts"() {
11874
- fs8 = __toESM(require("fs"));
12252
+ fs9 = __toESM(require("fs"));
11875
12253
  net3 = __toESM(require("net"));
11876
- path11 = __toESM(require("path"));
12254
+ path12 = __toESM(require("path"));
11877
12255
  init_targets();
11878
12256
  EventBroker = class {
11879
12257
  constructor(client) {
@@ -12668,7 +13046,7 @@ function wallTime(value) {
12668
13046
  }
12669
13047
  }
12670
13048
  function captureVersion2() {
12671
- const version = "1.4.21";
13049
+ const version = "1.4.23";
12672
13050
  return typeof version === "string" && version.length > 0 ? version : "development";
12673
13051
  }
12674
13052
  function deepFreeze(value) {
@@ -13424,11 +13802,11 @@ function buildTakeRectElementsExpression(nonce, frameIndex) {
13424
13802
  function buildTakeResizeTargetsExpression(nonce, seq) {
13425
13803
  return `(function(){var k='__captureRecorder_'+${JSON.stringify(nonce)};var h=window[k];return h&&h.takeResizeTargets?h.takeResizeTargets(${JSON.stringify(seq)}):[];})()`;
13426
13804
  }
13427
- 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;
13428
13806
  var init_motion = __esm({
13429
13807
  "src/cdp/host/collectors/motion.ts"() {
13430
13808
  crypto7 = __toESM(require("crypto"));
13431
- path12 = __toESM(require("path"));
13809
+ path13 = __toESM(require("path"));
13432
13810
  init_domains();
13433
13811
  init_timing();
13434
13812
  init_server();
@@ -13538,9 +13916,9 @@ var init_motion = __esm({
13538
13916
  constructor(opts) {
13539
13917
  this.client = opts.client;
13540
13918
  this.recDir = ensurePrivateDir(opts.recDir);
13541
- this.framesDir = ensurePrivateDir(path12.join(this.recDir, "frames"));
13542
- this.eventsPath = path12.join(this.recDir, "events.jsonl");
13543
- 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");
13544
13922
  this.events = new EventBroker(this.client);
13545
13923
  }
13546
13924
  async start() {
@@ -14001,7 +14379,7 @@ var init_motion = __esm({
14001
14379
  this.updateBaselinesPending();
14002
14380
  }
14003
14381
  const frameName = `frame-${String(frameIndex).padStart(6, "0")}.png`;
14004
- writeBinaryPrivate(path12.join(this.framesDir, frameName), Buffer.from(params.data, "base64"));
14382
+ writeBinaryPrivate(path13.join(this.framesDir, frameName), Buffer.from(params.data, "base64"));
14005
14383
  try {
14006
14384
  const sample = await this.sampleRects(frameIndex);
14007
14385
  const elements = this.sanitizeRectSample(sample.facts, sample.backendNodeIds);
@@ -14401,10 +14779,10 @@ var init_motion = __esm({
14401
14779
  if (config3.viewport) {
14402
14780
  const { width, height } = config3.viewport;
14403
14781
  if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height) || width <= 0 || height <= 0) throw new Error("motion viewport must contain positive integer width and height");
14404
- 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 });
14405
14783
  await context.client.send("Emulation.setDeviceMetricsOverride", { width, height, deviceScaleFactor: 1, mobile: false });
14406
14784
  this.viewportApplied = true;
14407
- 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 });
14408
14786
  }
14409
14787
  this.har = new HARRecorder(context.client, (batch) => appendToHarRecording(config3.harId, batch));
14410
14788
  await this.har.start();
@@ -14417,9 +14795,9 @@ var init_motion = __esm({
14417
14795
  try {
14418
14796
  await context.client.send("Emulation.clearDeviceMetricsOverride");
14419
14797
  this.viewportApplied = false;
14420
- removeArtifactTree(path12.join(context.dir, "viewport-override.json"));
14798
+ removeArtifactTree(path13.join(context.dir, "viewport-override.json"));
14421
14799
  } catch (cleanupError) {
14422
- writeJsonPrivate(path12.join(context.dir, "viewport-override.json"), {
14800
+ writeJsonPrivate(path13.join(context.dir, "viewport-override.json"), {
14423
14801
  phase: "restore-failed",
14424
14802
  targetId: context.targetId,
14425
14803
  retainOnStop: true,
@@ -14463,13 +14841,13 @@ var init_motion = __esm({
14463
14841
  if (this.viewportApplied && cause.trigger !== "explicit") {
14464
14842
  try {
14465
14843
  await this.context.client.send("Emulation.clearDeviceMetricsOverride");
14466
- removeArtifactTree(path12.join(this.context.dir, "viewport-override.json"));
14844
+ removeArtifactTree(path13.join(this.context.dir, "viewport-override.json"));
14467
14845
  viewportRestored = true;
14468
14846
  } catch {
14469
14847
  viewportRestored = false;
14470
14848
  }
14471
14849
  }
14472
- writeJsonPrivate(path12.join(this.context.dir, "markers.json"), stopped.markers);
14850
+ writeJsonPrivate(path13.join(this.context.dir, "markers.json"), stopped.markers);
14473
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: [] };
14474
14852
  }
14475
14853
  onDispatch(notice) {
@@ -14588,26 +14966,26 @@ var init_trace = __esm({
14588
14966
 
14589
14967
  // src/cdp/host/kinds.ts
14590
14968
  function motionReconstruct(dir) {
14591
- const framesDir = path13.join(dir, "frames");
14592
- 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;
14593
14971
  const bytes = (name) => {
14594
14972
  try {
14595
- return fs9.statSync(path13.join(dir, name)).size;
14973
+ return fs10.statSync(path14.join(dir, name)).size;
14596
14974
  } catch {
14597
14975
  return 0;
14598
14976
  }
14599
14977
  };
14600
- 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) }));
14601
14979
  return { summary: { frames: frames2, state: "orphaned-finalized" }, files };
14602
14980
  }
14603
14981
  function collectorKind(kind) {
14604
14982
  return COLLECTOR_KINDS[kind];
14605
14983
  }
14606
- var fs9, path13, motion, trace, heap, intercept, COLLECTOR_KINDS;
14984
+ var fs10, path14, motion, trace, heap, intercept, COLLECTOR_KINDS;
14607
14985
  var init_kinds = __esm({
14608
14986
  "src/cdp/host/kinds.ts"() {
14609
- fs9 = __toESM(require("node:fs"));
14610
- path13 = __toESM(require("node:path"));
14987
+ fs10 = __toESM(require("node:fs"));
14988
+ path14 = __toESM(require("node:path"));
14611
14989
  init_heap();
14612
14990
  init_intercept();
14613
14991
  init_motion();
@@ -14641,8 +15019,8 @@ var init_kinds = __esm({
14641
15019
  return new TraceCollector();
14642
15020
  },
14643
15021
  reconstruct(dir) {
14644
- const file = path13.join(dir, "trace.json");
14645
- 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;
14646
15024
  return { summary: { events: 0, windowMs: 0, navigations: 0, categories: "devtools-default" }, files: bytes ? [{ name: "trace.json", bytes }] : [] };
14647
15025
  }
14648
15026
  };
@@ -14672,12 +15050,12 @@ var init_kinds = __esm({
14672
15050
  reconstruct(dir) {
14673
15051
  const bytes = (name) => {
14674
15052
  try {
14675
- return fs9.statSync(path13.join(dir, name)).size;
15053
+ return fs10.statSync(path14.join(dir, name)).size;
14676
15054
  } catch {
14677
15055
  return 0;
14678
15056
  }
14679
15057
  };
14680
- 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) }));
14681
15059
  return { summary: { state: "orphaned-finalized" }, files };
14682
15060
  }
14683
15061
  };
@@ -14692,7 +15070,7 @@ var init_kinds = __esm({
14692
15070
 
14693
15071
  // src/cdp/host/handle.ts
14694
15072
  function collectorHostPath(sessionDir2) {
14695
- return path14.join(assertUnderCaptureRoot(sessionDir2), ".collector-host.json");
15073
+ return path15.join(assertUnderCaptureRoot(sessionDir2), ".collector-host.json");
14696
15074
  }
14697
15075
  function parseHandle(raw, sessionDir2) {
14698
15076
  if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
@@ -14716,7 +15094,7 @@ function scanCollectorHost(sessionDir2) {
14716
15094
  if (read.status === "unknown") return { classification: "unknown", handle, path: file };
14717
15095
  if (read.status === "found" && sameBirth(read.identity, handle.birth)) {
14718
15096
  try {
14719
- 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 };
14720
15098
  } catch {
14721
15099
  return { classification: "malformed", handle: null, path: file };
14722
15100
  }
@@ -14724,11 +15102,11 @@ function scanCollectorHost(sessionDir2) {
14724
15102
  }
14725
15103
  return { classification: "dead", handle, path: file };
14726
15104
  }
14727
- var fs10, path14;
15105
+ var fs11, path15;
14728
15106
  var init_handle = __esm({
14729
15107
  "src/cdp/host/handle.ts"() {
14730
- fs10 = __toESM(require("node:fs"));
14731
- path14 = __toESM(require("node:path"));
15108
+ fs11 = __toESM(require("node:fs"));
15109
+ path15 = __toESM(require("node:path"));
14732
15110
  init_spawn();
14733
15111
  init_artifacts();
14734
15112
  init_artifacts();
@@ -14741,7 +15119,7 @@ function isCollectorKind(value) {
14741
15119
  }
14742
15120
  function readCollecting(dir) {
14743
15121
  try {
14744
- 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"));
14745
15123
  if (typeof raw.id !== "string" || !isCollectorKind(raw.kind) || typeof raw.startedAt !== "string") return null;
14746
15124
  return { id: raw.id, kind: raw.kind, startedAt: raw.startedAt };
14747
15125
  } catch {
@@ -14753,12 +15131,12 @@ function collectingDirectories(root) {
14753
15131
  const walk2 = (dir) => {
14754
15132
  let entries;
14755
15133
  try {
14756
- entries = fs11.readdirSync(dir, { withFileTypes: true });
15134
+ entries = fs12.readdirSync(dir, { withFileTypes: true });
14757
15135
  } catch {
14758
15136
  return;
14759
15137
  }
14760
15138
  if (entries.some((entry) => entry.isFile() && entry.name === "collecting.json")) dirs.push(dir);
14761
- 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));
14762
15140
  };
14763
15141
  walk2(root);
14764
15142
  return dirs;
@@ -14771,7 +15149,7 @@ function reapOrphanedCollectors(sessionDir2) {
14771
15149
  try {
14772
15150
  const reconstructed = collectorKind(collecting.kind).reconstruct(dir);
14773
15151
  const summary = reconstructed.summary && typeof reconstructed.summary === "object" && !Array.isArray(reconstructed.summary) ? reconstructed.summary : { value: reconstructed.summary };
14774
- writeJsonPrivate(path15.join(dir, "meta.json"), {
15152
+ writeJsonPrivate(path16.join(dir, "meta.json"), {
14775
15153
  id: collecting.id,
14776
15154
  kind: collecting.kind,
14777
15155
  completion: "orphaned",
@@ -14782,7 +15160,7 @@ function reapOrphanedCollectors(sessionDir2) {
14782
15160
  summary,
14783
15161
  ...summary
14784
15162
  });
14785
- unlinkPrivateFile(path15.join(dir, "collecting.json"));
15163
+ unlinkPrivateFile(path16.join(dir, "collecting.json"));
14786
15164
  outcomes.push({ status: "reaped", id: collecting.id, kind: collecting.kind, dir });
14787
15165
  } catch (error4) {
14788
15166
  outcomes.push({ status: "terminal", id: collecting.id, kind: collecting.kind, dir, error: error4 instanceof Error ? error4.message : String(error4) });
@@ -14828,11 +15206,11 @@ async function stopAndReapCollectorHostAtSessionStop(sessionDir2) {
14828
15206
  return terminal(error4 instanceof Error ? error4.message : String(error4));
14829
15207
  }
14830
15208
  }
14831
- var fs11, path15;
15209
+ var fs12, path16;
14832
15210
  var init_lifecycle = __esm({
14833
15211
  "src/cdp/host/lifecycle.ts"() {
14834
- fs11 = __toESM(require("node:fs"));
14835
- path15 = __toESM(require("node:path"));
15212
+ fs12 = __toESM(require("node:fs"));
15213
+ path16 = __toESM(require("node:path"));
14836
15214
  init_spawn();
14837
15215
  init_artifacts();
14838
15216
  init_kinds();
@@ -14987,20 +15365,20 @@ function readBundleManifest(bundlePath) {
14987
15365
  return parsed;
14988
15366
  }
14989
15367
  function sessionDir(id, artifactRoot = CAPTURE_ROOT) {
14990
- return path16.join(artifactRoot, id);
15368
+ return path17.join(artifactRoot, id);
14991
15369
  }
14992
15370
  function isSessionStopped(session) {
14993
- 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"));
14994
15372
  }
14995
15373
  function createOneshotSession(kind, artifactDir) {
14996
15374
  const id = `oneshot-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
14997
15375
  const dir = sessionDir(id, artifactDir === void 0 ? CAPTURE_ROOT : registerArtifactRoot(artifactDir));
14998
- 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");
14999
15377
  ensurePrivateDir(artifactsDir);
15000
15378
  return { id, dir, kind, artifactsDir };
15001
15379
  }
15002
15380
  function readSnapMeta(snapDir, fallbackId) {
15003
- 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"));
15004
15382
  return {
15005
15383
  id: meta.id ?? fallbackId,
15006
15384
  path: snapDir,
@@ -15011,7 +15389,7 @@ function readSnapMeta(snapDir, fallbackId) {
15011
15389
  };
15012
15390
  }
15013
15391
  function readRecMeta(recDir, fallbackId) {
15014
- 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"));
15015
15393
  return {
15016
15394
  id: meta.id ?? fallbackId,
15017
15395
  path: recDir,
@@ -15023,40 +15401,40 @@ function readRecMeta(recDir, fallbackId) {
15023
15401
  };
15024
15402
  }
15025
15403
  function collectSnaps(dir) {
15026
- const snapsRoot = path16.join(dir, "measure", "snaps");
15027
- if (!fs12.existsSync(snapsRoot)) return [];
15028
- 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));
15029
15407
  }
15030
15408
  function collectRecs(dir) {
15031
- const recsRoot = path16.join(dir, "motion", "recs");
15032
- if (!fs12.existsSync(recsRoot)) return [];
15033
- 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));
15034
15412
  }
15035
15413
  function collectMocks(dir) {
15036
- const mocksRoot = path16.join(dir, "network", "mocks");
15037
- if (!fs12.existsSync(mocksRoot)) return [];
15038
- return fs12.readdirSync(mocksRoot).flatMap((name) => {
15039
- const mockDir = path16.join(mocksRoot, name);
15040
- const metaPath = path16.join(mockDir, "meta.json");
15041
- if (!fs12.existsSync(metaPath)) return [];
15042
- 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"));
15043
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 }];
15044
15422
  });
15045
15423
  }
15046
15424
  function readLighthouseReportMeta(reportDir, fallbackId) {
15047
- 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"));
15048
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") {
15049
15427
  throw captureError("artifact", "invalid_lighthouse_report_metadata", `Lighthouse report metadata at ${reportDir} is invalid.`);
15050
15428
  }
15051
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 };
15052
15430
  }
15053
15431
  function collectLighthouseReports(dir) {
15054
- const reportsRoot = path16.join(dir, "lighthouse", "reports");
15055
- if (!fs12.existsSync(reportsRoot)) return [];
15056
- 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));
15057
15435
  }
15058
15436
  function sessionMetaPath2(id, artifactRoot = CAPTURE_ROOT) {
15059
- return path16.join(sessionDir(id, artifactRoot), ".session.json");
15437
+ return path17.join(sessionDir(id, artifactRoot), ".session.json");
15060
15438
  }
15061
15439
  function tryReadSession(id, artifactRoot = CAPTURE_ROOT) {
15062
15440
  const metaPath = sessionMetaPath2(id, artifactRoot);
@@ -15178,7 +15556,7 @@ async function start(parsed) {
15178
15556
  let selectedBrowser = null;
15179
15557
  let endpointSelectionReason = null;
15180
15558
  try {
15181
- ensurePrivateDir(path16.join(dir, "shots"));
15559
+ ensurePrivateDir(path17.join(dir, "shots"));
15182
15560
  acquired.push({ label: "artifact tree", release: () => removeArtifactTree(dir) });
15183
15561
  harId = await startWorld.createHar(dir);
15184
15562
  const acquiredHarId = harId;
@@ -15318,8 +15696,8 @@ async function logTail(parsed) {
15318
15696
  process.exitCode = 1;
15319
15697
  return;
15320
15698
  }
15321
- const source = path16.resolve(sourcePath);
15322
- 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));
15323
15701
  const labelReason = rejectLogLabel(name);
15324
15702
  if (labelReason) {
15325
15703
  const labelHint = parsed.name === void 0 && labelReason === `exceeds ${MAX_LOG_LABEL_BYTES} bytes` ? " Pass `--name <short>` to choose a shorter destination label." : "";
@@ -15331,7 +15709,7 @@ async function logTail(parsed) {
15331
15709
  process.exitCode = 1;
15332
15710
  return;
15333
15711
  }
15334
- if (!fs12.existsSync(source)) {
15712
+ if (!fs13.existsSync(source)) {
15335
15713
  emitResult({
15336
15714
  tag: "error",
15337
15715
  attrs: { command: "session log", code: "log_file_not_found" },
@@ -15415,9 +15793,9 @@ async function logTail(parsed) {
15415
15793
  }
15416
15794
  }
15417
15795
  async function locateSessionHar(session) {
15418
- const stopped = fs12.existsSync(path16.join(session.dir, "bundle.json"));
15796
+ const stopped = fs13.existsSync(path17.join(session.dir, "bundle.json"));
15419
15797
  if (stopped) {
15420
- const harPath = path16.join(session.dir, "har.json");
15798
+ const harPath = path17.join(session.dir, "har.json");
15421
15799
  let raw;
15422
15800
  try {
15423
15801
  raw = readPrivateFile(harPath).toString("utf-8");
@@ -15661,7 +16039,7 @@ function exportHarBody(id, located, parsed) {
15661
16039
  return true;
15662
16040
  }
15663
16041
  try {
15664
- fs12.writeFileSync(parsed.out, bytes);
16042
+ fs13.writeFileSync(parsed.out, bytes);
15665
16043
  } catch (cause) {
15666
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 });
15667
16045
  process.exitCode = 1;
@@ -15825,7 +16203,7 @@ function bundleRows(manifest) {
15825
16203
  function emitStoppedFromManifest(id, sessionDir2, manifest, parsed) {
15826
16204
  emitResult({
15827
16205
  tag: "session-stopped",
15828
- attrs: { id, path: path16.join(sessionDir2, "bundle.json") },
16206
+ attrs: { id, path: path17.join(sessionDir2, "bundle.json") },
15829
16207
  summary: fact`Session ${id} stopped after ${manifest.duration}ms; bundle manifest written.`,
15830
16208
  sections: [lineList(bundleRows(manifest))],
15831
16209
  followUp: fact`capture session view ${id}`
@@ -15866,7 +16244,7 @@ async function stop(parsed) {
15866
16244
  let committed = false;
15867
16245
  try {
15868
16246
  session = readSession(id);
15869
- const bundlePath = path16.join(session.dir, "bundle.json");
16247
+ const bundlePath = path17.join(session.dir, "bundle.json");
15870
16248
  try {
15871
16249
  const manifest2 = readBundleManifest(bundlePath);
15872
16250
  if (session.harId) {
@@ -15895,19 +16273,19 @@ async function stop(parsed) {
15895
16273
  if (hostTeardown.status === "terminal") {
15896
16274
  throw new Error(`collector host teardown failed: ${hostTeardown.error}`);
15897
16275
  }
15898
- const shotsDir = path16.join(session.dir, "shots");
15899
- 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) })) : [];
15900
16278
  let har2 = null;
15901
16279
  if (session.harId) {
15902
16280
  const harData = await readHarRecording(session.harId);
15903
- const harPath = path16.join(session.dir, "har.json");
16281
+ const harPath = path17.join(session.dir, "har.json");
15904
16282
  writeJsonPrivate(harPath, harData);
15905
16283
  har2 = { id: session.harId, path: harPath, entryCount: harData.log.entries.length };
15906
16284
  }
15907
- const logsDir = path16.join(session.dir, "logs");
15908
- const logs = fs12.existsSync(logsDir) ? fs12.readdirSync(logsDir).filter((name) => name.endsWith(".log")).map((name) => {
15909
- const filePath = path16.join(logsDir, name);
15910
- 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");
15911
16289
  return { name, path: filePath, lines: content ? content.split("\n").filter(Boolean).length : 0 };
15912
16290
  }) : [];
15913
16291
  const snaps = collectSnaps(session.dir);
@@ -15916,7 +16294,7 @@ async function stop(parsed) {
15916
16294
  const reports = collectLighthouseReports(session.dir);
15917
16295
  const knownDirs = /* @__PURE__ */ new Set(["shots", "logs", "lighthouse", "measure", "motion", "network", ".har", ".stop.lock", ".operations.lock"]);
15918
16296
  const knownFiles = /* @__PURE__ */ new Set([".session.json", ".operations.json", "har.json", "bundle.json", "bridge.sock"]);
15919
- 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) }));
15920
16298
  const manifest = {
15921
16299
  id: session.sessionId,
15922
16300
  startedAt: session.startedAt ?? "",
@@ -15968,7 +16346,7 @@ function list(parsed) {
15968
16346
  return;
15969
16347
  }
15970
16348
  if (rejectSurplusPositionals(parsed, "list", 0, "capture session list")) return;
15971
- 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) => {
15972
16350
  try {
15973
16351
  return [tryReadSession(d, artifactRoot)];
15974
16352
  } catch {
@@ -15978,7 +16356,7 @@ function list(parsed) {
15978
16356
  const sessions = reads.flatMap((read) => {
15979
16357
  if (!read.ok) return [];
15980
16358
  const session = read.session;
15981
- const hasBundled = fs12.existsSync(path16.join(session.dir, "bundle.json"));
16359
+ const hasBundled = fs13.existsSync(path17.join(session.dir, "bundle.json"));
15982
16360
  return [{ id: session.sessionId, startedAt: session.startedAt ?? "", url: session.url ?? null, status: hasBundled ? "stopped" : "active" }];
15983
16361
  });
15984
16362
  const unreadable = reads.flatMap((read) => read.ok ? [] : [read]);
@@ -16056,7 +16434,7 @@ function view(parsed) {
16056
16434
  process.exitCode = 1;
16057
16435
  return;
16058
16436
  }
16059
- const bundlePath = path16.join(session.dir, "bundle.json");
16437
+ const bundlePath = path17.join(session.dir, "bundle.json");
16060
16438
  let manifest;
16061
16439
  try {
16062
16440
  manifest = readBundleManifest(bundlePath);
@@ -16139,11 +16517,11 @@ async function sessionMain(parsed, _args) {
16139
16517
  return;
16140
16518
  }
16141
16519
  }
16142
- 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;
16143
16521
  var init_commands = __esm({
16144
16522
  "src/session/commands.ts"() {
16145
- fs12 = __toESM(require("fs"));
16146
- path16 = __toESM(require("path"));
16523
+ fs13 = __toESM(require("fs"));
16524
+ path17 = __toESM(require("path"));
16147
16525
  init_har_manager();
16148
16526
  init_session_context();
16149
16527
  init_spawn();
@@ -16502,7 +16880,7 @@ async function startCollectors(parsed, client) {
16502
16880
  let harRecorder;
16503
16881
  if (parsed.har && !routed) {
16504
16882
  const harPath = harFilePath(parsed.har);
16505
- if (!fs13.existsSync(harPath)) {
16883
+ if (!fs14.existsSync(harPath)) {
16506
16884
  throw captureError(
16507
16885
  "precondition",
16508
16886
  "har_missing",
@@ -16584,10 +16962,10 @@ async function withPageAction(parsed, opts, fn) {
16584
16962
  client.close();
16585
16963
  }
16586
16964
  }
16587
- var fs13, import_node_perf_hooks2, seams;
16965
+ var fs14, import_node_perf_hooks2, seams;
16588
16966
  var init_connection = __esm({
16589
16967
  "src/cdp/connection.ts"() {
16590
- fs13 = __toESM(require("fs"));
16968
+ fs14 = __toESM(require("fs"));
16591
16969
  import_node_perf_hooks2 = require("node:perf_hooks");
16592
16970
  init_client();
16593
16971
  init_detect();
@@ -17075,6 +17453,7 @@ async function dispatchResolvedClick(client, resolved, opts) {
17075
17453
  }
17076
17454
  if (hitTest.backendNodeId !== backendNodeId2 || opts.includeHitTestReceiver) hitTestReceiverBackendNodeId = hitTest.backendNodeId;
17077
17455
  }
17456
+ await presentFrameBeforeInput(client);
17078
17457
  const pressParams = { type: "mousePressed", x: dispatchX, y: dispatchY, button: "left", clickCount: 1 };
17079
17458
  if (opts.mark !== void 0 && client.sendMarked) {
17080
17459
  await client.sendMarked("Input.dispatchMouseEvent", pressParams, opts.mark);
@@ -17091,6 +17470,12 @@ async function dispatchResolvedClick(client, resolved, opts) {
17091
17470
  ...hitTestReceiverBackendNodeId === void 0 ? {} : { hitTestReceiverBackendNodeId }
17092
17471
  };
17093
17472
  }
17473
+ async function presentFrameBeforeInput(client) {
17474
+ await client.send("Page.captureScreenshot", {
17475
+ format: "png",
17476
+ clip: { x: 0, y: 0, width: 1, height: 1, scale: 1 }
17477
+ });
17478
+ }
17094
17479
  async function typeText(client, text2) {
17095
17480
  await client.send("Input.insertText", { text: text2 });
17096
17481
  }
@@ -228530,22 +228915,22 @@ var init_exec_expression = __esm({
228530
228915
  // src/vault/paths.ts
228531
228916
  function vaultLibsDir() {
228532
228917
  const candidates = [
228533
- path17.resolve(__dirname, "..", "vault", "libs"),
228918
+ path18.resolve(__dirname, "..", "vault", "libs"),
228534
228919
  // built bin: bin/../vault/libs
228535
- path17.resolve(__dirname, "..", "..", "vault", "libs")
228920
+ path18.resolve(__dirname, "..", "..", "vault", "libs")
228536
228921
  // tsx dev: src/vault/../../vault/libs
228537
228922
  ];
228538
228923
  const hit = candidates.find(
228539
- (p) => fs14.existsSync(path17.join(p, "_runtime", "index.ts"))
228924
+ (p) => fs15.existsSync(path18.join(p, "_runtime", "index.ts"))
228540
228925
  );
228541
228926
  if (!hit) throw new Error(DEV_ONLY_MSG);
228542
228927
  return hit;
228543
228928
  }
228544
228929
  function vaultRepoRoot() {
228545
- return path17.dirname(vaultLibsDir());
228930
+ return path18.dirname(vaultLibsDir());
228546
228931
  }
228547
228932
  function listLibs() {
228548
- 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();
228549
228934
  }
228550
228935
  function unknownLibMsg(name) {
228551
228936
  return `Unknown lib "${name}". Available: ${listLibs().join(", ")}`;
@@ -228558,11 +228943,11 @@ async function loadEsbuild() {
228558
228943
  throw new Error(DEV_ONLY_MSG);
228559
228944
  });
228560
228945
  }
228561
- var fs14, path17, DEV_ONLY_MSG, VALID_LIB_NAME;
228946
+ var fs15, path18, DEV_ONLY_MSG, VALID_LIB_NAME;
228562
228947
  var init_paths = __esm({
228563
228948
  "src/vault/paths.ts"() {
228564
- fs14 = __toESM(require("fs"));
228565
- path17 = __toESM(require("path"));
228949
+ fs15 = __toESM(require("fs"));
228950
+ path18 = __toESM(require("path"));
228566
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.";
228567
228952
  VALID_LIB_NAME = /^[A-Za-z0-9_.-]+$/;
228568
228953
  }
@@ -228589,7 +228974,7 @@ function preflightLibs(imports) {
228589
228974
  let m;
228590
228975
  while ((m = LIB_SPECIFIER_RE.exec(imports)) !== null) {
228591
228976
  const name = m[2];
228592
- if (!isValidLibName(name) || !fs15.existsSync(path18.join(libs, name, "index.ts"))) {
228977
+ if (!isValidLibName(name) || !fs16.existsSync(path19.join(libs, name, "index.ts"))) {
228593
228978
  throw new Error(unknownLibMsg(name));
228594
228979
  }
228595
228980
  }
@@ -228605,7 +228990,7 @@ function buildEntry(code) {
228605
228990
  }
228606
228991
  function vaultResolver() {
228607
228992
  const libs = vaultLibsDir();
228608
- const runtime = path18.join(libs, "_runtime", "index.ts");
228993
+ const runtime = path19.join(libs, "_runtime", "index.ts");
228609
228994
  return {
228610
228995
  name: "vault",
228611
228996
  setup(build) {
@@ -228617,8 +229002,8 @@ function vaultResolver() {
228617
229002
  if (!isValidLibName(name)) {
228618
229003
  return { errors: [{ text: unknownLibMsg(name) }] };
228619
229004
  }
228620
- const entry = path18.join(libs, name, "index.ts");
228621
- 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) }] };
228622
229007
  });
228623
229008
  }
228624
229009
  };
@@ -228687,11 +229072,11 @@ async function bundleExec(code) {
228687
229072
  }
228688
229073
  return applyReturnInsert(result.outputFiles[0].text);
228689
229074
  }
228690
- var fs15, path18, SENTINEL, IMPORT_STMT_RE, LIB_SPECIFIER_RE;
229075
+ var fs16, path19, SENTINEL, IMPORT_STMT_RE, LIB_SPECIFIER_RE;
228691
229076
  var init_bundle = __esm({
228692
229077
  "src/vault/bundle.ts"() {
228693
- fs15 = __toESM(require("fs"));
228694
- path18 = __toESM(require("path"));
229078
+ fs16 = __toESM(require("fs"));
229079
+ path19 = __toESM(require("path"));
228695
229080
  init_paths();
228696
229081
  init_exec_expression();
228697
229082
  SENTINEL = "__CAPTURE_RESULT";
@@ -228711,10 +229096,23 @@ function emitExecError(parsed, code, summary, followUp) {
228711
229096
  function spillWholeResult(payload, artifactDir) {
228712
229097
  const name = `exec-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}.json`;
228713
229098
  const session = deps3.getActiveSession();
228714
- 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);
228715
229100
  writePrivateFile(filePath, payload);
228716
229101
  return filePath;
228717
229102
  }
229103
+ function describePageException(details) {
229104
+ const exception = details.exception;
229105
+ if (!exception) return details.text ?? "Unknown error";
229106
+ if (exception.subtype === "error" && exception.description) return exception.description;
229107
+ if (exception.type === "undefined") return "undefined";
229108
+ if ("value" in exception) return JSON.stringify(exception.value);
229109
+ const properties = exception.preview?.properties;
229110
+ if (exception.type === "object" && properties) {
229111
+ const fields = properties.map((p) => `${p.name}: ${p.value ?? "?"}`).join(", ");
229112
+ return `${exception.className ?? "Object"} {${fields}${exception.preview?.overflow ? ", \u2026" : ""}}`;
229113
+ }
229114
+ return exception.description ?? details.text ?? "Unknown error";
229115
+ }
228718
229116
  function isRequestTimeout(error4) {
228719
229117
  return error4 instanceof Error && /CDP request timeout \(\d+ms\): Runtime\.evaluate|collector-host CDP call "Runtime\.evaluate" failed: CDP request timeout/.test(error4.message);
228720
229118
  }
@@ -228792,7 +229190,7 @@ async function cmdPageExec(parsed, _args) {
228792
229190
  );
228793
229191
  }
228794
229192
  try {
228795
- code = fs16.readFileSync(parsed.file, "utf-8");
229193
+ code = fs17.readFileSync(parsed.file, "utf-8");
228796
229194
  } catch (err) {
228797
229195
  return emitExecError(
228798
229196
  parsed,
@@ -228860,7 +229258,7 @@ async function cmdPageExec(parsed, _args) {
228860
229258
  );
228861
229259
  }
228862
229260
  if (evalResult.exceptionDetails) {
228863
- const description = evalResult.exceptionDetails.exception?.description ?? "Unknown error";
229261
+ const description = describePageException(evalResult.exceptionDetails);
228864
229262
  return emitExecError(
228865
229263
  parsed,
228866
229264
  "exec_exception",
@@ -228890,11 +229288,11 @@ async function cmdPageExec(parsed, _args) {
228890
229288
  { json: parsed.json }
228891
229289
  );
228892
229290
  }
228893
- 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;
228894
229292
  var init_exec = __esm({
228895
229293
  "src/cdp/commands/page/exec.ts"() {
228896
- fs16 = __toESM(require("fs"));
228897
- path19 = __toESM(require("path"));
229294
+ fs17 = __toESM(require("fs"));
229295
+ path20 = __toESM(require("path"));
228898
229296
  init_connection();
228899
229297
  init_errors();
228900
229298
  init_exec_expression();
@@ -229006,7 +229404,7 @@ async function resolveOutPath(parsed) {
229006
229404
  if (parsed.out) return parsed.out;
229007
229405
  const sessionPath = await deps4.nextStepPath("shot", "manual");
229008
229406
  if (sessionPath) return sessionPath;
229009
- return path20.join(deps4.createOneshotSession("page", parsed.artifactDir).artifactsDir, "shot.png");
229407
+ return path21.join(deps4.createOneshotSession("page", parsed.artifactDir).artifactsDir, "shot.png");
229010
229408
  }
229011
229409
  function round6(value) {
229012
229410
  return Math.round(value * 1e6) / 1e6;
@@ -229244,10 +229642,10 @@ async function cmdPageShot(parsed, _args) {
229244
229642
  }
229245
229643
  emitResult(buildScreenshotResult({ ...result, emulation: parsed.fullPage ? "full-page" : viewport2 ? "viewport" : "none", viewport: viewport2, colorScheme }), { json: parsed.json });
229246
229644
  }
229247
- var path20, USAGE6, deps4, PNG_SIGNATURE, LEGIBILITY_SCALE_THRESHOLD;
229645
+ var path21, USAGE6, deps4, PNG_SIGNATURE, LEGIBILITY_SCALE_THRESHOLD;
229248
229646
  var init_shot = __esm({
229249
229647
  "src/cdp/commands/page/shot.ts"() {
229250
- path20 = __toESM(require("path"));
229648
+ path21 = __toESM(require("path"));
229251
229649
  init_connection();
229252
229650
  init_screenshot();
229253
229651
  init_viewport();
@@ -230199,9 +230597,9 @@ async function cmdTabClose(parsed, _args) {
230199
230597
  console.log(USAGE13);
230200
230598
  return;
230201
230599
  }
230202
- const target = parsed.positional[0];
230600
+ const target = parsed.positional[0] ?? (parsed.targetSource === "flag" ? parsed.target : void 0);
230203
230601
  if (!target) {
230204
- throw invalidInput("received: no target; expected: capture tab close <target> [--port <port>].", "missing_argument");
230602
+ throw invalidInput("received: no target; expected: capture tab close <target> (or --target <id>) [--port <port>].", "missing_argument");
230205
230603
  }
230206
230604
  if (target.length < 8) {
230207
230605
  throw invalidInput(`received: target ${target}; expected: a full target id or an unambiguous prefix of at least 8 characters.`, "target_prefix_too_short");
@@ -230267,6 +230665,7 @@ var init_close = __esm({
230267
230665
 
230268
230666
  input:
230269
230667
  <target> required \u2014 full tab target id or an unambiguous 8-character prefix
230668
+ --target <id> same as <target>; only an explicit flag counts, never the session's tab or CDP_TARGET
230270
230669
  --port <port> CDP endpoint (default: the auto-discovered preferred endpoint)
230271
230670
 
230272
230671
  output: <tab-closed port=\u2026 target=\u2026> \u2014 the closed tab's full target id and last observed URL.
@@ -230471,7 +230870,7 @@ async function cmdTabNetwork(parsed, _args) {
230471
230870
  `session ${session.sessionId} has no target to emulate on; open one with \`capture session start --hold --url <url>\` or \`capture tab reset <url>\`.`
230472
230871
  );
230473
230872
  }
230474
- if (!session.bridgeSocket || !fs17.existsSync(session.bridgeSocket)) {
230873
+ if (!session.bridgeSocket || !fs18.existsSync(session.bridgeSocket)) {
230475
230874
  throw captureError(
230476
230875
  "precondition",
230477
230876
  "no_held_bridge",
@@ -230535,10 +230934,10 @@ async function cmdTabNetwork(parsed, _args) {
230535
230934
  }
230536
230935
  emitResult(buildNetworkResult(mode, targetId), { json: parsed.json });
230537
230936
  }
230538
- var fs17, USAGE15;
230937
+ var fs18, USAGE15;
230539
230938
  var init_network = __esm({
230540
230939
  "src/cdp/commands/tab/network.ts"() {
230541
- fs17 = __toESM(require("node:fs"));
230940
+ fs18 = __toESM(require("node:fs"));
230542
230941
  init_errors();
230543
230942
  init_session_context();
230544
230943
  init_coordinator();
@@ -230649,12 +231048,12 @@ var init_lifecycle2 = __esm({
230649
231048
 
230650
231049
  // src/cdp/commands/tab/mock/start.ts
230651
231050
  function stagedRulesPath() {
230652
- 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`);
230653
231052
  }
230654
231053
  function sourceFor(pathname) {
230655
231054
  let source;
230656
231055
  try {
230657
- source = fs18.readFileSync(pathname);
231056
+ source = fs19.readFileSync(pathname);
230658
231057
  } catch (error4) {
230659
231058
  throw captureError("artifact", "mock_rules_unreadable", `could not read mock rules at ${pathname}: ${error4 instanceof Error ? error4.message : String(error4)}`);
230660
231059
  }
@@ -230698,12 +231097,12 @@ async function cmdTabMockStart(parsed) {
230698
231097
  }
230699
231098
  }
230700
231099
  }
230701
- var crypto8, fs18, path21, HELP;
231100
+ var crypto8, fs19, path22, HELP;
230702
231101
  var init_start = __esm({
230703
231102
  "src/cdp/commands/tab/mock/start.ts"() {
230704
231103
  crypto8 = __toESM(require("node:crypto"));
230705
- fs18 = __toESM(require("node:fs"));
230706
- path21 = __toESM(require("node:path"));
231104
+ fs19 = __toESM(require("node:fs"));
231105
+ path22 = __toESM(require("node:path"));
230707
231106
  init_errors();
230708
231107
  init_artifacts();
230709
231108
  init_render();
@@ -230864,13 +231263,13 @@ function isAgentVisible(doc) {
230864
231263
  return AGENT_VISIBLE_VISIBILITIES.has(doc.visibility);
230865
231264
  }
230866
231265
  function cacheDir() {
230867
- return path22.join(vaultRepoRoot(), ".cache", "docs");
231266
+ return path23.join(vaultRepoRoot(), ".cache", "docs");
230868
231267
  }
230869
231268
  function schemasPathFor(svc) {
230870
- return path22.join(vaultLibsDir(), svc, "schemas.ts");
231269
+ return path23.join(vaultLibsDir(), svc, "schemas.ts");
230871
231270
  }
230872
231271
  function srcSha(svc) {
230873
- 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");
230874
231273
  }
230875
231274
  function toJsonSchema(zodSchema) {
230876
231275
  const s = zodSchema;
@@ -230923,14 +231322,14 @@ async function buildDoc(svc) {
230923
231322
  return doc;
230924
231323
  }
230925
231324
  async function getDoc(name) {
230926
- if (!isValidLibName(name) || !fs19.existsSync(schemasPathFor(name))) {
231325
+ if (!isValidLibName(name) || !fs20.existsSync(schemasPathFor(name))) {
230927
231326
  throw new Error(unknownLibMsg(name));
230928
231327
  }
230929
231328
  const sha = srcSha(name);
230930
- const cacheFile = path22.join(cacheDir(), `${name}.json`);
230931
- if (fs19.existsSync(cacheFile)) {
231329
+ const cacheFile = path23.join(cacheDir(), `${name}.json`);
231330
+ if (fs20.existsSync(cacheFile)) {
230932
231331
  try {
230933
- const cached = JSON.parse(fs19.readFileSync(cacheFile, "utf8"));
231332
+ const cached = JSON.parse(fs20.readFileSync(cacheFile, "utf8"));
230934
231333
  if (cached._srcSha === sha) {
230935
231334
  delete cached._srcSha;
230936
231335
  return cached;
@@ -230939,8 +231338,8 @@ async function getDoc(name) {
230939
231338
  }
230940
231339
  }
230941
231340
  const doc = await buildDoc(name);
230942
- fs19.mkdirSync(cacheDir(), { recursive: true });
230943
- fs19.writeFileSync(
231341
+ fs20.mkdirSync(cacheDir(), { recursive: true });
231342
+ fs20.writeFileSync(
230944
231343
  cacheFile,
230945
231344
  JSON.stringify({ ...doc, _srcSha: sha }, null, 2)
230946
231345
  );
@@ -230962,11 +231361,11 @@ async function listDocs() {
230962
231361
  }
230963
231362
  return out;
230964
231363
  }
230965
- var fs19, path22, import_crypto, AGENT_VISIBLE_VISIBILITIES, cjsRequire;
231364
+ var fs20, path23, import_crypto, AGENT_VISIBLE_VISIBILITIES, cjsRequire;
230966
231365
  var init_docs = __esm({
230967
231366
  "src/vault/docs.ts"() {
230968
- fs19 = __toESM(require("fs"));
230969
- path22 = __toESM(require("path"));
231367
+ fs20 = __toESM(require("fs"));
231368
+ path23 = __toESM(require("path"));
230970
231369
  import_crypto = require("crypto");
230971
231370
  init_paths();
230972
231371
  AGENT_VISIBLE_VISIBILITIES = /* @__PURE__ */ new Set(["public", "chat"]);
@@ -231274,10 +231673,10 @@ async function cmdLib(parsed, _args) {
231274
231673
  throw e;
231275
231674
  }
231276
231675
  }
231277
- 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;
231278
231677
  var init_lib = __esm({
231279
231678
  "src/cdp/commands/lib.ts"() {
231280
- path23 = __toESM(require("path"));
231679
+ path24 = __toESM(require("path"));
231281
231680
  init_errors();
231282
231681
  init_render();
231283
231682
  init_paths();
@@ -231323,7 +231722,7 @@ Effects: local reads only; no CDP connection. Dev checkout required.`
231323
231722
  SERVICE_DESC_SUBSTRING = 10;
231324
231723
  svcName = (doc) => doc.library.replace(/^@vallum\//, "");
231325
231724
  cap = (value, maxLength) => value.length > maxLength ? `${value.slice(0, maxLength - 1)}\u2026` : value;
231326
- srcPathFor = (name) => path23.join(vaultLibsDir(), name, "index.ts");
231725
+ srcPathFor = (name) => path24.join(vaultLibsDir(), name, "index.ts");
231327
231726
  }
231328
231727
  });
231329
231728
 
@@ -231407,7 +231806,7 @@ async function runBrowserScope(method, params, parsed, timeoutMs, deps23 = DEFAU
231407
231806
  const flagTarget = parsed.targetSource === "flag" ? parsed.target : void 0;
231408
231807
  const session = getActiveSession();
231409
231808
  const bridgeSocket = session?.bridgeSocket;
231410
- const heldBridgeActive = Boolean(bridgeSocket && fs20.existsSync(bridgeSocket));
231809
+ const heldBridgeActive = Boolean(bridgeSocket && fs21.existsSync(bridgeSocket));
231411
231810
  if (parsed.portSource !== "flag" && bridgeSocket && heldBridgeActive) {
231412
231811
  let resp;
231413
231812
  try {
@@ -231535,10 +231934,10 @@ function waitForEventOnce(client, eventName, timeoutMs) {
231535
231934
  });
231536
231935
  });
231537
231936
  }
231538
- 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;
231539
231938
  var init_cdp = __esm({
231540
231939
  "src/cdp/commands/cdp.ts"() {
231541
- fs20 = __toESM(require("fs"));
231940
+ fs21 = __toESM(require("fs"));
231542
231941
  init_errors();
231543
231942
  init_session_context();
231544
231943
  init_connection();
@@ -231578,12 +231977,12 @@ Effects:
231578
231977
  });
231579
231978
 
231580
231979
  // src/cdp/host/core.ts
231581
- var crypto9, fs21, path24, import_node_perf_hooks3, CollectorHost;
231980
+ var crypto9, fs22, path25, import_node_perf_hooks3, CollectorHost;
231582
231981
  var init_core = __esm({
231583
231982
  "src/cdp/host/core.ts"() {
231584
231983
  crypto9 = __toESM(require("node:crypto"));
231585
- fs21 = __toESM(require("node:fs"));
231586
- path24 = __toESM(require("node:path"));
231984
+ fs22 = __toESM(require("node:fs"));
231985
+ path25 = __toESM(require("node:path"));
231587
231986
  import_node_perf_hooks3 = require("node:perf_hooks");
231588
231987
  init_artifacts();
231589
231988
  init_artifacts();
@@ -231657,7 +232056,7 @@ var init_core = __esm({
231657
232056
  this.refuseClaims(collector.claims);
231658
232057
  const id = `${entry.idPrefix}-${crypto9.randomBytes(5).toString("hex")}`;
231659
232058
  this.starting.set(id, collector.claims);
231660
- const dir = path24.join(this.sessionDir, ...entry.idSegments, id);
232059
+ const dir = path25.join(this.sessionDir, ...entry.idSegments, id);
231661
232060
  try {
231662
232061
  ensurePrivateDir(dir);
231663
232062
  const startedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -231669,13 +232068,13 @@ var init_core = __esm({
231669
232068
  id,
231670
232069
  targetId: this.identity.targetId,
231671
232070
  config: config3,
231672
- appendRecord: (file, record5) => appendNdjsonPrivate(path24.join(dir, file), record5),
231673
- 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)),
231674
232073
  noteLoss: (reason, detail) => {
231675
232074
  live.losses.push({ reason, detail });
231676
232075
  }
231677
232076
  };
231678
- 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 });
231679
232078
  await collector.start(context);
231680
232079
  this.starting.delete(id);
231681
232080
  this.live.set(id, live);
@@ -231697,10 +232096,10 @@ var init_core = __esm({
231697
232096
  finalizedFiles(dir) {
231698
232097
  const files = [];
231699
232098
  const walk2 = (relative2) => {
231700
- for (const entry of fs21.readdirSync(path24.join(dir, relative2), { withFileTypes: true })) {
231701
- 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);
231702
232101
  if (entry.isDirectory()) walk2(name);
231703
- 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 });
231704
232103
  }
231705
232104
  };
231706
232105
  walk2("");
@@ -231712,12 +232111,12 @@ var init_core = __esm({
231712
232111
  }
231713
232112
  async drain(live, cause) {
231714
232113
  const outcome = await live.collector.drain(cause);
231715
- unlinkPrivateFile(path24.join(live.row.dir, "collecting.json"));
232114
+ unlinkPrivateFile(path25.join(live.row.dir, "collecting.json"));
231716
232115
  const files = this.finalizedFiles(live.row.dir);
231717
232116
  const loss = live.losses[0];
231718
232117
  const completion = loss || !cause.clientUsable ? "partial" : "complete";
231719
232118
  const summary = outcome.summary && typeof outcome.summary === "object" && !Array.isArray(outcome.summary) ? outcome.summary : { value: outcome.summary };
231720
- writeJsonPrivate(path24.join(live.row.dir, "meta.json"), {
232119
+ writeJsonPrivate(path25.join(live.row.dir, "meta.json"), {
231721
232120
  id: live.row.id,
231722
232121
  kind: live.row.kind,
231723
232122
  completion,
@@ -231818,7 +232217,7 @@ async function runCollectorHost(options) {
231818
232217
  if (session.targetId !== options.targetId) throw new Error("collector host target does not match session state");
231819
232218
  await applyActiveSessionNetworkConditions(client, session, options.targetId);
231820
232219
  let server2;
231821
- const handlePath = path25.join(options.sessionDir, ".collector-host.json");
232220
+ const handlePath = path26.join(options.sessionDir, ".collector-host.json");
231822
232221
  const publish = (snapshot) => writeJsonPrivate(handlePath, {
231823
232222
  pid: process.pid,
231824
232223
  birth: birth.identity,
@@ -231915,11 +232314,11 @@ async function runCollectorHost(options) {
231915
232314
  cleanup();
231916
232315
  }, client);
231917
232316
  }
231918
- var crypto10, path25;
232317
+ var crypto10, path26;
231919
232318
  var init_server2 = __esm({
231920
232319
  "src/cdp/host/server.ts"() {
231921
232320
  crypto10 = __toESM(require("node:crypto"));
231922
- path25 = __toESM(require("node:path"));
232321
+ path26 = __toESM(require("node:path"));
231923
232322
  init_client();
231924
232323
  init_targets();
231925
232324
  init_server();
@@ -238167,8 +238566,8 @@ function round32(value) {
238167
238566
  return Math.round(value * 1e3) / 1e3;
238168
238567
  }
238169
238568
  async function captureStateRaster(ctx, client, state, id, borderRect2) {
238170
- const baselinePath = path26.join(ctx.dir, "screenshot.png");
238171
- if (!fs22.existsSync(baselinePath)) return void 0;
238569
+ const baselinePath = path27.join(ctx.dir, "screenshot.png");
238570
+ if (!fs23.existsSync(baselinePath)) return void 0;
238172
238571
  const response = await client.send("Page.captureScreenshot", { format: "png" });
238173
238572
  const statePng = Buffer.from(response.data, "base64");
238174
238573
  const imageName = `states/${sanitizeToken(state) || "state"}-${id}.png`;
@@ -238182,7 +238581,7 @@ async function captureStateRaster(ctx, client, state, id, borderRect2) {
238182
238581
  const scaleY = available ? stateImage.height / h : 1;
238183
238582
  const paddingCssPx = ctx.stateDiffPaddingCssPx ?? DEFAULT_STATE_DIFF_PADDING_CSS_PX;
238184
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 } };
238185
- const baseline = import_pngjs3.PNG.sync.read(fs22.readFileSync(baselinePath));
238584
+ const baseline = import_pngjs3.PNG.sync.read(fs23.readFileSync(baselinePath));
238186
238585
  if (baseline.width !== stateImage.width || baseline.height !== stateImage.height) return { ...raster, diffUnavailableReason: "dimension-mismatch" };
238187
238586
  const x0 = Math.max(0, Math.floor((borderRect2.x - paddingCssPx) * scaleX));
238188
238587
  const y0 = Math.max(0, Math.floor((borderRect2.y - paddingCssPx) * scaleY));
@@ -238405,11 +238804,11 @@ async function captureOneElement(ctx, client, selector, index, nodeId, identity3
238405
238804
  ...raster ? { raster } : {}
238406
238805
  };
238407
238806
  }
238408
- 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;
238409
238808
  var init_states = __esm({
238410
238809
  "src/cdp/measure/collectors/states.ts"() {
238411
- fs22 = __toESM(require("node:fs"));
238412
- path26 = __toESM(require("node:path"));
238810
+ fs23 = __toESM(require("node:fs"));
238811
+ path27 = __toESM(require("node:path"));
238413
238812
  import_pngjs3 = __toESM(require_png());
238414
238813
  init_pixelmatch();
238415
238814
  init_domains();
@@ -238835,9 +239234,9 @@ document.querySelectorAll('img, video, canvas, svg, iframe').length;`;
238835
239234
 
238836
239235
  // src/cdp/measure/snapshot.ts
238837
239236
  function resolveScopedArtifactPath(dir, filename) {
238838
- const target = path27.resolve(dir, filename);
238839
- const rel = path27.relative(dir, target);
238840
- 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);
238841
239240
  if (escapesDir) {
238842
239241
  throw new Error(`Refusing to write snapshot artifact "${filename}" outside its snap dir ${dir}`);
238843
239242
  }
@@ -238934,7 +239333,7 @@ function boundedClient(client, phase, partialPath, onTimeout, requestTimeoutCapM
238934
239333
  async function captureSnapshotSubstrate(options) {
238935
239334
  const client = options.target.client;
238936
239335
  const dir = ensurePrivateDir(options.path);
238937
- const snapId = options.snapId ?? path27.basename(dir);
239336
+ const snapId = options.snapId ?? path28.basename(dir);
238938
239337
  const settleTimeoutMs = options.settleTimeout ?? DEFAULT_SETTLE_TIMEOUT_MS;
238939
239338
  const quietThresholdMs = options.quietThresholdMs ?? DEFAULT_QUIET_THRESHOLD_MS;
238940
239339
  const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
@@ -238996,10 +239395,10 @@ async function captureSnapshotSubstrate(options) {
238996
239395
  const animationEvidence = await collectAnimationEvidence(phaseClient("settle-evidence"));
238997
239396
  const grouped = groupChurnEvidence(churnEvidenceRaw, animationEvidence, settleResult.elapsedMs, settleTimeoutMs);
238998
239397
  unstableRegions = grouped.unstableRegions;
238999
- writeJsonPrivate(path27.join(dir, "churn.json"), grouped.report);
239398
+ writeJsonPrivate(path28.join(dir, "churn.json"), grouped.report);
239000
239399
  artifacts.push("churn.json");
239001
239400
  if (!captured) {
239002
- writeJsonPrivate(path27.join(dir, "animation.json"), animationEvidence);
239401
+ writeJsonPrivate(path28.join(dir, "animation.json"), animationEvidence);
239003
239402
  artifacts.push("animation.json");
239004
239403
  }
239005
239404
  }
@@ -239051,7 +239450,7 @@ async function captureSnapshotSubstrate(options) {
239051
239450
  };
239052
239451
  for (const collector of baseline) collectorOutcomes.push(await runCollector(collector));
239053
239452
  const screenshotResponse = await phaseClient("baseline-screenshot").send("Page.captureScreenshot", { format: "png" });
239054
- writeBinaryPrivate(path27.join(dir, "screenshot.png"), Buffer.from(screenshotResponse.data, "base64"));
239453
+ writeBinaryPrivate(path28.join(dir, "screenshot.png"), Buffer.from(screenshotResponse.data, "base64"));
239055
239454
  artifacts.push("screenshot.png");
239056
239455
  let domHtmlValue;
239057
239456
  let domHtmlUnavailableReason;
@@ -239071,7 +239470,7 @@ async function captureSnapshotSubstrate(options) {
239071
239470
  if (domHtmlValue === void 0) {
239072
239471
  domHtmlFact = { available: false, unavailableReason: domHtmlUnavailableReason };
239073
239472
  } else {
239074
- writePrivateFile(path27.join(dir, "dom.html"), domHtmlValue);
239473
+ writePrivateFile(path28.join(dir, "dom.html"), domHtmlValue);
239075
239474
  artifacts.push("dom.html");
239076
239475
  domHtmlFact = { available: true };
239077
239476
  }
@@ -239118,7 +239517,7 @@ async function captureSnapshotSubstrate(options) {
239118
239517
  ...domHtmlFact ? { domHtml: domHtmlFact } : {},
239119
239518
  ...captured ? { collectors: collectorOutcomes } : {}
239120
239519
  };
239121
- writeJsonPrivate(path27.join(dir, "meta.json"), meta);
239520
+ writeJsonPrivate(path28.join(dir, "meta.json"), meta);
239122
239521
  artifacts.push("meta.json");
239123
239522
  return {
239124
239523
  id: snapId,
@@ -239137,7 +239536,7 @@ async function captureSnapshotSubstrate(options) {
239137
239536
  } catch (error4) {
239138
239537
  if (error4 instanceof SnapshotCaptureTimeout) {
239139
239538
  try {
239140
- writeJsonPrivate(path27.join(dir, "capture-timeout.json"), {
239539
+ writeJsonPrivate(path28.join(dir, "capture-timeout.json"), {
239141
239540
  schemaVersion: 1,
239142
239541
  id: snapId,
239143
239542
  status: "timeout",
@@ -239157,10 +239556,10 @@ async function captureSnapshotSubstrate(options) {
239157
239556
  await restoreAnimationsOnce();
239158
239557
  }
239159
239558
  }
239160
- 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;
239161
239560
  var init_snapshot = __esm({
239162
239561
  "src/cdp/measure/snapshot.ts"() {
239163
- path27 = __toESM(require("path"));
239562
+ path28 = __toESM(require("path"));
239164
239563
  init_artifacts();
239165
239564
  init_domains();
239166
239565
  init_settle();
@@ -239212,347 +239611,6 @@ var init_snapshot = __esm({
239212
239611
  }
239213
239612
  });
239214
239613
 
239215
- // src/output/artifact.ts
239216
- function isUrlRef(ref) {
239217
- return /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(ref);
239218
- }
239219
- function stripTrailingSep(p) {
239220
- return p.length > 1 && (p.endsWith("/") || p.endsWith(path28.sep)) ? p.slice(0, -1) : p;
239221
- }
239222
- function resolveRefFromPath(ref, config3) {
239223
- const resolved = assertUnderCaptureRoot(stripTrailingSep(ref));
239224
- const metaPath = path28.join(resolved, "meta.json");
239225
- if (!fs23.existsSync(metaPath)) {
239226
- throw new ArtifactResolutionError(
239227
- ref,
239228
- [resolved],
239229
- `no ${config3.label} found at this path (missing meta.json)`,
239230
- config3.creatingCommand
239231
- );
239232
- }
239233
- return { kind: config3.kind, id: path28.basename(resolved), dir: resolved };
239234
- }
239235
- function resolveRefFromId(ref, config3) {
239236
- const active = getActiveSession();
239237
- if (!active) {
239238
- throw new ArtifactResolutionError(
239239
- ref,
239240
- [],
239241
- `no active capture session \u2014 a bare id resolves against the active session's ${config3.idSegments.join("/")} directory`,
239242
- `capture session start (then re-run), or pass the absolute ${config3.label} path printed by ${config3.creatingCommand}`
239243
- );
239244
- }
239245
- const dir = path28.join(active.dir, ...config3.idSegments, ref);
239246
- const resolved = assertUnderCaptureRoot(dir);
239247
- const metaPath = path28.join(resolved, "meta.json");
239248
- if (!fs23.existsSync(metaPath)) {
239249
- throw new ArtifactResolutionError(
239250
- ref,
239251
- [resolved],
239252
- `no ${config3.label} named ${JSON.stringify(ref)} found in the active session (${active.sessionId})`,
239253
- config3.creatingCommand
239254
- );
239255
- }
239256
- return { kind: config3.kind, id: ref, dir: resolved };
239257
- }
239258
- function rejectRelativePath(ref) {
239259
- if (!path28.isAbsolute(ref) && (ref.includes("/") || ref.includes(path28.sep))) {
239260
- throw new ArtifactResolutionError(
239261
- ref,
239262
- [],
239263
- "relative paths are not accepted \u2014 pass an absolute artifact path (as printed by the capturing command) or a bare id"
239264
- );
239265
- }
239266
- }
239267
- async function resolveSnapRef(ref, options = {}) {
239268
- if (isUrlRef(ref)) {
239269
- if (!options.onUrl) {
239270
- throw new ArtifactResolutionError(ref, [], "this command does not accept a URL target \u2014 pass a snapshot id or path instead");
239271
- }
239272
- const result = await options.onUrl(ref);
239273
- return { kind: "snap", id: result.id, dir: assertUnderCaptureRoot(result.dir) };
239274
- }
239275
- if (path28.isAbsolute(ref)) return resolveRefFromPath(ref, SNAP_CONFIG);
239276
- rejectRelativePath(ref);
239277
- return resolveRefFromId(ref, SNAP_CONFIG);
239278
- }
239279
- function resolveRecRef(ref) {
239280
- if (isUrlRef(ref)) {
239281
- throw new ArtifactResolutionError(
239282
- ref,
239283
- [],
239284
- "a recording ref cannot be a URL \u2014 record one first with `capture motion rec <url> --do <action>` or `--start`/`--stop`"
239285
- );
239286
- }
239287
- if (path28.isAbsolute(ref)) return resolveRefFromPath(ref, REC_CONFIG);
239288
- rejectRelativePath(ref);
239289
- return resolveRefFromId(ref, REC_CONFIG);
239290
- }
239291
- function resolveTraceRef(ref) {
239292
- if (isUrlRef(ref)) {
239293
- throw new ArtifactResolutionError(ref, [], "a trace ref cannot be a URL \u2014 record one first with `capture perf trace <url>` or `--start`/`--stop`");
239294
- }
239295
- const resolved = path28.isAbsolute(ref) ? resolveRefFromPath(ref, TRACE_CONFIG) : (rejectRelativePath(ref), resolveRefFromId(ref, TRACE_CONFIG));
239296
- let meta;
239297
- try {
239298
- meta = JSON.parse(readPrivateFile(path28.join(resolved.dir, "meta.json")).toString("utf8"));
239299
- } catch (error4) {
239300
- 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);
239301
- }
239302
- const completion = meta.completion;
239303
- if (completion !== "complete" && completion !== "partial" && completion !== "orphaned") {
239304
- throw new ArtifactResolutionError(ref, [path28.join(resolved.dir, "meta.json")], "trace metadata has no valid completion state", TRACE_CONFIG.creatingCommand);
239305
- }
239306
- const inventory = meta.files;
239307
- const inventoryMalformed = !Array.isArray(inventory);
239308
- const inventoryMismatch = !inventoryMalformed && inventory.some((entry) => {
239309
- if (!entry || typeof entry !== "object" || Array.isArray(entry)) return true;
239310
- const item = entry;
239311
- const name = item.name;
239312
- const bytes = item.bytes;
239313
- if (typeof name !== "string" || typeof bytes !== "number" || !Number.isSafeInteger(bytes) || bytes < 0 || name.includes(path28.sep)) return true;
239314
- try {
239315
- return fs23.statSync(path28.join(resolved.dir, name)).size !== bytes;
239316
- } catch {
239317
- return true;
239318
- }
239319
- });
239320
- const truncatedReason = inventoryMalformed ? "artifact_inventory_missing_or_malformed" : inventoryMismatch ? "artifact_inventory_mismatch" : void 0;
239321
- return { ...resolved, completion: truncatedReason ? "truncated" : completion, ...truncatedReason ? { reason: truncatedReason } : typeof meta.reason === "string" ? { reason: meta.reason } : {} };
239322
- }
239323
- function creatingCommandFor(filename) {
239324
- return CREATING_COMMAND_BY_FILE[filename];
239325
- }
239326
- function artifactPath(ref, filename, opts = {}) {
239327
- const filePath = assertUnderCaptureRoot(path28.join(ref.dir, filename));
239328
- const mustExist = opts.mustExist ?? true;
239329
- if (mustExist && !fs23.existsSync(filePath)) {
239330
- throw new ArtifactResolutionError(
239331
- ref.id,
239332
- [filePath],
239333
- `${filename} is not present in ${ref.kind === "snap" ? "snapshot" : ref.kind === "rec" ? "recording" : "trace"} ${ref.id}`,
239334
- creatingCommandFor(filename)
239335
- );
239336
- }
239337
- return filePath;
239338
- }
239339
- function artifactExists(ref, filename) {
239340
- return fs23.existsSync(assertUnderCaptureRoot(path28.join(ref.dir, filename)));
239341
- }
239342
- function readJsonArtifactFile(ref, filename) {
239343
- const filePath = artifactPath(ref, filename);
239344
- let raw;
239345
- try {
239346
- raw = fs23.readFileSync(filePath, "utf-8");
239347
- } catch (err) {
239348
- throw new ArtifactResolutionError(
239349
- ref.id,
239350
- [filePath],
239351
- `could not read ${filename}: ${err instanceof Error ? err.message : String(err)}`,
239352
- creatingCommandFor(filename)
239353
- );
239354
- }
239355
- try {
239356
- return JSON.parse(raw);
239357
- } catch (err) {
239358
- throw new ArtifactResolutionError(
239359
- ref.id,
239360
- [filePath],
239361
- `${filename} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
239362
- creatingCommandFor(filename)
239363
- );
239364
- }
239365
- }
239366
- function readNdjsonArtifactFile(ref, filename) {
239367
- const filePath = artifactPath(ref, filename);
239368
- let raw;
239369
- try {
239370
- raw = fs23.readFileSync(filePath, "utf-8");
239371
- } catch (err) {
239372
- throw new ArtifactResolutionError(
239373
- ref.id,
239374
- [filePath],
239375
- `could not read ${filename}: ${err instanceof Error ? err.message : String(err)}`,
239376
- creatingCommandFor(filename)
239377
- );
239378
- }
239379
- const lines = raw.split("\n").filter((l) => l.trim().length > 0);
239380
- return lines.map((line4, i) => {
239381
- try {
239382
- return JSON.parse(line4);
239383
- } catch (err) {
239384
- throw new ArtifactResolutionError(
239385
- ref.id,
239386
- [filePath],
239387
- `${filename} line ${i + 1} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
239388
- creatingCommandFor(filename)
239389
- );
239390
- }
239391
- });
239392
- }
239393
- function assertKind(ref, kind, fnName) {
239394
- if (ref.kind !== kind) {
239395
- throw new TypeError(`${fnName} expects a ${kind} ref, got a ${ref.kind} ref (${ref.id})`);
239396
- }
239397
- }
239398
- function readMeta(ref) {
239399
- return readJsonArtifactFile(ref, "meta.json");
239400
- }
239401
- function readGeometry(ref) {
239402
- assertKind(ref, "snap", "readGeometry");
239403
- return readJsonArtifactFile(ref, "geometry.json");
239404
- }
239405
- function readStyles(ref) {
239406
- assertKind(ref, "snap", "readStyles");
239407
- return readJsonArtifactFile(ref, "styles.json");
239408
- }
239409
- function readHittest(ref) {
239410
- assertKind(ref, "snap", "readHittest");
239411
- return readJsonArtifactFile(ref, "hittest.json");
239412
- }
239413
- function readText(ref) {
239414
- assertKind(ref, "snap", "readText");
239415
- return readJsonArtifactFile(ref, "text.json");
239416
- }
239417
- function readForms(ref) {
239418
- assertKind(ref, "snap", "readForms");
239419
- return readJsonArtifactFile(ref, "forms.json");
239420
- }
239421
- function readAnimation(ref) {
239422
- assertKind(ref, "snap", "readAnimation");
239423
- return readJsonArtifactFile(ref, "animation.json");
239424
- }
239425
- function readAx(ref) {
239426
- assertKind(ref, "snap", "readAx");
239427
- return readJsonArtifactFile(ref, "ax.json");
239428
- }
239429
- function readQueries(ref) {
239430
- assertKind(ref, "snap", "readQueries");
239431
- return readJsonArtifactFile(ref, "queries.json");
239432
- }
239433
- function readFocus(ref) {
239434
- assertKind(ref, "snap", "readFocus");
239435
- return readJsonArtifactFile(ref, "focus.json");
239436
- }
239437
- function readScroll(ref) {
239438
- assertKind(ref, "snap", "readScroll");
239439
- return readJsonArtifactFile(ref, "scroll.json");
239440
- }
239441
- function readLayers(ref) {
239442
- assertKind(ref, "snap", "readLayers");
239443
- return readJsonArtifactFile(ref, "layers.json");
239444
- }
239445
- function readStates(ref) {
239446
- assertKind(ref, "snap", "readStates");
239447
- return readJsonArtifactFile(ref, "states.json");
239448
- }
239449
- function readPixels(ref) {
239450
- assertKind(ref, "snap", "readPixels");
239451
- return readJsonArtifactFile(ref, "pixels.json");
239452
- }
239453
- function readMedia(ref) {
239454
- assertKind(ref, "snap", "readMedia");
239455
- return readJsonArtifactFile(ref, "media.json");
239456
- }
239457
- function readRects(ref) {
239458
- assertKind(ref, "rec", "readRects");
239459
- return readNdjsonArtifactFile(ref, "rects.jsonl");
239460
- }
239461
- function readEvents(ref) {
239462
- assertKind(ref, "rec", "readEvents");
239463
- return readNdjsonArtifactFile(ref, "events.jsonl");
239464
- }
239465
- function readMarkers(ref) {
239466
- assertKind(ref, "rec", "readMarkers");
239467
- return readJsonArtifactFile(ref, "markers.json");
239468
- }
239469
- function unstableRegionsFor(ref) {
239470
- const geometry = readGeometry(ref);
239471
- return geometry.unstableRegions ?? [];
239472
- }
239473
- function rectsOverlap(a, b) {
239474
- 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;
239475
- }
239476
- function annotateUnstableFacts(facts, regions) {
239477
- return facts.map((fact4) => {
239478
- const caveats = [];
239479
- for (const region of regions) {
239480
- const byElement = fact4.elementId !== void 0 && (region.elementIds?.includes(fact4.elementId) ?? false);
239481
- const byRect = fact4.rect !== void 0 && region.rect !== void 0 && rectsOverlap(fact4.rect, region.rect);
239482
- if (byElement || byRect) {
239483
- caveats.push({ regionId: region.id, selector: region.selector, reason: region.reason });
239484
- }
239485
- }
239486
- return { fact: fact4, caveats };
239487
- });
239488
- }
239489
- var fs23, path28, ArtifactResolutionError, SNAP_CONFIG, REC_CONFIG, TRACE_CONFIG, CREATING_COMMAND_BY_FILE;
239490
- var init_artifact = __esm({
239491
- "src/output/artifact.ts"() {
239492
- fs23 = __toESM(require("fs"));
239493
- path28 = __toESM(require("path"));
239494
- init_artifacts();
239495
- init_session_context();
239496
- ArtifactResolutionError = class extends Error {
239497
- ref;
239498
- searched;
239499
- creatingCommand;
239500
- constructor(ref, searched, detail, creatingCommand) {
239501
- const parts = [`could not resolve ref ${JSON.stringify(ref)}: ${detail}`];
239502
- if (searched.length) parts.push(`looked at: ${searched.join(", ")}`);
239503
- if (creatingCommand) parts.push(`create it with: ${creatingCommand}`);
239504
- super(parts.join(" \u2014 "));
239505
- this.name = "ArtifactResolutionError";
239506
- this.ref = ref;
239507
- this.searched = searched;
239508
- this.creatingCommand = creatingCommand;
239509
- }
239510
- };
239511
- SNAP_CONFIG = {
239512
- kind: "snap",
239513
- idSegments: ["measure", "snaps"],
239514
- creatingCommand: "capture measure snap",
239515
- label: "snapshot"
239516
- };
239517
- REC_CONFIG = {
239518
- kind: "rec",
239519
- idSegments: ["motion", "recs"],
239520
- creatingCommand: "capture motion rec",
239521
- label: "recording"
239522
- };
239523
- TRACE_CONFIG = {
239524
- kind: "trace",
239525
- idSegments: ["perf", "traces"],
239526
- creatingCommand: "capture perf trace",
239527
- label: "trace"
239528
- };
239529
- CREATING_COMMAND_BY_FILE = {
239530
- "geometry.json": "capture measure snap",
239531
- "styles.json": "capture measure snap",
239532
- "hittest.json": "capture measure snap",
239533
- "text.json": "capture measure snap",
239534
- "forms.json": "capture measure snap",
239535
- "animation.json": "capture measure snap",
239536
- "ax.json": "capture measure snap",
239537
- "media.json": "capture measure snap",
239538
- "queries.json": "capture measure snap",
239539
- "focus.json": "capture measure snap",
239540
- "scroll.json": "capture measure snap",
239541
- "layers.json": "capture measure snap",
239542
- "states.json": "capture measure snap --state <state>",
239543
- "churn.json": "capture measure snap (written only when the page did not settle)",
239544
- "pixels.json": "capture measure snap --pixels",
239545
- "dom.html": "capture measure snap",
239546
- "screenshot.png": "capture measure snap",
239547
- "meta.json": "capture measure snap (or capture motion rec for a recording)",
239548
- "rects.jsonl": "capture motion rec",
239549
- "events.jsonl": "capture motion rec",
239550
- "markers.json": "capture motion rec",
239551
- "trace.json": "capture perf trace"
239552
- };
239553
- }
239554
- });
239555
-
239556
239614
  // src/cdp/commands/measure/snap.ts
239557
239615
  function generateSnapId() {
239558
239616
  return `snap-${Date.now().toString(36)}-${(0, import_crypto2.randomBytes)(4).toString("hex")}`;
@@ -267455,10 +267513,10 @@ Selecting a noun: session, page, and tab address the live browser; measure, moti
267455
267513
 
267456
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.
267457
267515
 
267458
- 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.`;
267459
267517
  }
267460
267518
  function printVersion() {
267461
- const declared = "1.4.21";
267519
+ const declared = "1.4.23";
267462
267520
  if (declared) {
267463
267521
  console.log(declared);
267464
267522
  return;