@enricai/barnacle 1.5.0 → 1.6.1

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.
Files changed (71) hide show
  1. package/README.md +86 -0
  2. package/dist/api/errors.d.ts +2 -1
  3. package/dist/api/errors.d.ts.map +1 -1
  4. package/dist/api/errors.js +3 -2
  5. package/dist/api/errors.js.map +1 -1
  6. package/dist/lib/llm/judge.d.ts +1 -1
  7. package/dist/lib/llm/judge.js +1 -1
  8. package/dist/recon/capture-filters.d.ts +40 -0
  9. package/dist/recon/capture-filters.d.ts.map +1 -0
  10. package/dist/recon/capture-filters.js +106 -0
  11. package/dist/recon/capture-filters.js.map +1 -0
  12. package/dist/recon/form-schema.d.ts +51 -0
  13. package/dist/recon/form-schema.d.ts.map +1 -0
  14. package/dist/recon/form-schema.js +20 -0
  15. package/dist/recon/form-schema.js.map +1 -0
  16. package/dist/recon/load-form-schema.d.ts +24 -0
  17. package/dist/recon/load-form-schema.d.ts.map +1 -0
  18. package/dist/recon/load-form-schema.js +66 -0
  19. package/dist/recon/load-form-schema.js.map +1 -0
  20. package/dist/scraper/cookie-jar.d.ts +19 -0
  21. package/dist/scraper/cookie-jar.d.ts.map +1 -0
  22. package/dist/scraper/cookie-jar.js +43 -0
  23. package/dist/scraper/cookie-jar.js.map +1 -0
  24. package/dist/scraper/errors.d.ts +14 -14
  25. package/dist/scraper/errors.d.ts.map +1 -1
  26. package/dist/scraper/errors.js +15 -15
  27. package/dist/scraper/errors.js.map +1 -1
  28. package/dist/scraper/flow-runner.d.ts.map +1 -1
  29. package/dist/scraper/flow-runner.js +28 -8
  30. package/dist/scraper/flow-runner.js.map +1 -1
  31. package/dist/scraper/http-client.d.ts +12 -0
  32. package/dist/scraper/http-client.d.ts.map +1 -1
  33. package/dist/scraper/http-client.js +11 -15
  34. package/dist/scraper/http-client.js.map +1 -1
  35. package/dist/scraper/parse-json-response.d.ts +7 -5
  36. package/dist/scraper/parse-json-response.d.ts.map +1 -1
  37. package/dist/scraper/parse-json-response.js +9 -8
  38. package/dist/scraper/parse-json-response.js.map +1 -1
  39. package/dist/scraper/rate-limited-json-client.d.ts +8 -1
  40. package/dist/scraper/rate-limited-json-client.d.ts.map +1 -1
  41. package/dist/scraper/rate-limited-json-client.js +6 -1
  42. package/dist/scraper/rate-limited-json-client.js.map +1 -1
  43. package/dist/scraper/session-browserbase.d.ts +6 -0
  44. package/dist/scraper/session-browserbase.d.ts.map +1 -1
  45. package/dist/scraper/session-browserbase.js +6 -0
  46. package/dist/scraper/session-browserbase.js.map +1 -1
  47. package/dist/scripts/recon-browser.d.ts +19 -2
  48. package/dist/scripts/recon-browser.d.ts.map +1 -1
  49. package/dist/scripts/recon-browser.js +40 -5
  50. package/dist/scripts/recon-browser.js.map +1 -1
  51. package/dist/scripts/recon-generate.d.ts +50 -21
  52. package/dist/scripts/recon-generate.d.ts.map +1 -1
  53. package/dist/scripts/recon-generate.js +190 -182
  54. package/dist/scripts/recon-generate.js.map +1 -1
  55. package/dist/scripts/recon-http.d.ts +5 -2
  56. package/dist/scripts/recon-http.d.ts.map +1 -1
  57. package/dist/scripts/recon-http.js +16 -4
  58. package/dist/scripts/recon-http.js.map +1 -1
  59. package/dist/scripts/recon-shared.d.ts +33 -0
  60. package/dist/scripts/recon-shared.d.ts.map +1 -1
  61. package/dist/scripts/recon-shared.js +2 -1
  62. package/dist/scripts/recon-shared.js.map +1 -1
  63. package/dist/testing/persona-fixture.d.ts +2 -2
  64. package/dist/testing/persona-fixture.d.ts.map +1 -1
  65. package/dist/testing/persona-fixture.js +2 -2
  66. package/dist/testing/persona-fixture.js.map +1 -1
  67. package/package.json +13 -1
  68. package/dist/scraper/oracle-sentinels.d.ts +0 -22
  69. package/dist/scraper/oracle-sentinels.d.ts.map +0 -1
  70. package/dist/scraper/oracle-sentinels.js +0 -40
  71. package/dist/scraper/oracle-sentinels.js.map +0 -1
package/README.md CHANGED
@@ -106,6 +106,63 @@ Each step runs through a self-healing cascade (`act` → `observe + act` → `ob
106
106
 
107
107
  Total runtime: 20–40 minutes for a typical flow (longer if healing or replans fire), fully unattended.
108
108
 
109
+ #### Cookie-jar snapshots
110
+
111
+ Alongside the network captures, every run snapshots the browser's complete cookie jar (via CDP `Network.getAllCookies`, which returns the whole-browser jar regardless of the current page's URL — unlike `document.cookie` or `Page.getCookies`, it also sees HttpOnly cookies) at each phase boundary: the initial goto, immediately before each flow step (`pre-step`), immediately after each flow step completes (`post-step`), and once more at run completion (`run-complete`).
112
+
113
+ Snapshots land in `/tmp/recon/cookies/<NNN>-<label>-<phase>.json` — one file per boundary, using the same zero-padded chronological index convention as the network captures. `<label>` is the boundary kind (`goto`, `pre-step`, `post-step`, `run-complete`); `<phase>` is the current step's slugified instruction (e.g. `click-the-apply-button`), or `home` before the first step starts.
114
+
115
+ Each file is a JSON object:
116
+
117
+ ```json
118
+ {
119
+ "label": "post-step",
120
+ "phase": "click-the-apply-button",
121
+ "stepIndex": 2,
122
+ "timestamp": "2026-07-18T12:34:56.789Z",
123
+ "cookies": [
124
+ {
125
+ "name": "_appcast_attr",
126
+ "value": "abc123",
127
+ "domain": ".appcast.io",
128
+ "path": "/",
129
+ "expires": 1234567890,
130
+ "size": 20,
131
+ "httpOnly": true,
132
+ "secure": true,
133
+ "session": false,
134
+ "sameSite": "Lax"
135
+ }
136
+ ]
137
+ }
138
+ ```
139
+
140
+ Field reference (mirrors CDP's `Network.Cookie` type verbatim — no remapping between capture and disk):
141
+
142
+ | Field | Meaning |
143
+ | --- | --- |
144
+ | `name` / `value` | The cookie's name and value. |
145
+ | `domain` | Scope, e.g. `.appcast.io` (all subdomains) vs. `apply.appcast.io` (exact host) — the detail needed to tell a click-domain cookie from an apply-domain cookie. |
146
+ | `path` | Cookie path scope. |
147
+ | `expires` | Raw CDP epoch-seconds number; `-1` means a session cookie (also reflected in `session: true`). Not reformatted — read it as CDP reports it. |
148
+ | `size` | Cookie size in bytes, as reported by CDP. |
149
+ | `httpOnly` / `secure` | Standard cookie flags. |
150
+ | `session` | `true` for a session cookie (no persistent expiry). |
151
+ | `sameSite` | `"Strict" \| "Lax" \| "None" \| null` — `null` when the cookie doesn't set the attribute. |
152
+
153
+ If the CDP call fails, the file still writes but with an empty `cookies` array and an `error` string field carrying the failure message — cookie telemetry is best-effort and never aborts the run.
154
+
155
+ **Diffing what a phase established:** to isolate what a specific traversal (e.g. the click.appcast.io redirect) minted, diff its `post-step` snapshot against the `pre-step` snapshot for the *next* step — cookies present in the later file but absent from the earlier one were established during that step:
156
+
157
+ ```bash
158
+ diff <(jq -S .cookies /tmp/recon/cookies/004-post-step-click-the-apply-button.json) \
159
+ <(jq -S .cookies /tmp/recon/cookies/005-pre-step-fill-in-your-name.json)
160
+ ```
161
+
162
+ **Cookies actually sent per request:** the jar snapshot shows what's *available*, not what's *sent*. Each network capture in `/tmp/recon/graphql/` separately carries the outgoing `Cookie` header in its `requestHeaders` (recovered via CDP's `Network.requestWillBeSentExtraInfo`, since `requestWillBeSent` omits it by design) — cross-reference that capture's `requestHeaders.Cookie` against a jar snapshot to see which of the available cookies a given request, e.g. the application submit, actually sent.
163
+
164
+ **Caveat on `Set-Cookie`:** response captures fold `responseReceivedExtraInfo` headers (which is where `Set-Cookie` actually appears — `responseReceived` omits it) into `responseHeaders` as a flat `Record<string, string>`. CDP does not guarantee multiple `Set-Cookie` values on one response stay distinguishable once folded into that shape — if a single response mints more than one cookie, treat the jar snapshot (not the response capture's `Set-Cookie` header) as the source of truth for what actually landed.
165
+
109
166
  ### Phase 2–3 — Replay and probe
110
167
 
111
168
  ```bash
@@ -165,6 +222,35 @@ pnpm run recon:generate -- --site-id my-site --vocabulary ./src/recon/my-vocabul
165
222
 
166
223
  > **Deprecated fallback:** omit `--vocabulary` and the generator falls back to a built-in recruiting table (first/last name, email, phone, address), warning as it does. That table is **removed in 2.0.0**, after which an absent vocabulary on a spliceable flow is an error. Supply one now.
167
224
 
225
+ #### Telling the generator your ATS's form-schema wire keys
226
+
227
+ Where `--vocabulary` matches instruction *prose*, `--form-schema` names the JSON *keys* the generator reads out of an ATS's form-definition responses when recovering field ids, option ids, and submitted values. The engine ships no vendor's format; a site whose ATS exposes a form definition declares its keys with `--form-schema`:
228
+
229
+ ```ts
230
+ // src/recon/my-form-schema.ts
231
+ import type { ReconFormSchema } from "@enricai/barnacle/recon/form-schema";
232
+
233
+ export const formSchema: ReconFormSchema = {
234
+ fieldIdKey: "fieldId", // UUID-valued field identity
235
+ fieldNameKeys: ["code", "label"], // code, then human label — code preferred
236
+ fieldOptionsKey: "options",
237
+ optionIdKey: "optionId", // option id, inside the options array
238
+ optionValueKey: "optionLabel", // option label, inside the options array
239
+ responseValueKey: "submittedValue", // submitted free value
240
+ responseOptionIdKey: "submittedOptionId", // submitted option reference
241
+ };
242
+ ```
243
+
244
+ ```bash
245
+ pnpm run recon:generate -- --site-id my-site --form-schema ./src/recon/my-form-schema.ts
246
+ ```
247
+
248
+ - The specifier follows the same rule as `--vocabulary`: a leading `.` or `/` is a filesystem path; anything else resolves from your `node_modules`. The module may export `formSchema` or a default.
249
+ - **`--form-schema none`** for a site with no ATS form definition (a search API, a cruise site) — same as omitting it. "none" is the explicit form.
250
+ - Wire keys anchor `"key":"uuid"` markers, so they may be any non-empty string without a quote or backslash — the JS-identifier rule that governs vocabulary field names does **not** apply here.
251
+ - `fieldNameKeys` models two roles: the first key is a machine code (PascalCased directly), the second is a human label (run through the section-heading heuristic). Supply one key for a label-only ATS, or two for one that exposes both. Additional keys are unused.
252
+ - Omit `--form-schema` (or pass `none`) and ATS form-key recovery does not run — the engine hardcodes no vendor's wire format. A site whose ATS exposes a form definition must supply one to recover its option fields. See issue #57.
253
+
168
254
  #### Mapping the site's screening questions
169
255
 
170
256
  If the site asks screening questions, tell the generator which payload field answers each one — the same reasoning applies, it cannot know what your site asks:
@@ -54,7 +54,8 @@ export declare class ThrottledRequestError extends ApiError {
54
54
  constructor(message?: string);
55
55
  }
56
56
  /**
57
- * The upstream vendor has locked the target URL (e.g. Oracle ORA_URL_LOCKED).
57
+ * The upstream site has locked the target URL (a plugin raises the underlying
58
+ * `HttpUrlLockedError`, e.g. from a `classifyResponseBody` sentinel).
58
59
  * Signals "back off and retry later" — not a browser-fallback trigger.
59
60
  */
60
61
  export declare class UrlLockedError extends ApiError {
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/api/errors.ts"],"names":[],"mappings":"AAGA,OAAO,EAAwC,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAE5F;;;;GAIG;AACH,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,gBAAgB;IACxB,MAAM,EAAE;QACN,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,eAAe,EAAE,CAAC;KAC5B,CAAC;CACH;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAgCzD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAQvD;AAED;;;;GAIG;AACH,qBAAa,QAAS,SAAQ,KAAK;IACjC,SAAgB,IAAI,EAAE,SAAS,CAAC;IAChC,SAAgB,UAAU,EAAE,MAAM,CAAC;IAEnC,YAAY,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,SAAU,EAKjE;CACF;AAED,qBAAa,iBAAkB,SAAQ,QAAQ;IAC7C,YAAY,OAAO,SAA4C,EAE9D;CACF;AAED,qBAAa,mBAAoB,SAAQ,QAAQ;IAC/C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACF;AAED,qBAAa,qBAAsB,SAAQ,QAAQ;IACjD,YAAY,OAAO,SAAwB,EAE1C;CACF;AAED;;;GAGG;AACH,qBAAa,cAAe,SAAQ,QAAQ;IAC1C,YAAY,OAAO,SAA6D,EAE/E;CACF;AAED,qBAAa,kBAAmB,SAAQ,QAAQ;IAC9C,YAAY,OAAO,SAA0C,EAE5D;CACF;AAED,qBAAa,uBAAwB,SAAQ,QAAQ;IACnD,YAAY,OAAO,SAA2C,EAE7D;CACF;AAED,qBAAa,oBAAqB,SAAQ,QAAQ;IAChD,YAAY,OAAO,SAA6C,EAE/D;CACF;AAED;;;;;GAKG;AACH,qBAAa,eAAgB,SAAQ,QAAQ;IAC3C,YAAY,OAAO,SAA4C,EAE9D;CACF;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,MAAM,EACf,UAAU,SAAU,GACnB,gBAAgB,CAgBlB"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/api/errors.ts"],"names":[],"mappings":"AAGA,OAAO,EAAwC,KAAK,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAE5F;;;;GAIG;AACH,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,gBAAgB;IACxB,MAAM,EAAE;QACN,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,OAAO,EAAE,eAAe,EAAE,CAAC;KAC5B,CAAC;CACH;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAgCzD;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAQvD;AAED;;;;GAIG;AACH,qBAAa,QAAS,SAAQ,KAAK;IACjC,SAAgB,IAAI,EAAE,SAAS,CAAC;IAChC,SAAgB,UAAU,EAAE,MAAM,CAAC;IAEnC,YAAY,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,SAAU,EAKjE;CACF;AAED,qBAAa,iBAAkB,SAAQ,QAAQ;IAC7C,YAAY,OAAO,SAA4C,EAE9D;CACF;AAED,qBAAa,mBAAoB,SAAQ,QAAQ;IAC/C,YAAY,OAAO,EAAE,MAAM,EAE1B;CACF;AAED,qBAAa,qBAAsB,SAAQ,QAAQ;IACjD,YAAY,OAAO,SAAwB,EAE1C;CACF;AAED;;;;GAIG;AACH,qBAAa,cAAe,SAAQ,QAAQ;IAC1C,YAAY,OAAO,SAA2D,EAE7E;CACF;AAED,qBAAa,kBAAmB,SAAQ,QAAQ;IAC9C,YAAY,OAAO,SAA0C,EAE5D;CACF;AAED,qBAAa,uBAAwB,SAAQ,QAAQ;IACnD,YAAY,OAAO,SAA2C,EAE7D;CACF;AAED,qBAAa,oBAAqB,SAAQ,QAAQ;IAChD,YAAY,OAAO,SAA6C,EAE/D;CACF;AAED;;;;;GAKG;AACH,qBAAa,eAAgB,SAAQ,QAAQ;IAC3C,YAAY,OAAO,SAA4C,EAE9D;CACF;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,SAAS,EACf,OAAO,EAAE,MAAM,EACf,UAAU,SAAU,GACnB,gBAAgB,CAgBlB"}
@@ -100,11 +100,12 @@ class ThrottledRequestError extends ApiError {
100
100
  }
101
101
  exports.ThrottledRequestError = ThrottledRequestError;
102
102
  /**
103
- * The upstream vendor has locked the target URL (e.g. Oracle ORA_URL_LOCKED).
103
+ * The upstream site has locked the target URL (a plugin raises the underlying
104
+ * `HttpUrlLockedError`, e.g. from a `classifyResponseBody` sentinel).
104
105
  * Signals "back off and retry later" — not a browser-fallback trigger.
105
106
  */
106
107
  class UrlLockedError extends ApiError {
107
- constructor(message = "target URL is locked by the upstream vendor; retry later") {
108
+ constructor(message = "target URL is locked by the upstream site; retry later") {
108
109
  super(common_1.ERROR_CODES.URL_LOCKED, message);
109
110
  }
110
111
  }
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/api/errors.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/api/errors.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@
3
3
  * routes through this — submit-verification, invalid-field detection, modal-
4
4
  * priority ranking, error-message extraction. Replaces deterministic regexes
5
5
  * that silently miss patterns outside what we've happened to observe across
6
- * AppCast + ClearCompany (both Angular).
6
+ * the Angular-based ATS sites.
7
7
  *
8
8
  * Why a separate primitive instead of inlining into each call site:
9
9
  * - Single chokepoint for telemetry, error classification, and latency
@@ -4,7 +4,7 @@
4
4
  * routes through this — submit-verification, invalid-field detection, modal-
5
5
  * priority ranking, error-message extraction. Replaces deterministic regexes
6
6
  * that silently miss patterns outside what we've happened to observe across
7
- * AppCast + ClearCompany (both Angular).
7
+ * the Angular-based ATS sites.
8
8
  *
9
9
  * Why a separate primitive instead of inlining into each call site:
10
10
  * - Single chokepoint for telemetry, error classification, and latency
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Shared predicates for deciding which captured requests are the site's real
3
+ * flow versus incidental browser noise (analytics beacons, ad-tech, a page's own
4
+ * error-reporting sink, static assets).
5
+ *
6
+ * Lives outside `src/scripts/` so both the emitter (`recon-generate.ts`, which
7
+ * must not emit noise into a generated plugin) and the HTTP probe
8
+ * (`recon-http.ts`, which must not burn hours replaying third-party hosts) apply
9
+ * ONE definition of "noise." Previously only the emitter filtered; the probe
10
+ * replayed and rate-limited everything, including `clicktale`/`adsrvr`/`tiktok`.
11
+ */
12
+ /**
13
+ * Path/URL substrings we always treat as analytics or logging noise. Site-specific
14
+ * trackers belong in the `RECON_TELEMETRY_URL_PATTERNS` env var (comma-separated),
15
+ * not here — the engine must not carry any one site's ad-tech domains.
16
+ *
17
+ * Read at call time, not frozen at import: a module-level const would ignore an
18
+ * env var set after load, the exact foot-gun `RECON_QUESTION_KEYWORDS` documents.
19
+ */
20
+ export declare function telemetryUrlPatterns(): string[];
21
+ /**
22
+ * A path whose own segment is `error`/`errors` is a client-side reporting sink,
23
+ * never a call a caller wants replayed or emitted.
24
+ *
25
+ * Matched on a whole path segment rather than by substring so `/error-codes` and
26
+ * `/terrorism-screening` stay data endpoints, and kept out of the telemetry list
27
+ * because that list is literal substrings — a site's own sink is structural, not
28
+ * an ad-tech domain the operator must enumerate.
29
+ */
30
+ export declare const ERROR_SINK_PATH_SEGMENT: RegExp;
31
+ /**
32
+ * True when a captured URL is noise the recon pipeline should skip: a telemetry
33
+ * pattern (including any `RECON_TELEMETRY_URL_PATTERNS` addition), a third-party
34
+ * asset/tracking host, a same-host error-reporting sink, or a static asset.
35
+ *
36
+ * The one gate both the emitter and the probe consult so "what counts as the
37
+ * site's real flow" cannot drift between them.
38
+ */
39
+ export declare function isNoiseUrl(url: string): boolean;
40
+ //# sourceMappingURL=capture-filters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture-filters.d.ts","sourceRoot":"","sources":["../../src/recon/capture-filters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,EAAE,CAW/C;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,QAAyB,CAAC;AAmC9D;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAc/C"}
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ /**
3
+ * Shared predicates for deciding which captured requests are the site's real
4
+ * flow versus incidental browser noise (analytics beacons, ad-tech, a page's own
5
+ * error-reporting sink, static assets).
6
+ *
7
+ * Lives outside `src/scripts/` so both the emitter (`recon-generate.ts`, which
8
+ * must not emit noise into a generated plugin) and the HTTP probe
9
+ * (`recon-http.ts`, which must not burn hours replaying third-party hosts) apply
10
+ * ONE definition of "noise." Previously only the emitter filtered; the probe
11
+ * replayed and rate-limited everything, including `clicktale`/`adsrvr`/`tiktok`.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.ERROR_SINK_PATH_SEGMENT = void 0;
15
+ exports.telemetryUrlPatterns = telemetryUrlPatterns;
16
+ exports.isNoiseUrl = isNoiseUrl;
17
+ /**
18
+ * Path/URL substrings we always treat as analytics or logging noise. Site-specific
19
+ * trackers belong in the `RECON_TELEMETRY_URL_PATTERNS` env var (comma-separated),
20
+ * not here — the engine must not carry any one site's ad-tech domains.
21
+ *
22
+ * Read at call time, not frozen at import: a module-level const would ignore an
23
+ * env var set after load, the exact foot-gun `RECON_QUESTION_KEYWORDS` documents.
24
+ */
25
+ function telemetryUrlPatterns() {
26
+ return [
27
+ "/util/logging/vweb/message",
28
+ "/blank/page",
29
+ "stats.g.doubleclick.net",
30
+ "google-analytics.com",
31
+ ...(process.env.RECON_TELEMETRY_URL_PATTERNS ?? "")
32
+ .split(",")
33
+ .map((p) => p.trim())
34
+ .filter(Boolean),
35
+ ];
36
+ }
37
+ /**
38
+ * A path whose own segment is `error`/`errors` is a client-side reporting sink,
39
+ * never a call a caller wants replayed or emitted.
40
+ *
41
+ * Matched on a whole path segment rather than by substring so `/error-codes` and
42
+ * `/terrorism-screening` stay data endpoints, and kept out of the telemetry list
43
+ * because that list is literal substrings — a site's own sink is structural, not
44
+ * an ad-tech domain the operator must enumerate.
45
+ */
46
+ exports.ERROR_SINK_PATH_SEGMENT = /(^|\/)errors?(\/|$)/i;
47
+ /**
48
+ * Third-party hosts recon repeatedly wastes time on: ad-tech, session replay,
49
+ * social pixels, tag managers. A capture whose host matches is not the site's
50
+ * own endpoint and never worth replaying or rate-limiting.
51
+ *
52
+ * A suffix match on the registrable-ish host substring, so `x.clicktale.net`
53
+ * and `sync.adsrvr.org` both match. Extendable per-site via
54
+ * `RECON_TELEMETRY_URL_PATTERNS`, which {@link isNoiseUrl} also honors.
55
+ */
56
+ const THIRD_PARTY_ASSET_HOSTS = [
57
+ "clicktale.net",
58
+ "adsrvr.org",
59
+ "tiktok.com",
60
+ "doubleclick.net",
61
+ "google-analytics.com",
62
+ "googletagmanager.com",
63
+ "facebook.net",
64
+ "facebook.com",
65
+ "hotjar.com",
66
+ "segment.io",
67
+ "segment.com",
68
+ "fullstory.com",
69
+ "cdn.cookielaw.org",
70
+ "onetrust.com",
71
+ "demdex.net",
72
+ "omtrdc.net",
73
+ "quantserve.com",
74
+ "scorecardresearch.com",
75
+ ];
76
+ /** Static-asset extensions a probe should never replay as an API endpoint. */
77
+ const ASSET_EXTENSION = /\.(js|mjs|css|png|jpe?g|gif|svg|webp|ico|woff2?|ttf|eot|map)$/i;
78
+ /**
79
+ * True when a captured URL is noise the recon pipeline should skip: a telemetry
80
+ * pattern (including any `RECON_TELEMETRY_URL_PATTERNS` addition), a third-party
81
+ * asset/tracking host, a same-host error-reporting sink, or a static asset.
82
+ *
83
+ * The one gate both the emitter and the probe consult so "what counts as the
84
+ * site's real flow" cannot drift between them.
85
+ */
86
+ function isNoiseUrl(url) {
87
+ const patterns = telemetryUrlPatterns();
88
+ if (patterns.some((p) => url.includes(p)))
89
+ return true;
90
+ let parsed;
91
+ try {
92
+ parsed = new URL(url);
93
+ }
94
+ catch {
95
+ return false;
96
+ }
97
+ const host = parsed.host.toLowerCase();
98
+ if (THIRD_PARTY_ASSET_HOSTS.some((h) => host === h || host.endsWith(`.${h}`)))
99
+ return true;
100
+ if (exports.ERROR_SINK_PATH_SEGMENT.test(parsed.pathname))
101
+ return true;
102
+ if (ASSET_EXTENSION.test(parsed.pathname))
103
+ return true;
104
+ return false;
105
+ }
106
+ //# sourceMappingURL=capture-filters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture-filters.js","sourceRoot":"","sources":["../../src/recon/capture-filters.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;;;AAEH;;;;;;;GAOG;AACH;IACE,OAAO;QACL,4BAA4B;QAC5B,aAAa;QACb,yBAAyB;QACzB,sBAAsB;QACtB,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,EAAE,CAAC;aAChD,KAAK,CAAC,GAAG,CAAC;aACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;aACpB,MAAM,CAAC,OAAO,CAAC;KACnB,CAAC;AACJ,CAAC;AAED;;;;;;;;GAQG;AACU,QAAA,uBAAuB,GAAG,sBAAsB,CAAC;AAE9D;;;;;;;;GAQG;AACH,MAAM,uBAAuB,GAAG;IAC9B,eAAe;IACf,YAAY;IACZ,YAAY;IACZ,iBAAiB;IACjB,sBAAsB;IACtB,sBAAsB;IACtB,cAAc;IACd,cAAc;IACd,YAAY;IACZ,YAAY;IACZ,aAAa;IACb,eAAe;IACf,mBAAmB;IACnB,cAAc;IACd,YAAY;IACZ,YAAY;IACZ,gBAAgB;IAChB,uBAAuB;CACxB,CAAC;AAEF,8EAA8E;AAC9E,MAAM,eAAe,GAAG,gEAAgE,CAAC;AAEzF;;;;;;;GAOG;AACH,oBAA2B,GAAW;IACpC,MAAM,QAAQ,GAAG,oBAAoB,EAAE,CAAC;IACxC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACvD,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;IACvC,IAAI,uBAAuB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3F,IAAI,QAAA,uBAAuB,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/D,IAAI,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IACvD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Consumer-supplied form-schema that names the wire keys the generator reads out
3
+ * of an ATS's form-definition responses (field ids, option ids, submitted values).
4
+ *
5
+ * A sibling to {@link @/recon/vocabulary}, not a field on it: a vocabulary matches
6
+ * English instruction prose, this matches JSON response keys. Different inputs,
7
+ * different validation, independent applicability — a cruise site needs a
8
+ * vocabulary and no form-schema. Published at `@enricai/barnacle/recon/form-schema`;
9
+ * all imports are type-only so there are zero runtime side effects.
10
+ *
11
+ * Why this exists (issue #57): the generator hardcoded one ATS vendor's wire
12
+ * format in ~8 private functions. That is a claim about a *vendor's schema*, not
13
+ * about HTTP — so the engine no longer carries any vendor's keys. A consumer
14
+ * whose ATS exposes a form definition declares its keys with `--form-schema`;
15
+ * absent one, form-key recovery does not run. This is the same inversion
16
+ * `--vocabulary` already made for instruction prose.
17
+ */
18
+ /**
19
+ * The response-key names the generator threads into raw-body string anchors when
20
+ * recovering an ATS form definition.
21
+ *
22
+ * These are *key names, not values*: each is interpolated into a
23
+ * `"${key}":"${uuid}"` marker used to locate and substitute UUIDs in a captured
24
+ * request body. A legal wire key like `field-id` is therefore fine — the loader
25
+ * rejects only quotes and backslashes, which would break the marker, NOT the
26
+ * JS-identifier rule the vocabulary uses (those keys splice into `payload.<name>`
27
+ * code; these do not).
28
+ */
29
+ export interface ReconFormSchema {
30
+ /** The UUID-valued field-identity key. Anchors the substitution passes. */
31
+ fieldIdKey: string;
32
+ /**
33
+ * The name key(s), by role: the first is a machine code (PascalCased
34
+ * directly), the second is a human label (run through the section-heading
35
+ * heuristic). Supply one key for a label-only ATS, two for one exposing both
36
+ * (the code is preferred when present). Must be non-empty; a third+ key is
37
+ * unused — the model has exactly the two roles.
38
+ */
39
+ fieldNameKeys: string[];
40
+ /** The options-array key on a field. */
41
+ fieldOptionsKey: string;
42
+ /** The option-id key, INSIDE a `fieldOptionsKey` entry. */
43
+ optionIdKey: string;
44
+ /** The option-label key, inside a `fieldOptionsKey` entry. */
45
+ optionValueKey: string;
46
+ /** The submitted free-value key (a distinct role from {@link optionValueKey}). */
47
+ responseValueKey: string;
48
+ /** The submitted option-reference key (distinct from the schema-side {@link optionIdKey}). */
49
+ responseOptionIdKey: string;
50
+ }
51
+ //# sourceMappingURL=form-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form-schema.d.ts","sourceRoot":"","sources":["../../src/recon/form-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH;;;;;;;;;;GAUG;AACH,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,wCAAwC;IACxC,eAAe,EAAE,MAAM,CAAC;IACxB,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,cAAc,EAAE,MAAM,CAAC;IACvB,kFAAkF;IAClF,gBAAgB,EAAE,MAAM,CAAC;IACzB,8FAA8F;IAC9F,mBAAmB,EAAE,MAAM,CAAC;CAC7B"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ /**
3
+ * Consumer-supplied form-schema that names the wire keys the generator reads out
4
+ * of an ATS's form-definition responses (field ids, option ids, submitted values).
5
+ *
6
+ * A sibling to {@link @/recon/vocabulary}, not a field on it: a vocabulary matches
7
+ * English instruction prose, this matches JSON response keys. Different inputs,
8
+ * different validation, independent applicability — a cruise site needs a
9
+ * vocabulary and no form-schema. Published at `@enricai/barnacle/recon/form-schema`;
10
+ * all imports are type-only so there are zero runtime side effects.
11
+ *
12
+ * Why this exists (issue #57): the generator hardcoded one ATS vendor's wire
13
+ * format in ~8 private functions. That is a claim about a *vendor's schema*, not
14
+ * about HTTP — so the engine no longer carries any vendor's keys. A consumer
15
+ * whose ATS exposes a form definition declares its keys with `--form-schema`;
16
+ * absent one, form-key recovery does not run. This is the same inversion
17
+ * `--vocabulary` already made for instruction prose.
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ //# sourceMappingURL=form-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"form-schema.js","sourceRoot":"","sources":["../../src/recon/form-schema.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;GAgBG"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Engine-internal loader for `--form-schema`. Like {@link @/recon/load-vocabulary},
3
+ * deliberately NOT in the package's `exports` map: consumers author a form-schema
4
+ * against the `./recon/form-schema` type contract, and only `recon-generate` ever
5
+ * loads one. Exporting this would publish an import-time side effect (dynamic
6
+ * `import()` of arbitrary consumer code) as public API for no caller.
7
+ */
8
+ import type { ReconFormSchema } from "../recon/form-schema";
9
+ /** The `--form-schema` value that opts a site out of form-key recovery entirely. */
10
+ export declare const FORM_SCHEMA_NONE = "none";
11
+ /**
12
+ * Loads a consumer's form-schema module.
13
+ *
14
+ * Resolution reuses {@link resolvePluginSpecifier}, so `--form-schema` accepts the
15
+ * same specifier forms as `BARNACLE_PLUGINS` and `--vocabulary`. Export resolution
16
+ * mirrors the plugin loader's `m.formSchema ?? m.default ?? m`. The
17
+ * {@link FORM_SCHEMA_NONE} sentinel returns `null` (not an empty struct — see the
18
+ * form-schema module for why), the shape for "this site has no ATS form data."
19
+ *
20
+ * Throws rather than falling back: a form-schema that was asked for and is broken
21
+ * is an error, while asking for nothing is the caller's explicit `none`.
22
+ */
23
+ export declare function loadReconFormSchema(specifier: string, baseDir: string): Promise<ReconFormSchema | null>;
24
+ //# sourceMappingURL=load-form-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-form-schema.d.ts","sourceRoot":"","sources":["../../src/recon/load-form-schema.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,SAAS,CAAC;AA4BvC;;;;;;;;;;;GAWG;AACH,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAejC"}
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ /**
3
+ * Engine-internal loader for `--form-schema`. Like {@link @/recon/load-vocabulary},
4
+ * deliberately NOT in the package's `exports` map: consumers author a form-schema
5
+ * against the `./recon/form-schema` type contract, and only `recon-generate` ever
6
+ * loads one. Exporting this would publish an import-time side effect (dynamic
7
+ * `import()` of arbitrary consumer code) as public API for no caller.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.FORM_SCHEMA_NONE = void 0;
11
+ exports.loadReconFormSchema = loadReconFormSchema;
12
+ const v4_1 = require("zod/v4");
13
+ const errors_1 = require("../lib/errors");
14
+ const discover_1 = require("../plugins/discover");
15
+ /** The `--form-schema` value that opts a site out of form-key recovery entirely. */
16
+ exports.FORM_SCHEMA_NONE = "none";
17
+ /**
18
+ * Wire keys interpolate into `"${key}":"${uuid}"` markers, not into code, so the
19
+ * rule is NOT the vocabulary's JS-identifier rule: a legal response key like
20
+ * `field-id` must be accepted. Reject only what would break the marker — an empty
21
+ * key (matches every field), or a quote/backslash (escapes the JSON string anchor).
22
+ */
23
+ const wireKeySchema = v4_1.z
24
+ .string()
25
+ .min(1, "wire key must be non-empty")
26
+ .regex(/^[^"\\]+$/, "wire key must not contain a quote or backslash (it anchors a JSON string)");
27
+ /**
28
+ * Validates the shape at the boundary so a malformed preset fails at generate
29
+ * time with a field path, rather than silently recovering no form fields and
30
+ * emitting a plugin that ignores the site's option data.
31
+ */
32
+ const formSchemaSchema = v4_1.z.object({
33
+ fieldIdKey: wireKeySchema,
34
+ fieldNameKeys: v4_1.z.array(wireKeySchema).min(1, "fieldNameKeys must list at least one key"),
35
+ fieldOptionsKey: wireKeySchema,
36
+ optionIdKey: wireKeySchema,
37
+ optionValueKey: wireKeySchema,
38
+ responseValueKey: wireKeySchema,
39
+ responseOptionIdKey: wireKeySchema,
40
+ });
41
+ /**
42
+ * Loads a consumer's form-schema module.
43
+ *
44
+ * Resolution reuses {@link resolvePluginSpecifier}, so `--form-schema` accepts the
45
+ * same specifier forms as `BARNACLE_PLUGINS` and `--vocabulary`. Export resolution
46
+ * mirrors the plugin loader's `m.formSchema ?? m.default ?? m`. The
47
+ * {@link FORM_SCHEMA_NONE} sentinel returns `null` (not an empty struct — see the
48
+ * form-schema module for why), the shape for "this site has no ATS form data."
49
+ *
50
+ * Throws rather than falling back: a form-schema that was asked for and is broken
51
+ * is an error, while asking for nothing is the caller's explicit `none`.
52
+ */
53
+ async function loadReconFormSchema(specifier, baseDir) {
54
+ if (specifier === exports.FORM_SCHEMA_NONE)
55
+ return null;
56
+ const href = (0, discover_1.resolvePluginSpecifier)(specifier, baseDir);
57
+ const mod = await import(href);
58
+ const record = mod;
59
+ const raw = record.formSchema ?? record.default ?? mod;
60
+ const parsed = formSchemaSchema.safeParse(raw);
61
+ if (!parsed.success) {
62
+ throw new Error(`form-schema module ${JSON.stringify(specifier)} does not export a valid ReconFormSchema: ${(0, errors_1.toErrorMessage)(parsed.error)}`);
63
+ }
64
+ return parsed.data;
65
+ }
66
+ //# sourceMappingURL=load-form-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-form-schema.js","sourceRoot":"","sources":["../../src/recon/load-form-schema.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;;;AAEH,+BAA2B;AAE3B,yCAA8C;AAC9C,iDAA4D;AAG5D,oFAAoF;AACvE,QAAA,gBAAgB,GAAG,MAAM,CAAC;AAEvC;;;;;GAKG;AACH,MAAM,aAAa,GAAG,MAAC;KACpB,MAAM,EAAE;KACR,GAAG,CAAC,CAAC,EAAE,4BAA4B,CAAC;KACpC,KAAK,CAAC,WAAW,EAAE,2EAA2E,CAAC,CAAC;AAEnG;;;;GAIG;AACH,MAAM,gBAAgB,GAAG,MAAC,CAAC,MAAM,CAAC;IAChC,UAAU,EAAE,aAAa;IACzB,aAAa,EAAE,MAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,0CAA0C,CAAC;IACxF,eAAe,EAAE,aAAa;IAC9B,WAAW,EAAE,aAAa;IAC1B,cAAc,EAAE,aAAa;IAC7B,gBAAgB,EAAE,aAAa;IAC/B,mBAAmB,EAAE,aAAa;CACnC,CAAC,CAAC;AAEH;;;;;;;;;;;GAWG;AACI,KAAK,8BACV,SAAiB,EACjB,OAAe;IAEf,IAAI,SAAS,KAAK,QAAA,gBAAgB;QAAE,OAAO,IAAI,CAAC;IAEhD,MAAM,IAAI,GAAG,IAAA,iCAAsB,EAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACxD,MAAM,GAAG,GAAY,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,GAA8B,CAAC;IAC9C,MAAM,GAAG,GAAG,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,OAAO,IAAI,GAAG,CAAC;IAEvD,MAAM,MAAM,GAAG,gBAAgB,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IAC/C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CACb,sBAAsB,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,6CAA6C,IAAA,uBAAc,EAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAC3H,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Cookie-jar snapshot capture via CDP `Network.getAllCookies`. `document.cookie`
3
+ * and Stagehand's own cookie helpers can't see HttpOnly cookies, and
4
+ * `Page.getCookies`/`Network.getCookies` only return cookies scoped to the
5
+ * current frame's URLs — a journey that navigated click.appcast.io ->
6
+ * apply.appcast.io would miss the click-domain cookies once on the apply
7
+ * page. `Network.getAllCookies` returns the whole-browser jar regardless of
8
+ * the page's current URL, which is what attribution analysis needs.
9
+ */
10
+ import type { Page } from "@browserbasehq/stagehand";
11
+ import type { CookieJarSnapshot } from "../scripts/recon-shared";
12
+ /**
13
+ * Reads the browser's complete cookie jar and returns it as a labeled
14
+ * snapshot. Never throws — telemetry capture is best-effort, so a failed CDP
15
+ * call yields a snapshot with an `error` field and an empty `cookies` array
16
+ * rather than aborting the recon run.
17
+ */
18
+ export declare function captureCookieJarSnapshot(page: Page, label: string, phase: string, stepIndex: number): Promise<CookieJarSnapshot>;
19
+ //# sourceMappingURL=cookie-jar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookie-jar.d.ts","sourceRoot":"","sources":["../../src/scraper/cookie-jar.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAGrD,OAAO,KAAK,EAAE,iBAAiB,EAAgB,MAAM,wBAAwB,CAAC;AAI9E;;;;;GAKG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,IAAI,EACV,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,iBAAiB,CAAC,CAqB5B"}
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ /**
3
+ * Cookie-jar snapshot capture via CDP `Network.getAllCookies`. `document.cookie`
4
+ * and Stagehand's own cookie helpers can't see HttpOnly cookies, and
5
+ * `Page.getCookies`/`Network.getCookies` only return cookies scoped to the
6
+ * current frame's URLs — a journey that navigated click.appcast.io ->
7
+ * apply.appcast.io would miss the click-domain cookies once on the apply
8
+ * page. `Network.getAllCookies` returns the whole-browser jar regardless of
9
+ * the page's current URL, which is what attribution analysis needs.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.captureCookieJarSnapshot = captureCookieJarSnapshot;
13
+ const errors_1 = require("../lib/errors");
14
+ /**
15
+ * Reads the browser's complete cookie jar and returns it as a labeled
16
+ * snapshot. Never throws — telemetry capture is best-effort, so a failed CDP
17
+ * call yields a snapshot with an `error` field and an empty `cookies` array
18
+ * rather than aborting the recon run.
19
+ */
20
+ async function captureCookieJarSnapshot(page, label, phase, stepIndex) {
21
+ const timestamp = new Date().toISOString();
22
+ try {
23
+ const result = await page.sendCDP("Network.getAllCookies");
24
+ return {
25
+ label,
26
+ phase,
27
+ stepIndex,
28
+ timestamp,
29
+ cookies: result.cookies,
30
+ };
31
+ }
32
+ catch (err) {
33
+ return {
34
+ label,
35
+ phase,
36
+ stepIndex,
37
+ timestamp,
38
+ cookies: [],
39
+ error: (0, errors_1.toErrorMessage)(err),
40
+ };
41
+ }
42
+ }
43
+ //# sourceMappingURL=cookie-jar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookie-jar.js","sourceRoot":"","sources":["../../src/scraper/cookie-jar.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAIH,yCAA8C;AAK9C;;;;;GAKG;AACI,KAAK,mCACV,IAAU,EACV,KAAa,EACb,KAAa,EACb,SAAiB;IAEjB,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAwB,uBAAuB,CAAC,CAAC;QAClF,OAAO;YACL,KAAK;YACL,KAAK;YACL,SAAS;YACT,SAAS;YACT,OAAO,EAAE,MAAM,CAAC,OAAO;SACxB,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,KAAK;YACL,KAAK;YACL,SAAS;YACT,SAAS;YACT,OAAO,EAAE,EAAE;YACX,KAAK,EAAE,IAAA,uBAAc,EAAC,GAAG,CAAC;SAC3B,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -133,25 +133,25 @@ export declare class HttpRateLimitError extends ScraperError {
133
133
  constructor(message?: string);
134
134
  }
135
135
  /**
136
- * Oracle HCM returned a plain-text `ORA_URL_LOCKED` sentinel body, meaning the
137
- * requisition URL has been locked by Oracle after repeated requests. Non-retryable
138
- * and NOT a browser-fallback trigger — the URL is locked at Oracle's end, so
139
- * neither a retry of the identical HTTP request nor a fresh Stagehand browser
140
- * session can succeed. The caller must back off and surface a "retry later"
141
- * state rather than burning a Steel session. Kept distinct from HttpRateLimitError
142
- * so metrics/logs (classifyDispatchError) can tell an Oracle requisition lock
143
- * apart from a self-inflicted 429 rate limit.
136
+ * A backend has locked the requested URL — a terminal "come back later" signal a
137
+ * plugin raises (e.g. via `classifyResponseBody`) when the target refuses the
138
+ * resource after repeated requests. Non-retryable and NOT a browser-fallback
139
+ * trigger the lock is at the target's end, so neither a retry of the identical
140
+ * HTTP request nor a fresh Stagehand browser session can succeed. The caller must
141
+ * back off and surface a "retry later" state rather than burning a Steel session.
142
+ * Kept distinct from HttpRateLimitError so metrics/logs (classifyDispatchError)
143
+ * can tell a requisition lock apart from a self-inflicted 429 rate limit.
144
144
  */
145
145
  export declare class HttpUrlLockedError extends ScraperError {
146
146
  constructor(message?: string);
147
147
  }
148
148
  /**
149
- * Oracle HCM returned a plain-text `ORA_IRC_TOKEN_EXPIRED` sentinel body during
150
- * a burst / rate-limit / token-expiry window. Retryable so pRetry keeps retrying,
151
- * but kept distinct from the generic {@link UnknownScraperError} so the encompass
152
- * http-flow can catch exactly this class and re-mint the AccessCode before
153
- * retrying replaying the same stale token would produce the same sentinel
154
- * indefinitely. Distinct from {@link HttpUrlLockedError} which is terminal.
149
+ * @deprecated Removed in 2.0.0. Oracle-specific and no longer thrown by the
150
+ * engine response-body sentinel detection now lives in the plugin via
151
+ * `HttpClientOptions.classifyResponseBody`. A plugin that needs a retryable
152
+ * token-expiry signal should define its own `ScraperError` subclass (see the
153
+ * encompasshealth plugin). Retained only so existing importers of
154
+ * `@enricai/barnacle/scraper/errors` keep resolving under a minor release.
155
155
  */
156
156
  export declare class OracleTokenExpiredError extends ScraperError {
157
157
  constructor(message?: string);