device-passport 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +110 -2
  3. package/assets/ego-profile-manager.png +0 -0
  4. package/dist/adapters/index.cjs +19 -0
  5. package/dist/adapters/index.d.cts +66 -0
  6. package/dist/adapters/index.d.mts +66 -0
  7. package/dist/adapters/index.d.ts +66 -0
  8. package/dist/adapters/index.mjs +3 -0
  9. package/dist/ego/index.cjs +22 -0
  10. package/dist/ego/index.d.cts +5 -0
  11. package/dist/ego/index.d.mts +5 -0
  12. package/dist/ego/index.d.ts +5 -0
  13. package/dist/ego/index.mjs +8 -0
  14. package/dist/evaluator/index.cjs +140 -0
  15. package/dist/evaluator/index.d.cts +204 -0
  16. package/dist/evaluator/index.d.mts +204 -0
  17. package/dist/evaluator/index.d.ts +204 -0
  18. package/dist/evaluator/index.mjs +119 -0
  19. package/dist/geo/index.cjs +50 -0
  20. package/dist/geo/index.d.cts +213 -0
  21. package/dist/geo/index.d.mts +213 -0
  22. package/dist/geo/index.d.ts +213 -0
  23. package/dist/geo/index.mjs +18 -0
  24. package/dist/index.cjs +110 -3940
  25. package/dist/index.d.cts +45 -1091
  26. package/dist/index.d.mts +45 -1091
  27. package/dist/index.d.ts +45 -1091
  28. package/dist/index.mjs +11 -3849
  29. package/dist/shared/device-passport.BGfRcQNh.mjs +565 -0
  30. package/dist/shared/device-passport.BKtmLzPt.cjs +1266 -0
  31. package/dist/shared/device-passport.BPQj0qdT.mjs +1238 -0
  32. package/dist/shared/device-passport.CDS7xrj8.d.ts +469 -0
  33. package/dist/shared/device-passport.C_F1vBE8.d.cts +79 -0
  34. package/dist/shared/device-passport.C_F1vBE8.d.mts +79 -0
  35. package/dist/shared/device-passport.C_F1vBE8.d.ts +79 -0
  36. package/dist/shared/device-passport.CfLes-lq.d.cts +469 -0
  37. package/dist/shared/device-passport.Cjxlv8xI.cjs +245 -0
  38. package/dist/shared/device-passport.CsOWqPIr.d.cts +77 -0
  39. package/dist/shared/device-passport.CyQtcZeD.mjs +237 -0
  40. package/dist/shared/device-passport.D7pEnyp2.d.ts +77 -0
  41. package/dist/shared/device-passport.D9xW6R0B.cjs +1571 -0
  42. package/dist/shared/device-passport.DirQmSd4.d.cts +47 -0
  43. package/dist/shared/device-passport.DirQmSd4.d.mts +47 -0
  44. package/dist/shared/device-passport.DirQmSd4.d.ts +47 -0
  45. package/dist/shared/device-passport.EK4RX7Xd.cjs +584 -0
  46. package/dist/shared/device-passport.ZBspe4ZW.d.mts +77 -0
  47. package/dist/shared/device-passport.dVEqzSEb.d.mts +469 -0
  48. package/dist/shared/device-passport.gNo82fQ5.mjs +1537 -0
  49. package/dist/shared/device-passport.kekU28UM.mjs +705 -0
  50. package/dist/shared/device-passport.qL4tjwc0.cjs +718 -0
  51. package/dist/storage/index.cjs +10 -0
  52. package/dist/storage/index.d.cts +97 -0
  53. package/dist/storage/index.d.mts +97 -0
  54. package/dist/storage/index.d.ts +97 -0
  55. package/dist/storage/index.mjs +3 -0
  56. package/dist/types/index.cjs +2 -0
  57. package/dist/types/index.d.cts +397 -0
  58. package/dist/types/index.d.mts +397 -0
  59. package/dist/types/index.d.ts +397 -0
  60. package/dist/types/index.mjs +1 -0
  61. package/package.json +32 -2
package/CHANGELOG.md CHANGED
@@ -5,6 +5,39 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.2.0] - 2026-11-14
9
+
10
+ ### Added
11
+ - **ego-lite account profile layer** (`device-passport/ego`): `AccountProfileManager` maps one account (seed) onto a sticky passport, an ego-lite profile binding and a proxy declaration. It lists the profiles ego-lite already has (there is no create-profile API), binds an account to one of them, and produces a complete launch plan — ordered CDP commands, init scripts, canonical headers, the `profileId`, and the proxy declaration. The proxy is modelled as a declared *gap*, not a feature: ego-lite has no browser-level proxy API (CDP has no `Browser.setProxySettings`, `--proxy-server` is a launch flag rather than a CDP command, and the native bindings expose no proxy setter), so a profile reports `ready` only when a proxy is declared and `missing-proxy` otherwise, and every plan repeats `proxy.bound` / `proxy.resolution === 'system-level'` so an unbound proxy can never be mistaken for an effective one. The proxy must be bound outside the browser (OS proxy / TUN / local forwarder). The passports are persisted with the same durability as the account records, so a restart cannot silently regenerate a persona with a different geo context while keeping the same passport id.
12
+ - **`passportStore` option on `AccountProfileManager`**: account records and personas are two different kinds of state. Persisting only the records left the persona living in memory, so a restart regenerated it from the default geo — the identity drifted while its `id` stayed the same and no caller could tell. The option makes the persona survive a restart for real, and `createSessionPlan()` now aligns the persona's geo context with the declared proxy country so the browser's claimed country and the traffic's exit country cannot diverge.
13
+ - **`EgoSessionPlan` now extends `CdpPassportPlan`**: re-declaring `commands` / `initScripts` / `headers` let the two shapes drift apart and silently dropped `stealthScript` from the plan. The plan also carries its `sessionId` on the init script itself, so a caller that sends `plan.initScripts` directly cannot drop it.
14
+ - **Single source for the profile id**: the session plan takes the profile id from the persisted account record only. Accepting a caller-supplied profile id as well let the plan's profile id and its readiness disagree, and a session could be launched under a profile that was never validated.
15
+
16
+ ## [1.1.0] - 2026-11-04
17
+
18
+
19
+ ### Added
20
+ - **Pixel-level protection**: `getStealthScript()` now appends a deterministic noise layer that patches `canvas.toDataURL()`, `getImageData()`, `gl.readPixels()` and the audio analyser, so read-back no longer exposes the host GPU or a shared hash across personas. The noise is derived from the passport seed — stable per account, distinct per account — and uses discrete quantization levels rather than per-pixel dithering, because a smooth ±1 RGB jitter is itself a fingerprint.
21
+ - **Raw CDP adapter**: `getCdpPlan()` and `applyCdpPassport()` emit an ordered, transport-agnostic Chrome DevTools Protocol command plan for tools that speak bare CDP (e.g. `lite-browser`'s `cdp` channel). Covers headers, `Emulation.setUserAgentOverride` with Client-Hints metadata, timezone, locale, device metrics, touch emulation and init-script installation.
22
+
23
+ ### Fixed
24
+ - **Type-check baseline was red**: `bun run lint` failed on a clean checkout — `toGeoLocation()` dereferenced the optional `GeoContext.coordinates`, and `createPassport()` called the synchronous storage helpers that the `StorageAdapter` interface never declared. The sync capability is now declared on the interface as optional, and `toGeoLocation()` falls back to the country's representative coordinates so an emulated geolocation can never contradict the country/timezone pairing.
25
+ - **`tsconfig.json` rejected by modern TypeScript**: the deprecated `baseUrl` option (unused — the `@/*` alias has no importers) broke `tsc --noEmit` on TypeScript 6, and node globals were unresolvable because `types` was never declared. Both are fixed, so the lint gate passes on TypeScript 5 and 6 alike.
26
+ - **Canvas serialization recursion**: the temporary canvas used for re-serialization shares the patched prototype, so calling its `toDataURL` recursed until the stack limit. Serialization now uses the captured original method (verified with 25 repeated calls in real Chromium).
27
+ - **WebGL canvases leaked the real framebuffer through `toDataURL()`**: a canvas with a WebGL context returns `null` from `getContext('2d')`, so that path silently skipped protection. Pixel perturbation now runs through `gl.readPixels` for such canvases.
28
+ - **Alpha bytes were perturbed**: RGBA alpha now stays untouched, so transparent and opaque pixels keep their semantics (masks and hit-testing unaffected).
29
+ - **Audio perturbation was seed-independent**: a fixed `1e-7` step was identical for every persona and rounded away in Float32 at typical dB values. The step is now seed-derived and representable.
30
+ - **Injected stealth script was invalid JavaScript**: the runtime script embedded TypeScript-only syntax (`navigator as any`), which raised a `SyntaxError` in the page and silently disabled the **entire** stealth layer — `navigator.webdriver` stayed `true` and every spoofed API reverted to the real values. The script is now valid browser JavaScript, and a regression test parses it to prevent this from returning.
31
+ - **CDP init scripts never executed**: `Page.addScriptToEvaluateOnNewDocument` was issued without `Page.enable`. Chrome accepted the script, returned an identifier and then never ran it, so a CDP-driven persona looked like a plain automation session. `Page.enable` is now emitted first, with a regression test covering the ordering.
32
+
33
+ ## [1.0.1] - 2026-10-17
34
+
35
+ ### Added
36
+ - **Standard ESM subpath exports**: `device-passport/geo`, `device-passport/storage`, `device-passport/adapters` are now importable as separate entry points, each shipping its own `types` / `import` / `require` conditions. Consumers can tree-shake by pulling only the module they need instead of the whole bundle.
37
+
38
+ ### Fixed
39
+ - **npm publish path**: the release workflow now runs on Node 24 with pure OIDC Trusted Publishing (the previous `NODE_AUTH_TOKEN` pointed at a secret that was never configured, which forced npm down the token-auth path and made Trusted Publishing ineffective).
40
+
8
41
  ## [1.0.0] - 2026-10-17
9
42
 
10
43
  ### Added
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="assets/banner.svg" alt="device-passport banner" width="800"/>
2
+ <img src="https://cdn.jsdelivr.net/gh/webkubor/free-imgbed@main/device-passport/banner.svg" alt="device-passport banner" width="800"/>
3
3
  </p>
4
4
 
5
5
  <h1 align="center">🛂 device-passport</h1>
@@ -59,7 +59,7 @@ Modern anti-bot classifiers (Cloudflare Turnstile, DataDome, Akamai, GitHub Anti
59
59
  Under-parameterized headers ($H(X) < 4.6$ bits/char) scream "naive scraper"; chaotic randomized noise ($H(X) > 5.6$ bits/char) screams "evasion bot". `device-passport` operates strictly within the natural human entropy equilibrium ($5.24$ bits/char).
60
60
 
61
61
  <p align="center">
62
- <img src="assets/architecture.svg" alt="device-passport formal state-space architecture" width="100%"/>
62
+ <img src="https://cdn.jsdelivr.net/gh/webkubor/free-imgbed@main/device-passport/architecture.svg" alt="device-passport formal state-space architecture" width="100%"/>
63
63
  </p>
64
64
 
65
65
  ---
@@ -292,6 +292,60 @@ await passport.injectToPuppeteer(page);
292
292
  await page.goto('https://bot.sannysoft.com');
293
293
  ```
294
294
 
295
+ ### Raw CDP Integration (lite-browser, custom transports)
296
+
297
+ Tools that drive Chrome over a bare DevTools Protocol socket cannot use the Playwright or
298
+ Puppeteer adapters, because those return framework-shaped options. `getCdpPlan()` returns
299
+ an ordered, transport-agnostic command plan instead:
300
+
301
+ ```typescript
302
+ import { createPassport, applyCdpPassport } from 'device-passport';
303
+
304
+ const passport = createPassport({ seed: 'worker_de_19', geo: 'DE', os: 'windows' });
305
+
306
+ // `send` is yours: a WebSocket, stdio, or an in-process socket.
307
+ await applyCdpPassport(passport, (cmd) => transport.send(cmd.method, cmd.params));
308
+ ```
309
+
310
+ The plan covers `Page.enable`, extra HTTP headers, `Emulation.setUserAgentOverride`
311
+ (with Client-Hints metadata), timezone, locale, device metrics and touch emulation, and
312
+ installs the stealth script through `Page.addScriptToEvaluateOnNewDocument`.
313
+
314
+ > **`Page.enable` is mandatory.** Without it Chrome accepts the init script, returns an
315
+ > identifier, and then never runs it — the persona stays a plain automation session.
316
+ > `applyCdpPassport` emits it first for that reason.
317
+
318
+ ---
319
+
320
+ ## Pixel-Level Protection
321
+
322
+ API-level spoofing is not sufficient on its own. `canvas.toDataURL()` and
323
+ `gl.readPixels()` hand back the **real** framebuffer, so a headless browser exposes
324
+ SwiftShader while its User-Agent claims a discrete GPU — and every persona rendered on
325
+ the same machine produces a **byte-identical** canvas hash, which makes a fleet easy to
326
+ cluster.
327
+
328
+ `getStealthScript()` now appends a deterministic pixel-protection layer that patches
329
+ `toDataURL`, `getImageData`, `readPixels` and the audio analyser:
330
+
331
+ ```typescript
332
+ import { createPassport, getStealthScript } from 'device-passport';
333
+
334
+ const passport = createPassport('alice');
335
+ const script = getStealthScript(passport);
336
+ // Use with Playwright addInitScript, Puppeteer evaluateOnNewDocument, or CDP.
337
+ ```
338
+
339
+ Properties:
340
+
341
+ - **Deterministic per seed** — the same account reads back identical pixels forever,
342
+ which is what makes "sticky hardware" true beyond the API layer.
343
+ - **Distinct per account** — different seeds produce different noise tables.
344
+ - **Plausible** — a discrete quantization of a few levels, not per-pixel dithering. Real
345
+ GPUs quantize; a smooth ±1 RGB jitter on every pixel is itself a fingerprint.
346
+ - **Self-contained** — dependency-free IIFE, safe to inject repeatedly (re-injection is
347
+ a no-op).
348
+
295
349
  ---
296
350
 
297
351
  ## Multi-Account Fleet Management (`PassportManager`)
@@ -323,6 +377,47 @@ const fleetSnapshot = await manager.exportAll();
323
377
 
324
378
  ---
325
379
 
380
+ ## ego-lite Account Profiles (`AccountProfileManager`)
381
+
382
+ ![device-passport ego-lite account profile manager — real run output](assets/ego-profile-manager.png)
383
+
384
+ When the browser is [ego-lite](https://github.com/citrolabs/ego-lite), one account maps to three things that must travel together: a sticky passport, an ego-lite **profile** binding (cookies and login state persist per profile), and a **proxy declaration**.
385
+
386
+ ```typescript
387
+ import { AccountProfileManager } from 'device-passport/ego';
388
+
389
+ const manager = new AccountProfileManager({
390
+ store: './data/accounts.json', // atomic JSON persistence
391
+ // bridge: egoLiteBridge, // optional; inject ego-lite's native bridge
392
+ });
393
+
394
+ // List the profiles ego-lite already has — there is no create-profile API.
395
+ const profiles = await manager.listEgoProfiles(); // [{ id, name, isDefault }]
396
+
397
+ // Bind one account (seed) to one profile and declare its proxy.
398
+ await manager.bindProfile('account_9041', {
399
+ profileId: 'Profile 2',
400
+ proxy: { url: 'http://user-jp:pass@gate.example.com:7000', description: 'Tokyo residential exit', country: 'JP' },
401
+ });
402
+
403
+ // The complete launch plan: CDP commands, init scripts, headers, profileId, proxy declaration.
404
+ const plan = await manager.createSessionPlan('account_9041');
405
+ plan.readiness; // 'ready' | 'missing-proxy' | 'missing-profile'
406
+ plan.launchable; // true only when a proxy is declared
407
+ ```
408
+
409
+ ### ⚠️ The proxy gap, stated plainly
410
+
411
+ **ego-lite cannot configure a proxy at the browser layer, and neither can this library.**
412
+
413
+ - CDP has no `Browser.setProxySettings` — Chromium has never shipped one.
414
+ - `--proxy-server` is a browser **launch** flag on the process, not a CDP command; it cannot be changed after the profile is running.
415
+ - ego-lite's native bindings expose no proxy setter either.
416
+
417
+ A proxy is therefore bound **outside the browser**: at the OS level (system proxy / TUN / transparent redirect), or by routing the ego-lite process through a local forwarder that owns the upstream proxy. This layer refuses to fake it: an account profile is `ready` only when a proxy is **declared**, `missing-proxy` otherwise, and every session plan repeats that state (`plan.proxy.bound`, `plan.proxy.resolution === 'system-level'`) so an unbound proxy can never be mistaken for an effective one.
418
+
419
+ ---
420
+
326
421
  ## Self-Diagnostic Coherence Evaluation
327
422
 
328
423
  Verify any passport or raw external request headers against the theoretical coherence framework:
@@ -411,6 +506,19 @@ npx device-passport inspect --seed production_user_102
411
506
  │ Native Fetch │ │ Playwright │ │ Puppeteer │
412
507
  │ Headers │ │Context Options│ │ Emulation Hook│
413
508
  └──────────────┘ └──────────────┘ └──────────────┘
509
+ │ │
510
+ └───────────────┬───────────────┘
511
+ ▼
512
+ ┌───────────────────────────┐
513
+ │ Pixel-Level Protection │
514
+ │ canvas / WebGL / audio │
515
+ │ deterministic per seed │
516
+ └───────────┬───────────────┘
517
+ ▼
518
+ ┌───────────────────────────┐
519
+ │ Raw CDP Command Plan │
520
+ │ (lite-browser, custom) │
521
+ └───────────────────────────┘
414
522
  ```
415
523
 
416
524
  ---
Binary file
@@ -0,0 +1,19 @@
1
+ 'use strict';
2
+
3
+ const cdp = require('../shared/device-passport.D9xW6R0B.cjs');
4
+ require('node:crypto');
5
+ require('../shared/device-passport.BKtmLzPt.cjs');
6
+
7
+
8
+
9
+ exports.CANONICAL_HEADER_ORDER = cdp.CANONICAL_HEADER_ORDER;
10
+ exports.applyCdpPassport = cdp.applyCdpPassport;
11
+ exports.applyToPlaywrightContext = cdp.applyToPlaywrightContext;
12
+ exports.applyToPuppeteerPage = cdp.applyToPuppeteerPage;
13
+ exports.formatCanonicalHeaders = cdp.formatCanonicalHeaders;
14
+ exports.getCdpPlan = cdp.getCdpPlan;
15
+ exports.getPlaywrightContext = cdp.getPlaywrightContext;
16
+ exports.getPuppeteerArgs = cdp.getPuppeteerArgs;
17
+ exports.getStealthScript = cdp.getStealthScript;
18
+ exports.toCurl = cdp.toCurl;
19
+ exports.withPassport = cdp.withPassport;
@@ -0,0 +1,66 @@
1
+ import { DevicePassport, CdpCommand, CdpPassportPlan } from '../types/index.cjs';
2
+ export { P as PlaywrightContextOptions, a as PuppeteerEmulationOptions, b as applyToPlaywrightContext, c as applyToPuppeteerPage, g as getPlaywrightContext, d as getPuppeteerArgs, e as getStealthScript } from '../shared/device-passport.CsOWqPIr.cjs';
3
+ import '../shared/device-passport.C_F1vBE8.cjs';
4
+
5
+ /**
6
+ * Standard Chromium browser header ordering.
7
+ * Anti-bot systems (Cloudflare, Akamai, DataDome, Kasada) strictly inspect
8
+ * the sequence of HTTP request headers. A mismatch in header order triggers
9
+ * immediate bot heuristics.
10
+ */
11
+ declare const CANONICAL_HEADER_ORDER: readonly string[];
12
+ /**
13
+ * Sorts and formats HTTP headers according to canonical browser ordering.
14
+ * Merges base headers with optional overrides.
15
+ */
16
+ declare function formatCanonicalHeaders(baseHeaders: Record<string, string>, overrides?: Record<string, string>): Record<string, string>;
17
+ interface PassportLike {
18
+ headers: Record<string, string>;
19
+ getHeaders?: (overrides?: Record<string, string>) => Record<string, string>;
20
+ }
21
+ /**
22
+ * Higher-order fetch function wrapping standard fetch with canonical DevicePassport headers.
23
+ * Ensures consistent TLS/HTTP persona across API calls, scraping requests, and webhooks.
24
+ *
25
+ * @param passport DevicePassport instance or object
26
+ * @param customFetch Optional fetch implementation (e.g. node-fetch, undici, or global fetch)
27
+ */
28
+ declare function withPassport(passport: PassportLike | DevicePassport, customFetch?: typeof fetch): typeof fetch;
29
+ /**
30
+ * Generates an executable cURL command with correct browser headers.
31
+ */
32
+ declare function toCurl(passport: PassportLike | DevicePassport, url: string, options?: {
33
+ method?: string;
34
+ data?: any;
35
+ headers?: Record<string, string>;
36
+ }): string;
37
+
38
+ /**
39
+ * Raw Chrome DevTools Protocol adapter.
40
+ *
41
+ * 🔴 Why this exists (2026-11-04):
42
+ * Tools that drive Chrome over a bare CDP socket — `lite-browser`'s `cdp <method> [json]`
43
+ * channel, for example — cannot use the Playwright/Puppeteer adapters, because those
44
+ * hand back framework-shaped options. This adapter speaks plain CDP so any CDP client
45
+ * can apply a passport.
46
+ *
47
+ * The returned plan is deliberately **declarative**: a `send(method, params)` function
48
+ * applies every command in order. The caller owns the transport, so this module has no
49
+ * dependency and works over WebSocket, stdio, or an in-process socket.
50
+ */
51
+ /**
52
+ * Builds the ordered CDP command plan for a passport.
53
+ *
54
+ * Order matters: the UA override must precede navigation, and the init script has to be
55
+ * installed before the first document is created.
56
+ */
57
+ declare function getCdpPlan(passport: DevicePassport, sessionId?: string): CdpPassportPlan;
58
+ /**
59
+ * Applies a passport through a raw CDP transport.
60
+ *
61
+ * @param send Function that sends one CDP command and resolves with its result.
62
+ */
63
+ declare function applyCdpPassport(passport: DevicePassport, send: (command: CdpCommand) => Promise<unknown>, sessionId?: string): Promise<void>;
64
+
65
+ export { CANONICAL_HEADER_ORDER, applyCdpPassport, formatCanonicalHeaders, getCdpPlan, toCurl, withPassport };
66
+ export type { PassportLike };
@@ -0,0 +1,66 @@
1
+ import { DevicePassport, CdpCommand, CdpPassportPlan } from '../types/index.mjs';
2
+ export { P as PlaywrightContextOptions, a as PuppeteerEmulationOptions, b as applyToPlaywrightContext, c as applyToPuppeteerPage, g as getPlaywrightContext, d as getPuppeteerArgs, e as getStealthScript } from '../shared/device-passport.ZBspe4ZW.mjs';
3
+ import '../shared/device-passport.C_F1vBE8.mjs';
4
+
5
+ /**
6
+ * Standard Chromium browser header ordering.
7
+ * Anti-bot systems (Cloudflare, Akamai, DataDome, Kasada) strictly inspect
8
+ * the sequence of HTTP request headers. A mismatch in header order triggers
9
+ * immediate bot heuristics.
10
+ */
11
+ declare const CANONICAL_HEADER_ORDER: readonly string[];
12
+ /**
13
+ * Sorts and formats HTTP headers according to canonical browser ordering.
14
+ * Merges base headers with optional overrides.
15
+ */
16
+ declare function formatCanonicalHeaders(baseHeaders: Record<string, string>, overrides?: Record<string, string>): Record<string, string>;
17
+ interface PassportLike {
18
+ headers: Record<string, string>;
19
+ getHeaders?: (overrides?: Record<string, string>) => Record<string, string>;
20
+ }
21
+ /**
22
+ * Higher-order fetch function wrapping standard fetch with canonical DevicePassport headers.
23
+ * Ensures consistent TLS/HTTP persona across API calls, scraping requests, and webhooks.
24
+ *
25
+ * @param passport DevicePassport instance or object
26
+ * @param customFetch Optional fetch implementation (e.g. node-fetch, undici, or global fetch)
27
+ */
28
+ declare function withPassport(passport: PassportLike | DevicePassport, customFetch?: typeof fetch): typeof fetch;
29
+ /**
30
+ * Generates an executable cURL command with correct browser headers.
31
+ */
32
+ declare function toCurl(passport: PassportLike | DevicePassport, url: string, options?: {
33
+ method?: string;
34
+ data?: any;
35
+ headers?: Record<string, string>;
36
+ }): string;
37
+
38
+ /**
39
+ * Raw Chrome DevTools Protocol adapter.
40
+ *
41
+ * 🔴 Why this exists (2026-11-04):
42
+ * Tools that drive Chrome over a bare CDP socket — `lite-browser`'s `cdp <method> [json]`
43
+ * channel, for example — cannot use the Playwright/Puppeteer adapters, because those
44
+ * hand back framework-shaped options. This adapter speaks plain CDP so any CDP client
45
+ * can apply a passport.
46
+ *
47
+ * The returned plan is deliberately **declarative**: a `send(method, params)` function
48
+ * applies every command in order. The caller owns the transport, so this module has no
49
+ * dependency and works over WebSocket, stdio, or an in-process socket.
50
+ */
51
+ /**
52
+ * Builds the ordered CDP command plan for a passport.
53
+ *
54
+ * Order matters: the UA override must precede navigation, and the init script has to be
55
+ * installed before the first document is created.
56
+ */
57
+ declare function getCdpPlan(passport: DevicePassport, sessionId?: string): CdpPassportPlan;
58
+ /**
59
+ * Applies a passport through a raw CDP transport.
60
+ *
61
+ * @param send Function that sends one CDP command and resolves with its result.
62
+ */
63
+ declare function applyCdpPassport(passport: DevicePassport, send: (command: CdpCommand) => Promise<unknown>, sessionId?: string): Promise<void>;
64
+
65
+ export { CANONICAL_HEADER_ORDER, applyCdpPassport, formatCanonicalHeaders, getCdpPlan, toCurl, withPassport };
66
+ export type { PassportLike };
@@ -0,0 +1,66 @@
1
+ import { DevicePassport, CdpCommand, CdpPassportPlan } from '../types/index.js';
2
+ export { P as PlaywrightContextOptions, a as PuppeteerEmulationOptions, b as applyToPlaywrightContext, c as applyToPuppeteerPage, g as getPlaywrightContext, d as getPuppeteerArgs, e as getStealthScript } from '../shared/device-passport.D7pEnyp2.js';
3
+ import '../shared/device-passport.C_F1vBE8.js';
4
+
5
+ /**
6
+ * Standard Chromium browser header ordering.
7
+ * Anti-bot systems (Cloudflare, Akamai, DataDome, Kasada) strictly inspect
8
+ * the sequence of HTTP request headers. A mismatch in header order triggers
9
+ * immediate bot heuristics.
10
+ */
11
+ declare const CANONICAL_HEADER_ORDER: readonly string[];
12
+ /**
13
+ * Sorts and formats HTTP headers according to canonical browser ordering.
14
+ * Merges base headers with optional overrides.
15
+ */
16
+ declare function formatCanonicalHeaders(baseHeaders: Record<string, string>, overrides?: Record<string, string>): Record<string, string>;
17
+ interface PassportLike {
18
+ headers: Record<string, string>;
19
+ getHeaders?: (overrides?: Record<string, string>) => Record<string, string>;
20
+ }
21
+ /**
22
+ * Higher-order fetch function wrapping standard fetch with canonical DevicePassport headers.
23
+ * Ensures consistent TLS/HTTP persona across API calls, scraping requests, and webhooks.
24
+ *
25
+ * @param passport DevicePassport instance or object
26
+ * @param customFetch Optional fetch implementation (e.g. node-fetch, undici, or global fetch)
27
+ */
28
+ declare function withPassport(passport: PassportLike | DevicePassport, customFetch?: typeof fetch): typeof fetch;
29
+ /**
30
+ * Generates an executable cURL command with correct browser headers.
31
+ */
32
+ declare function toCurl(passport: PassportLike | DevicePassport, url: string, options?: {
33
+ method?: string;
34
+ data?: any;
35
+ headers?: Record<string, string>;
36
+ }): string;
37
+
38
+ /**
39
+ * Raw Chrome DevTools Protocol adapter.
40
+ *
41
+ * 🔴 Why this exists (2026-11-04):
42
+ * Tools that drive Chrome over a bare CDP socket — `lite-browser`'s `cdp <method> [json]`
43
+ * channel, for example — cannot use the Playwright/Puppeteer adapters, because those
44
+ * hand back framework-shaped options. This adapter speaks plain CDP so any CDP client
45
+ * can apply a passport.
46
+ *
47
+ * The returned plan is deliberately **declarative**: a `send(method, params)` function
48
+ * applies every command in order. The caller owns the transport, so this module has no
49
+ * dependency and works over WebSocket, stdio, or an in-process socket.
50
+ */
51
+ /**
52
+ * Builds the ordered CDP command plan for a passport.
53
+ *
54
+ * Order matters: the UA override must precede navigation, and the init script has to be
55
+ * installed before the first document is created.
56
+ */
57
+ declare function getCdpPlan(passport: DevicePassport, sessionId?: string): CdpPassportPlan;
58
+ /**
59
+ * Applies a passport through a raw CDP transport.
60
+ *
61
+ * @param send Function that sends one CDP command and resolves with its result.
62
+ */
63
+ declare function applyCdpPassport(passport: DevicePassport, send: (command: CdpCommand) => Promise<unknown>, sessionId?: string): Promise<void>;
64
+
65
+ export { CANONICAL_HEADER_ORDER, applyCdpPassport, formatCanonicalHeaders, getCdpPlan, toCurl, withPassport };
66
+ export type { PassportLike };
@@ -0,0 +1,3 @@
1
+ export { C as CANONICAL_HEADER_ORDER, a as applyCdpPassport, b as applyToPlaywrightContext, c as applyToPuppeteerPage, f as formatCanonicalHeaders, g as getCdpPlan, d as getPlaywrightContext, e as getPuppeteerArgs, h as getStealthScript, t as toCurl, w as withPassport } from '../shared/device-passport.gNo82fQ5.mjs';
2
+ import 'node:crypto';
3
+ import '../shared/device-passport.BPQj0qdT.mjs';
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ const bridge = require('../shared/device-passport.qL4tjwc0.cjs');
4
+ require('../shared/device-passport.D9xW6R0B.cjs');
5
+ require('node:crypto');
6
+ require('../shared/device-passport.BKtmLzPt.cjs');
7
+ require('../shared/device-passport.EK4RX7Xd.cjs');
8
+ require('../shared/device-passport.Cjxlv8xI.cjs');
9
+ require('node:fs');
10
+ require('node:path');
11
+
12
+
13
+
14
+ exports.AccountProfileManager = bridge.AccountProfileManager;
15
+ exports.PROXY_MISSING_NOTE = bridge.PROXY_MISSING_NOTE;
16
+ exports.PROXY_RESOLUTION_NOTE = bridge.PROXY_RESOLUTION_NOTE;
17
+ exports.buildEgoSessionPlan = bridge.buildEgoSessionPlan;
18
+ exports.buildProxyDeclaration = bridge.buildProxyDeclaration;
19
+ exports.createEgoBridge = bridge.createEgoBridge;
20
+ exports.defaultTaskSpaceName = bridge.defaultTaskSpaceName;
21
+ exports.getEgoNativeBridge = bridge.getEgoNativeBridge;
22
+ exports.getProfileReadiness = bridge.getProfileReadiness;
@@ -0,0 +1,5 @@
1
+ export { A as AccountProfileManager, a as AccountProfileManagerOptions, B as BindProfileOptions, b as BuildEgoSessionPlanOptions, c as CreateSessionPlanOptions, E as EgoNativeBridge, P as PROXY_MISSING_NOTE, d as PROXY_RESOLUTION_NOTE, g as buildEgoSessionPlan, h as buildProxyDeclaration, i as createEgoBridge, k as defaultTaskSpaceName, l as getEgoNativeBridge, m as getProfileReadiness } from '../shared/device-passport.CfLes-lq.cjs';
2
+ import '../types/index.cjs';
3
+ import '../shared/device-passport.C_F1vBE8.cjs';
4
+ import '../shared/device-passport.CsOWqPIr.cjs';
5
+ import '../shared/device-passport.DirQmSd4.cjs';
@@ -0,0 +1,5 @@
1
+ export { A as AccountProfileManager, a as AccountProfileManagerOptions, B as BindProfileOptions, b as BuildEgoSessionPlanOptions, c as CreateSessionPlanOptions, E as EgoNativeBridge, P as PROXY_MISSING_NOTE, d as PROXY_RESOLUTION_NOTE, g as buildEgoSessionPlan, h as buildProxyDeclaration, i as createEgoBridge, k as defaultTaskSpaceName, l as getEgoNativeBridge, m as getProfileReadiness } from '../shared/device-passport.dVEqzSEb.mjs';
2
+ import '../types/index.mjs';
3
+ import '../shared/device-passport.C_F1vBE8.mjs';
4
+ import '../shared/device-passport.ZBspe4ZW.mjs';
5
+ import '../shared/device-passport.DirQmSd4.mjs';
@@ -0,0 +1,5 @@
1
+ export { A as AccountProfileManager, a as AccountProfileManagerOptions, B as BindProfileOptions, b as BuildEgoSessionPlanOptions, c as CreateSessionPlanOptions, E as EgoNativeBridge, P as PROXY_MISSING_NOTE, d as PROXY_RESOLUTION_NOTE, g as buildEgoSessionPlan, h as buildProxyDeclaration, i as createEgoBridge, k as defaultTaskSpaceName, l as getEgoNativeBridge, m as getProfileReadiness } from '../shared/device-passport.CDS7xrj8.js';
2
+ import '../types/index.js';
3
+ import '../shared/device-passport.C_F1vBE8.js';
4
+ import '../shared/device-passport.D7pEnyp2.js';
5
+ import '../shared/device-passport.DirQmSd4.js';
@@ -0,0 +1,8 @@
1
+ export { A as AccountProfileManager, P as PROXY_MISSING_NOTE, a as PROXY_RESOLUTION_NOTE, b as buildEgoSessionPlan, c as buildProxyDeclaration, d as createEgoBridge, e as defaultTaskSpaceName, g as getEgoNativeBridge, f as getProfileReadiness } from '../shared/device-passport.kekU28UM.mjs';
2
+ import '../shared/device-passport.gNo82fQ5.mjs';
3
+ import 'node:crypto';
4
+ import '../shared/device-passport.BPQj0qdT.mjs';
5
+ import '../shared/device-passport.BGfRcQNh.mjs';
6
+ import '../shared/device-passport.CyQtcZeD.mjs';
7
+ import 'node:fs';
8
+ import 'node:path';
@@ -0,0 +1,140 @@
1
+ 'use strict';
2
+
3
+ const entropy = require('../shared/device-passport.EK4RX7Xd.cjs');
4
+
5
+ function evaluateGeoAlignment(passport) {
6
+ const geoContradictions = entropy.detectGeoChronoContradictions(passport);
7
+ let score = 100;
8
+ for (const c of geoContradictions) {
9
+ score -= c.impactPenalty;
10
+ }
11
+ const acceptLang = passport.headers["accept-language"] || "";
12
+ if (acceptLang) {
13
+ if (!/^[a-zA-Z]{2,3}(-[a-zA-Z0-9]+)?(;q=[0-1](\.\d{1,3})?)?/.test(acceptLang.trim())) {
14
+ score -= 10;
15
+ }
16
+ } else {
17
+ score -= 15;
18
+ }
19
+ return Math.max(0, Math.min(100, score));
20
+ }
21
+ function evaluateCoherence(passport) {
22
+ const contradictions = entropy.detectAllContradictions(passport);
23
+ const geoAlignmentScore = evaluateGeoAlignment(passport);
24
+ const entropyResult = entropy.analyzeHeaderEntropy(passport.headers || {});
25
+ const entropyScore = entropyResult.entropyScore;
26
+ let totalPenalty = 0;
27
+ let fatalCount = 0;
28
+ let warningCount = 0;
29
+ for (const item of contradictions) {
30
+ totalPenalty += item.impactPenalty;
31
+ if (item.severity === "fatal") {
32
+ fatalCount++;
33
+ } else {
34
+ warningCount++;
35
+ }
36
+ }
37
+ if (entropyResult.anomaly !== "optimal") {
38
+ const entropyPenalty = Math.max(0, 100 - entropyScore) * 0.25;
39
+ totalPenalty += entropyPenalty;
40
+ }
41
+ const rawCoherence = 100 - totalPenalty;
42
+ const coherenceScore = Number(Math.max(0, Math.min(100, rawCoherence)).toFixed(1));
43
+ const hContradictions = contradictions.filter((c) => c.category === "hardware_system");
44
+ const sContradictions = contradictions.filter((c) => c.category === "os_browser");
45
+ const gContradictions = contradictions.filter((c) => c.category === "geo_chrono");
46
+ const cContradictions = contradictions.filter((c) => c.category === "client_hints");
47
+ const calcSubScore = (list) => Math.max(0, 100 - list.reduce((acc, curr) => acc + curr.impactPenalty, 0));
48
+ const mathematicalModel = {
49
+ formalism: "P = \u27E8H, S, G, C\u27E9",
50
+ definition: {
51
+ H: "Hardware Sub-space \u27E8Screen, CPU, Memory, GPU\u27E9",
52
+ S: "System Sub-space \u27E8OS, Browser, UserAgent\u27E9",
53
+ G: "Geo-Chrono Sub-space \u27E8Country, Timezone, Locale, Languages, Coordinates\u27E9",
54
+ C: "Client-Hints Sub-space \u27E8Sec-CH-UA*, Low & High Entropy Hints\u27E9"
55
+ },
56
+ subspaces: {
57
+ H: {
58
+ valid: hContradictions.every((c) => c.severity !== "fatal"),
59
+ score: calcSubScore(hContradictions),
60
+ notes: hContradictions.map((c) => c.description)
61
+ },
62
+ S: {
63
+ valid: sContradictions.every((c) => c.severity !== "fatal"),
64
+ score: calcSubScore(sContradictions),
65
+ notes: sContradictions.map((c) => c.description)
66
+ },
67
+ G: {
68
+ valid: gContradictions.every((c) => c.severity !== "fatal"),
69
+ score: geoAlignmentScore,
70
+ notes: gContradictions.map((c) => c.description)
71
+ },
72
+ C: {
73
+ valid: cContradictions.every((c) => c.severity !== "fatal"),
74
+ score: calcSubScore(cContradictions),
75
+ notes: cContradictions.map((c) => c.description)
76
+ }
77
+ }
78
+ };
79
+ let grade = "F";
80
+ if (fatalCount === 0) {
81
+ if (coherenceScore >= 95 && entropyScore >= 80) grade = "A+";
82
+ else if (coherenceScore >= 88) grade = "A";
83
+ else if (coherenceScore >= 75) grade = "B";
84
+ else grade = "C";
85
+ } else {
86
+ grade = "F";
87
+ }
88
+ const passed = fatalCount === 0 && coherenceScore >= 80;
89
+ let verdict = "";
90
+ if (grade === "A+") {
91
+ verdict = "Flawless mathematical coherence. Zero contradictions across H, S, G, C spaces.";
92
+ } else if (grade === "A") {
93
+ verdict = "High consistency persona with natural browser characteristics and low bot detection risk.";
94
+ } else if (grade === "B" || grade === "C") {
95
+ verdict = "Minor cross-layer discrepancies detected; susceptible to advanced anti-bot heuristics.";
96
+ } else {
97
+ verdict = "Fatal architectural contradiction detected. Persona is trivially flagged by Cloudflare/DataDome.";
98
+ }
99
+ return {
100
+ coherenceScore,
101
+ geoAlignmentScore,
102
+ entropyScore,
103
+ contradictions,
104
+ mathematicalModel,
105
+ entropyAnalysis: entropyResult,
106
+ summary: {
107
+ passed,
108
+ fatalCount,
109
+ warningCount,
110
+ grade,
111
+ verdict
112
+ }
113
+ };
114
+ }
115
+
116
+ function evaluatePassport(passport) {
117
+ return evaluateCoherence(passport);
118
+ }
119
+
120
+ exports.BASELINE_OPTIMAL_ENTROPY = entropy.BASELINE_OPTIMAL_ENTROPY;
121
+ exports.BROWSER_BASELINE_FREQUENCIES = entropy.BROWSER_BASELINE_FREQUENCIES;
122
+ exports.COUNTRY_PRIMARY_LANGUAGES = entropy.COUNTRY_PRIMARY_LANGUAGES;
123
+ exports.COUNTRY_TIMEZONE_MAP = entropy.COUNTRY_TIMEZONE_MAP;
124
+ exports.MIN_STRUCTURAL_ENTROPY = entropy.MIN_STRUCTURAL_ENTROPY;
125
+ exports.OPTIMAL_CHAR_ENTROPY_MAX = entropy.OPTIMAL_CHAR_ENTROPY_MAX;
126
+ exports.OPTIMAL_CHAR_ENTROPY_MIN = entropy.OPTIMAL_CHAR_ENTROPY_MIN;
127
+ exports.OPTIMAL_ENTROPY_MAX = entropy.OPTIMAL_ENTROPY_MAX;
128
+ exports.OPTIMAL_ENTROPY_MIN = entropy.OPTIMAL_ENTROPY_MIN;
129
+ exports.analyzeHeaderEntropy = entropy.analyzeHeaderEntropy;
130
+ exports.calculateKLDivergence = entropy.calculateKLDivergence;
131
+ exports.calculateShannonEntropy = entropy.calculateShannonEntropy;
132
+ exports.calculateStructuralEntropy = entropy.calculateStructuralEntropy;
133
+ exports.detectAllContradictions = entropy.detectAllContradictions;
134
+ exports.detectClientHintsContradictions = entropy.detectClientHintsContradictions;
135
+ exports.detectGeoChronoContradictions = entropy.detectGeoChronoContradictions;
136
+ exports.detectHardwareContradictions = entropy.detectHardwareContradictions;
137
+ exports.detectOSBrowserContradictions = entropy.detectOSBrowserContradictions;
138
+ exports.evaluateCoherence = evaluateCoherence;
139
+ exports.evaluateGeoAlignment = evaluateGeoAlignment;
140
+ exports.evaluatePassport = evaluatePassport;