clearcote 0.21.0 → 0.22.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.
- package/dist/humanize.d.ts.map +1 -1
- package/dist/humanize.js +4 -2
- package/dist/humanize.js.map +1 -1
- package/dist/index.d.ts +18 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +75 -2
- package/dist/index.js.map +1 -1
- package/dist/motionscore.js +1 -1
- package/dist/motionscore.js.map +1 -1
- package/dist/profileauto.d.ts +66 -0
- package/dist/profileauto.d.ts.map +1 -0
- package/dist/profileauto.js +182 -0
- package/dist/profileauto.js.map +1 -0
- package/dist/profilefetch.d.ts +21 -0
- package/dist/profilefetch.d.ts.map +1 -0
- package/dist/profilefetch.js +124 -0
- package/dist/profilefetch.js.map +1 -0
- package/dist/profileimport.d.ts +22 -0
- package/dist/profileimport.d.ts.map +1 -0
- package/dist/profileimport.js +125 -0
- package/dist/profileimport.js.map +1 -0
- package/dist/profilelib.d.ts +100 -0
- package/dist/profilelib.d.ts.map +1 -0
- package/dist/profilelib.js +181 -0
- package/dist/profilelib.js.map +1 -0
- package/dist/profilesource.d.ts +38 -0
- package/dist/profilesource.d.ts.map +1 -0
- package/dist/profilesource.js +200 -0
- package/dist/profilesource.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// Profile library: index, host-coherence scoring, and selection.
|
|
2
|
+
//
|
|
3
|
+
// The persona path this serves is `--fingerprint-profile` WITHOUT `--fingerprint`. That
|
|
4
|
+
// distinction is load-bearing and is why this module exists: measured against a strict
|
|
5
|
+
// anti-bot device check, a seed persona failed 13/13 while a profile-only persona passed. With no
|
|
6
|
+
// seed the farbling machinery never engages, so canvas/WebGL/audio readbacks come back
|
|
7
|
+
// byte-identical to an unmodified browser — the profile is static value substitution over a
|
|
8
|
+
// real, coherent capture rather than perturbation of this machine's.
|
|
9
|
+
//
|
|
10
|
+
// SELECTION IS ABOUT COHERENCE WITH *THIS HOST*, NOT ABOUT MATCHING AN OS STRING.
|
|
11
|
+
// A profile claiming an RTX 3080 on an Intel UHD host says NVIDIA in the strings and paints
|
|
12
|
+
// Intel pixels — a contradiction a detector reads in one call. So the GPU term dominates the
|
|
13
|
+
// score, and `browser_major` is a HARD filter: a Chrome-138 capture on a 150 engine reports
|
|
14
|
+
// UA 138 while the engine behaves like 150, which is self-contradictory in a way no scoring
|
|
15
|
+
// weight should be able to outvote.
|
|
16
|
+
//
|
|
17
|
+
// ROTATION IS PER-IDENTITY, NOT PER-LAUNCH. Always serving the single best profile makes every
|
|
18
|
+
// customer on similar hardware converge on one identity, and that shared fingerprint becomes its
|
|
19
|
+
// own signal. But re-rolling every launch is worse: an account whose device changes on every
|
|
20
|
+
// visit is a harder tell than a slightly-suboptimal stable one. So the default ("rotate") is
|
|
21
|
+
// sticky-per-key — same key always resolves to the same profile, different keys spread across
|
|
22
|
+
// the top-N.
|
|
23
|
+
import { createHash } from "node:crypto";
|
|
24
|
+
/** Windows caps a process command line at 32767 chars. The profile rides on argv as
|
|
25
|
+
* `--fingerprint-profile=<gzip+base64>`, and the rest of the switches need room too, so keep a
|
|
26
|
+
* wide margin. Exceeding this is not a soft failure — Chromium refuses to spawn and Playwright
|
|
27
|
+
* surfaces it as an opaque `spawn UNKNOWN`, which is why this is enforced at selection time. */
|
|
28
|
+
export const DEFAULT_MAX_ENCODED = 24000;
|
|
29
|
+
const DEFAULT_TOP_N = 25;
|
|
30
|
+
/** Coarse GPU vendor class from a renderer string (works for ANGLE and raw GL strings). */
|
|
31
|
+
export function gpuVendorClass(renderer) {
|
|
32
|
+
const r = (renderer ?? "").toLowerCase();
|
|
33
|
+
if (!r)
|
|
34
|
+
return "unknown";
|
|
35
|
+
if (r.includes("nvidia") || r.includes("geforce") || r.includes("quadro"))
|
|
36
|
+
return "nvidia";
|
|
37
|
+
if (r.includes("amd") || r.includes("radeon") || r.includes("ati "))
|
|
38
|
+
return "amd";
|
|
39
|
+
if (r.includes("intel"))
|
|
40
|
+
return "intel";
|
|
41
|
+
if (r.includes("apple"))
|
|
42
|
+
return "apple";
|
|
43
|
+
if (r.includes("swiftshader") || r.includes("llvmpipe") || r.includes("software"))
|
|
44
|
+
return "software";
|
|
45
|
+
if (r.includes("mali") || r.includes("adreno") || r.includes("powervr"))
|
|
46
|
+
return "mobile";
|
|
47
|
+
return "other";
|
|
48
|
+
}
|
|
49
|
+
/** Ratio-based closeness in [0,1] — 1 when equal, decaying as the values diverge. */
|
|
50
|
+
function closeness(a, b) {
|
|
51
|
+
if (a === undefined || b === undefined || a <= 0 || b <= 0)
|
|
52
|
+
return 0.5; // unknown: neutral
|
|
53
|
+
const hi = Math.max(a, b), lo = Math.min(a, b);
|
|
54
|
+
return lo / hi;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Score a candidate against the host. Higher is better; the maximum is 1.
|
|
58
|
+
*
|
|
59
|
+
* Weights encode what a detector can actually catch, in descending order of how cheaply it can
|
|
60
|
+
* be caught:
|
|
61
|
+
* - GPU class (0.45): string-vs-render mismatch is a single-call contradiction.
|
|
62
|
+
* - Screen (0.25): a persona whose screen cannot contain the real window is the documented
|
|
63
|
+
* "reliable block trigger"; a profile *larger* than the host display is the dangerous
|
|
64
|
+
* direction, so it is penalised harder than a smaller one.
|
|
65
|
+
* - DPR (0.10): interacts with screen and with CSS pixel math.
|
|
66
|
+
* - Cores/memory (0.10): weakly checkable against measured worker parallelism.
|
|
67
|
+
* - Freshness (0.10): a recent capture is likelier to match current Chrome behaviour.
|
|
68
|
+
*/
|
|
69
|
+
export function scoreProfile(entry, host) {
|
|
70
|
+
let score = 0;
|
|
71
|
+
// -- GPU: exact vendor match dominates. A software renderer on either side is disqualifying
|
|
72
|
+
// in practice (the pixels can never match a claimed discrete GPU), so it scores 0.
|
|
73
|
+
const hv = host.gpu_vendor ?? "unknown";
|
|
74
|
+
const ev = entry.gpu_vendor ?? "unknown";
|
|
75
|
+
let gpu;
|
|
76
|
+
if (ev === "software" || hv === "software")
|
|
77
|
+
gpu = 0;
|
|
78
|
+
else if (ev === hv)
|
|
79
|
+
gpu = 1;
|
|
80
|
+
else if (hv === "unknown" || ev === "unknown")
|
|
81
|
+
gpu = 0.5;
|
|
82
|
+
else
|
|
83
|
+
gpu = 0.15; // different real vendor: strings and pixels disagree
|
|
84
|
+
// tier proximity refines a same-vendor match
|
|
85
|
+
if (gpu === 1 && entry.gpu_tier !== undefined && host.gpu_tier !== undefined) {
|
|
86
|
+
gpu = 1 - Math.min(1, Math.abs(entry.gpu_tier - host.gpu_tier) / 3) * 0.4;
|
|
87
|
+
}
|
|
88
|
+
score += 0.45 * gpu;
|
|
89
|
+
// -- Screen. Asymmetric on purpose: claiming a LARGER display than the host has is the
|
|
90
|
+
// contradiction that gets caught (the window cannot be contained), so penalise it more.
|
|
91
|
+
const ew = entry.screen_width, eh = entry.screen_height;
|
|
92
|
+
const hw = host.screen_width, hh = host.screen_height;
|
|
93
|
+
let screen = 0.5;
|
|
94
|
+
if (ew && eh && hw && hh) {
|
|
95
|
+
const fits = ew <= hw && eh <= hh;
|
|
96
|
+
const areaRatio = closeness(ew * eh, hw * hh);
|
|
97
|
+
screen = fits ? 0.7 + 0.3 * areaRatio : 0.25 * areaRatio;
|
|
98
|
+
}
|
|
99
|
+
score += 0.25 * screen;
|
|
100
|
+
score += 0.10 * closeness(entry.device_pixel_ratio, host.device_pixel_ratio);
|
|
101
|
+
score += 0.05 * closeness(entry.hardware_concurrency, host.hardware_concurrency);
|
|
102
|
+
score += 0.05 * closeness(entry.device_memory, host.device_memory);
|
|
103
|
+
// -- Freshness: full credit inside 6 months, decaying to zero at ~2 years.
|
|
104
|
+
let fresh = 0.5;
|
|
105
|
+
if (entry.captured_at) {
|
|
106
|
+
const days = (Date.now() - Date.parse(entry.captured_at)) / 86_400_000;
|
|
107
|
+
fresh = Number.isFinite(days) ? Math.max(0, Math.min(1, 1 - (days - 180) / 550)) : 0.5;
|
|
108
|
+
}
|
|
109
|
+
score += 0.10 * fresh;
|
|
110
|
+
return score;
|
|
111
|
+
}
|
|
112
|
+
/** Hard filters. A candidate failing any of these is not merely low-scoring — it is incoherent,
|
|
113
|
+
* and no weight should be able to promote it back into the pool. */
|
|
114
|
+
export function eligible(entry, host, opts = {}) {
|
|
115
|
+
const os = (opts.os ?? host.os_family).toLowerCase();
|
|
116
|
+
if (entry.os_family.toLowerCase() !== os)
|
|
117
|
+
return false;
|
|
118
|
+
// A version-agnostic profile (no browser_major) carries hardware identity only and inherits
|
|
119
|
+
// the engine's version, so it cannot contradict it — those pass. A profile that PINS a
|
|
120
|
+
// version and gets it wrong is a genuine contradiction: the UA would claim 138 while the
|
|
121
|
+
// engine's observable behaviour is 150. Those are rejected however well they score.
|
|
122
|
+
if (entry.browser_major != null && entry.browser_major !== host.browser_major)
|
|
123
|
+
return false;
|
|
124
|
+
const max = opts.maxEncodedSize ?? DEFAULT_MAX_ENCODED;
|
|
125
|
+
if (entry.encoded_size !== undefined && entry.encoded_size > max)
|
|
126
|
+
return false;
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
/** Stable 32-bit hash — used to map a stickiness key onto the candidate pool. */
|
|
130
|
+
function keyHash(key) {
|
|
131
|
+
return parseInt(createHash("sha256").update(key).digest("hex").slice(0, 8), 16);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Pick a profile for this host.
|
|
135
|
+
*
|
|
136
|
+
* Throws when nothing is eligible rather than silently falling back to an incoherent profile —
|
|
137
|
+
* a wrong persona is worse than no persona, because it converts a clean browser into a
|
|
138
|
+
* contradictory one.
|
|
139
|
+
*/
|
|
140
|
+
export function selectProfile(index, host, opts = {}) {
|
|
141
|
+
const mode = opts.mode ?? "rotate";
|
|
142
|
+
const candidates = index.filter((e) => eligible(e, host, opts));
|
|
143
|
+
if (candidates.length === 0) {
|
|
144
|
+
const os = opts.os ?? host.os_family;
|
|
145
|
+
throw new Error(`no profile matches host (os=${os}, chromium major=${host.browser_major}). ` +
|
|
146
|
+
`The index has ${index.length} entries; none passed the os/major/size filters. ` +
|
|
147
|
+
`Sync a newer index, or pass an explicit fingerprintProfile.`);
|
|
148
|
+
}
|
|
149
|
+
const scored = candidates
|
|
150
|
+
.map((entry) => ({ entry, score: scoreProfile(entry, host) }))
|
|
151
|
+
// tie-break by id so the ordering is deterministic across runs and machines
|
|
152
|
+
.sort((a, b) => b.score - a.score || (a.entry.id < b.entry.id ? -1 : 1));
|
|
153
|
+
const poolSize = scored.length;
|
|
154
|
+
const n = Math.max(1, Math.min(opts.topN ?? DEFAULT_TOP_N, poolSize));
|
|
155
|
+
const pool = scored.slice(0, n);
|
|
156
|
+
let chosen;
|
|
157
|
+
if (mode === "best") {
|
|
158
|
+
chosen = pool[0];
|
|
159
|
+
}
|
|
160
|
+
else if (mode === "random") {
|
|
161
|
+
chosen = pool[Math.floor(Math.random() * pool.length)];
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
// "rotate": sticky per key. With no key the caller still gets a STABLE profile rather than a
|
|
165
|
+
// new one each launch — an identity whose device changes every visit is a worse tell than a
|
|
166
|
+
// slightly-suboptimal fixed one. Callers wanting spread should pass a key.
|
|
167
|
+
const key = opts.key ?? defaultStickyKey();
|
|
168
|
+
chosen = pool[keyHash(key) % pool.length];
|
|
169
|
+
}
|
|
170
|
+
return { entry: chosen.entry, score: chosen.score, poolSize, mode };
|
|
171
|
+
}
|
|
172
|
+
/** A stable per-machine key, so keyless `"rotate"` is consistent rather than random. */
|
|
173
|
+
let cachedStickyKey = null;
|
|
174
|
+
export function defaultStickyKey() {
|
|
175
|
+
if (cachedStickyKey)
|
|
176
|
+
return cachedStickyKey;
|
|
177
|
+
const bits = [process.env.CLEARCOTE_PROFILE_KEY, process.env.USERNAME ?? process.env.USER, process.platform];
|
|
178
|
+
cachedStickyKey = createHash("sha256").update(bits.filter(Boolean).join("|")).digest("hex").slice(0, 16);
|
|
179
|
+
return cachedStickyKey;
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=profilelib.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profilelib.js","sourceRoot":"","sources":["../src/profilelib.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,EAAE;AACF,wFAAwF;AACxF,uFAAuF;AACvF,kGAAkG;AAClG,uFAAuF;AACvF,4FAA4F;AAC5F,qEAAqE;AACrE,EAAE;AACF,kFAAkF;AAClF,4FAA4F;AAC5F,6FAA6F;AAC7F,4FAA4F;AAC5F,4FAA4F;AAC5F,oCAAoC;AACpC,EAAE;AACF,+FAA+F;AAC/F,iGAAiG;AACjG,6FAA6F;AAC7F,6FAA6F;AAC7F,8FAA8F;AAC9F,aAAa;AAEb,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAiEzC;;;iGAGiG;AACjG,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,CAAC;AAEzC,MAAM,aAAa,GAAG,EAAE,CAAC;AAEzB,2FAA2F;AAC3F,MAAM,UAAU,cAAc,CAAC,QAAmC;IAChE,MAAM,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACzC,IAAI,CAAC,CAAC;QAAE,OAAO,SAAS,CAAC;IACzB,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC3F,IAAI,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IAClF,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IACxC,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IACxC,IAAI,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC;QAAE,OAAO,UAAU,CAAC;IACrG,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,QAAQ,CAAC;IACzF,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,qFAAqF;AACrF,SAAS,SAAS,CAAC,CAAqB,EAAE,CAAqB;IAC7D,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,CAAC,mBAAmB;IAC3F,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/C,OAAO,EAAE,GAAG,EAAE,CAAC;AACjB,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,YAAY,CAAC,KAAwB,EAAE,IAAe;IACpE,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,4FAA4F;IAC5F,sFAAsF;IACtF,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,IAAI,SAAS,CAAC;IACxC,MAAM,EAAE,GAAG,KAAK,CAAC,UAAU,IAAI,SAAS,CAAC;IACzC,IAAI,GAAW,CAAC;IAChB,IAAI,EAAE,KAAK,UAAU,IAAI,EAAE,KAAK,UAAU;QAAE,GAAG,GAAG,CAAC,CAAC;SAC/C,IAAI,EAAE,KAAK,EAAE;QAAE,GAAG,GAAG,CAAC,CAAC;SACvB,IAAI,EAAE,KAAK,SAAS,IAAI,EAAE,KAAK,SAAS;QAAE,GAAG,GAAG,GAAG,CAAC;;QACpD,GAAG,GAAG,IAAI,CAAC,CAAC,qDAAqD;IACtE,6CAA6C;IAC7C,IAAI,GAAG,KAAK,CAAC,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC7E,GAAG,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;IAC5E,CAAC;IACD,KAAK,IAAI,IAAI,GAAG,GAAG,CAAC;IAEpB,uFAAuF;IACvF,2FAA2F;IAC3F,MAAM,EAAE,GAAG,KAAK,CAAC,YAAY,EAAE,EAAE,GAAG,KAAK,CAAC,aAAa,CAAC;IACxD,MAAM,EAAE,GAAG,IAAI,CAAC,YAAY,EAAE,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC;IACtD,IAAI,MAAM,GAAG,GAAG,CAAC;IACjB,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QAClC,MAAM,SAAS,GAAG,SAAS,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;QAC9C,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,GAAG,SAAS,CAAC;IAC3D,CAAC;IACD,KAAK,IAAI,IAAI,GAAG,MAAM,CAAC;IAEvB,KAAK,IAAI,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,kBAAkB,EAAE,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC7E,KAAK,IAAI,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,oBAAoB,EAAE,IAAI,CAAC,oBAAoB,CAAC,CAAC;IACjF,KAAK,IAAI,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,aAAa,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;IAEnE,2EAA2E;IAC3E,IAAI,KAAK,GAAG,GAAG,CAAC;IAChB,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;QACtB,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,GAAG,UAAU,CAAC;QACvE,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,IAAI,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IACzF,CAAC;IACD,KAAK,IAAI,IAAI,GAAG,KAAK,CAAC;IAEtB,OAAO,KAAK,CAAC;AACf,CAAC;AAED;qEACqE;AACrE,MAAM,UAAU,QAAQ,CACtB,KAAwB,EACxB,IAAe,EACf,OAAsB,EAAE;IAExB,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;IACrD,IAAI,KAAK,CAAC,SAAS,CAAC,WAAW,EAAE,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IACvD,4FAA4F;IAC5F,uFAAuF;IACvF,yFAAyF;IACzF,oFAAoF;IACpF,IAAI,KAAK,CAAC,aAAa,IAAI,IAAI,IAAI,KAAK,CAAC,aAAa,KAAK,IAAI,CAAC,aAAa;QAAE,OAAO,KAAK,CAAC;IAC5F,MAAM,GAAG,GAAG,IAAI,CAAC,cAAc,IAAI,mBAAmB,CAAC;IACvD,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS,IAAI,KAAK,CAAC,YAAY,GAAG,GAAG;QAAE,OAAO,KAAK,CAAC;IAC/E,OAAO,IAAI,CAAC;AACd,CAAC;AAED,iFAAiF;AACjF,SAAS,OAAO,CAAC,GAAW;IAC1B,OAAO,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAClF,CAAC;AAUD;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAC3B,KAA0B,EAC1B,IAAe,EACf,OAAsB,EAAE;IAExB,MAAM,IAAI,GAAe,IAAI,CAAC,IAAI,IAAI,QAAQ,CAAC;IAC/C,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;IAChE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,EAAE,GAAG,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC;QACrC,MAAM,IAAI,KAAK,CACb,+BAA+B,EAAE,oBAAoB,IAAI,CAAC,aAAa,KAAK;YAC1E,iBAAiB,KAAK,CAAC,MAAM,mDAAmD;YAChF,6DAA6D,CAChE,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,UAAU;SACtB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;QAC9D,4EAA4E;SAC3E,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAE3E,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC;IAC/B,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,aAAa,EAAE,QAAQ,CAAC,CAAC,CAAC;IACtE,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAEhC,IAAI,MAAmD,CAAC;IACxD,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC;SAAM,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IACzD,CAAC;SAAM,CAAC;QACN,6FAA6F;QAC7F,4FAA4F;QAC5F,2EAA2E;QAC3E,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,gBAAgB,EAAE,CAAC;QAC3C,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AACtE,CAAC;AAED,wFAAwF;AACxF,IAAI,eAAe,GAAkB,IAAI,CAAC;AAC1C,MAAM,UAAU,gBAAgB;IAC9B,IAAI,eAAe;QAAE,OAAO,eAAe,CAAC;IAC5C,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC7G,eAAe,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACzG,OAAO,eAAe,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type HostFacts, type ProfileIndexEntry, type SelectOptions, type Selection } from "./profilelib.js";
|
|
2
|
+
export interface ProfileSourceOptions extends SelectOptions {
|
|
3
|
+
licenseKey?: string;
|
|
4
|
+
apiBase?: string;
|
|
5
|
+
/** Skip the network entirely and use only what is already cached. */
|
|
6
|
+
offline?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/** Map a Node platform string onto the os_family values the corpus stores. */
|
|
9
|
+
export declare function hostOsFamily(): string;
|
|
10
|
+
export declare function readCachedHost(exe: string): HostFacts | null;
|
|
11
|
+
export declare function writeCachedHost(exe: string, facts: HostFacts): void;
|
|
12
|
+
/**
|
|
13
|
+
* Read the host's real GPU/display by rendering in the engine itself, with no persona applied.
|
|
14
|
+
*
|
|
15
|
+
* `browserMajor` is a FALLBACK only. The major is read from the running browser when possible,
|
|
16
|
+
* because that is the only authoritative source: the caller may have pinned `version: "149"`,
|
|
17
|
+
* set CLEARCOTE_BROWSER_VERSION, or supplied their own `executablePath`, and in all of those the
|
|
18
|
+
* SDK's pinned RELEASE version describes a different binary than the one about to run. Filtering
|
|
19
|
+
* profiles against the wrong major would silently produce a persona that contradicts the engine.
|
|
20
|
+
*/
|
|
21
|
+
export declare function measureHost(launchFn: (opts: Record<string, unknown>) => Promise<{
|
|
22
|
+
newContext: () => Promise<{
|
|
23
|
+
newPage: () => Promise<unknown>;
|
|
24
|
+
}>;
|
|
25
|
+
version?: () => string;
|
|
26
|
+
close: () => Promise<void>;
|
|
27
|
+
}>, exe: string, browserMajor: number): Promise<HostFacts>;
|
|
28
|
+
/** Candidate index for a described host. Cached briefly so repeated launches do not re-ask. */
|
|
29
|
+
export declare function fetchIndex(host: HostFacts, opts?: ProfileSourceOptions): Promise<ProfileIndexEntry[]>;
|
|
30
|
+
/** Fetch one profile by id, preferring the on-disk copy — a repeat costs no distinct budget,
|
|
31
|
+
* but not spending a request at all is better still. */
|
|
32
|
+
export declare function fetchProfile(id: string, opts?: ProfileSourceOptions): Promise<Record<string, unknown>>;
|
|
33
|
+
/** Resolve a persona end to end: index -> score against this host -> fetch the winner. */
|
|
34
|
+
export declare function resolveAutoProfile(host: HostFacts, opts?: ProfileSourceOptions): Promise<{
|
|
35
|
+
profile: Record<string, unknown>;
|
|
36
|
+
selection: Selection;
|
|
37
|
+
}>;
|
|
38
|
+
//# sourceMappingURL=profilesource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profilesource.d.ts","sourceRoot":"","sources":["../src/profilesource.ts"],"names":[],"mappings":"AAaA,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,SAAS,EACf,MAAM,iBAAiB,CAAC;AAezB,MAAM,WAAW,oBAAqB,SAAQ,aAAa;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,8EAA8E;AAC9E,wBAAgB,YAAY,IAAI,MAAM,CAOrC;AAgBD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAW5D;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,IAAI,CAMnE;AA8BD;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC;IACnD,UAAU,EAAE,MAAM,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IAC/D,OAAO,CAAC,EAAE,MAAM,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B,CAAC,EACF,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,SAAS,CAAC,CA2CpB;AAiBD,+FAA+F;AAC/F,wBAAsB,UAAU,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,GAAE,oBAAyB,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAsB/G;AAED;yDACyD;AACzD,wBAAsB,YAAY,CAChC,EAAE,EAAE,MAAM,EACV,IAAI,GAAE,oBAAyB,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAiBlC;AAED,0FAA0F;AAC1F,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,SAAS,EACf,IAAI,GAAE,oBAAyB,GAC9B,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,SAAS,EAAE,SAAS,CAAA;CAAE,CAAC,CAKrE"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// Fetching personas from the profile service, and caching them locally.
|
|
2
|
+
//
|
|
3
|
+
// Selection runs CLIENT-SIDE (see profilelib.ts) because it needs facts only this machine has:
|
|
4
|
+
// the real GPU, the real display, the engine's actual Chromium major. The server cannot see any
|
|
5
|
+
// of those, so it returns candidates for a described host and the SDK picks among them.
|
|
6
|
+
//
|
|
7
|
+
// The service caps DISTINCT profiles per license per day, and re-fetching one already held is
|
|
8
|
+
// free. That is why this module caches aggressively on disk: a sticky identity must resolve to
|
|
9
|
+
// the same profile every launch without spending budget or requiring the network.
|
|
10
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { PROFILE_DIR } from "./profile.js";
|
|
13
|
+
import { selectProfile, gpuVendorClass, } from "./profilelib.js";
|
|
14
|
+
const DEFAULT_API_BASE = "https://www.clearcotelabs.com";
|
|
15
|
+
function apiBase(base) {
|
|
16
|
+
return (base || process.env.CLEARCOTE_PROFILE_API || process.env.CLEARCOTE_LICENSE_API || DEFAULT_API_BASE)
|
|
17
|
+
.replace(/\/$/, "");
|
|
18
|
+
}
|
|
19
|
+
function cacheDir() {
|
|
20
|
+
const d = join(PROFILE_DIR, "library");
|
|
21
|
+
if (!existsSync(d))
|
|
22
|
+
mkdirSync(d, { recursive: true });
|
|
23
|
+
return d;
|
|
24
|
+
}
|
|
25
|
+
/** Map a Node platform string onto the os_family values the corpus stores. */
|
|
26
|
+
export function hostOsFamily() {
|
|
27
|
+
switch (process.platform) {
|
|
28
|
+
case "win32": return "windows";
|
|
29
|
+
case "darwin": return "macos";
|
|
30
|
+
case "linux": return "linux";
|
|
31
|
+
default: return process.platform;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function hostCachePath() {
|
|
35
|
+
return join(cacheDir(), "host.json");
|
|
36
|
+
}
|
|
37
|
+
export function readCachedHost(exe) {
|
|
38
|
+
try {
|
|
39
|
+
const c = JSON.parse(readFileSync(hostCachePath(), "utf8"));
|
|
40
|
+
if (c._exe !== exe)
|
|
41
|
+
return null; // different binary: re-measure
|
|
42
|
+
// Host hardware does not change often, but a monitor or driver swap does; a month is a
|
|
43
|
+
// reasonable compromise between staleness and paying for a launch every run.
|
|
44
|
+
if (Date.now() - Date.parse(c._at) > 30 * 86_400_000)
|
|
45
|
+
return null;
|
|
46
|
+
return c;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function writeCachedHost(exe, facts) {
|
|
53
|
+
try {
|
|
54
|
+
writeFileSync(hostCachePath(), JSON.stringify({ ...facts, _exe: exe, _at: new Date().toISOString() }, null, 1));
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
/* a cache write failure must never fail a launch */
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** The in-page probe, as an expression string. Reads the REAL GPU and display — this launch
|
|
61
|
+
* applies no persona, so what it sees is the host itself. */
|
|
62
|
+
const PROBE_EXPR = `(() => {
|
|
63
|
+
let renderer = "";
|
|
64
|
+
try {
|
|
65
|
+
const gl = document.createElement("canvas").getContext("webgl");
|
|
66
|
+
const dbg = gl && gl.getExtension("WEBGL_debug_renderer_info");
|
|
67
|
+
if (gl && dbg) renderer = String(gl.getParameter(dbg.UNMASKED_RENDERER_WEBGL));
|
|
68
|
+
} catch (e) { /* headless with no GPU: leave blank, scored as unknown rather than guessed */ }
|
|
69
|
+
return {
|
|
70
|
+
renderer: renderer,
|
|
71
|
+
screen_width: screen.width,
|
|
72
|
+
screen_height: screen.height,
|
|
73
|
+
device_pixel_ratio: devicePixelRatio,
|
|
74
|
+
hardware_concurrency: navigator.hardwareConcurrency,
|
|
75
|
+
device_memory: navigator.deviceMemory,
|
|
76
|
+
};
|
|
77
|
+
})()`;
|
|
78
|
+
/**
|
|
79
|
+
* Read the host's real GPU/display by rendering in the engine itself, with no persona applied.
|
|
80
|
+
*
|
|
81
|
+
* `browserMajor` is a FALLBACK only. The major is read from the running browser when possible,
|
|
82
|
+
* because that is the only authoritative source: the caller may have pinned `version: "149"`,
|
|
83
|
+
* set CLEARCOTE_BROWSER_VERSION, or supplied their own `executablePath`, and in all of those the
|
|
84
|
+
* SDK's pinned RELEASE version describes a different binary than the one about to run. Filtering
|
|
85
|
+
* profiles against the wrong major would silently produce a persona that contradicts the engine.
|
|
86
|
+
*/
|
|
87
|
+
export async function measureHost(launchFn, exe, browserMajor) {
|
|
88
|
+
const cached = readCachedHost(exe);
|
|
89
|
+
if (cached)
|
|
90
|
+
return cached;
|
|
91
|
+
const browser = await launchFn({ executablePath: exe, headless: true, quiet: true });
|
|
92
|
+
try {
|
|
93
|
+
const ctx = await browser.newContext();
|
|
94
|
+
const page = (await ctx.newPage());
|
|
95
|
+
// about:blank is enough: WebGL and screen do not need an origin.
|
|
96
|
+
await page.goto("about:blank");
|
|
97
|
+
// Passed as an EXPRESSION STRING, not a closure: this body runs in the browser, and typing
|
|
98
|
+
// it here would mean adding the DOM lib to a Node-only SDK's tsconfig just to describe code
|
|
99
|
+
// that never executes in Node.
|
|
100
|
+
const facts = await page.evaluate(PROBE_EXPR);
|
|
101
|
+
// The engine's OWN version wins over the caller's guess. Playwright reports it as
|
|
102
|
+
// "150.0.7871.114"; fall back to the passed-in major if the shape is unexpected.
|
|
103
|
+
let major = browserMajor;
|
|
104
|
+
try {
|
|
105
|
+
const v = typeof browser.version === "function" ? browser.version() : "";
|
|
106
|
+
const m = String(v).match(/(\d+)\./);
|
|
107
|
+
if (m)
|
|
108
|
+
major = Number(m[1]);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
/* keep the fallback */
|
|
112
|
+
}
|
|
113
|
+
const out = {
|
|
114
|
+
os_family: hostOsFamily(),
|
|
115
|
+
browser_major: major,
|
|
116
|
+
gpu_vendor: gpuVendorClass(facts.renderer),
|
|
117
|
+
screen_width: facts.screen_width,
|
|
118
|
+
screen_height: facts.screen_height,
|
|
119
|
+
device_pixel_ratio: facts.device_pixel_ratio,
|
|
120
|
+
hardware_concurrency: facts.hardware_concurrency,
|
|
121
|
+
device_memory: facts.device_memory,
|
|
122
|
+
};
|
|
123
|
+
writeCachedHost(exe, out);
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
await browser.close().catch(() => { });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
// ---------------------------------------------------------------------------
|
|
131
|
+
// Service client
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
function authHeaders(licenseKey) {
|
|
134
|
+
const key = licenseKey || process.env.CLEARCOTE_LICENSE_KEY || "";
|
|
135
|
+
if (!key) {
|
|
136
|
+
throw new Error("the profile library needs a license key — pass licenseKey or set CLEARCOTE_LICENSE_KEY " +
|
|
137
|
+
"(profiles are served to PRO licenses with an active subscription)");
|
|
138
|
+
}
|
|
139
|
+
return { authorization: `Bearer ${key}` };
|
|
140
|
+
}
|
|
141
|
+
/** Candidate index for a described host. Cached briefly so repeated launches do not re-ask. */
|
|
142
|
+
export async function fetchIndex(host, opts = {}) {
|
|
143
|
+
const os = (opts.os ?? host.os_family).toLowerCase();
|
|
144
|
+
const cachePath = join(cacheDir(), `index-${os}-${host.browser_major}.json`);
|
|
145
|
+
if (opts.offline) {
|
|
146
|
+
if (!existsSync(cachePath)) {
|
|
147
|
+
throw new Error(`offline: no cached profile index for ${os}/${host.browser_major}`);
|
|
148
|
+
}
|
|
149
|
+
return JSON.parse(readFileSync(cachePath, "utf8"));
|
|
150
|
+
}
|
|
151
|
+
const url = `${apiBase(opts.apiBase)}/api/v1/profiles?os=${encodeURIComponent(os)}&major=${host.browser_major}`;
|
|
152
|
+
const res = await fetch(url, { headers: authHeaders(opts.licenseKey) });
|
|
153
|
+
if (!res.ok) {
|
|
154
|
+
// Serve stale rather than fail the launch — a cached index is far better than no persona.
|
|
155
|
+
if (existsSync(cachePath))
|
|
156
|
+
return JSON.parse(readFileSync(cachePath, "utf8"));
|
|
157
|
+
const body = await res.text().catch(() => "");
|
|
158
|
+
throw new Error(`profile index request failed (${res.status}): ${body.slice(0, 200)}`);
|
|
159
|
+
}
|
|
160
|
+
const json = (await res.json());
|
|
161
|
+
try {
|
|
162
|
+
writeFileSync(cachePath, JSON.stringify(json.candidates));
|
|
163
|
+
}
|
|
164
|
+
catch { /* non-fatal */ }
|
|
165
|
+
return json.candidates ?? [];
|
|
166
|
+
}
|
|
167
|
+
/** Fetch one profile by id, preferring the on-disk copy — a repeat costs no distinct budget,
|
|
168
|
+
* but not spending a request at all is better still. */
|
|
169
|
+
export async function fetchProfile(id, opts = {}) {
|
|
170
|
+
const cachePath = join(cacheDir(), `profile-${id}.json`);
|
|
171
|
+
if (existsSync(cachePath)) {
|
|
172
|
+
try {
|
|
173
|
+
return JSON.parse(readFileSync(cachePath, "utf8"));
|
|
174
|
+
}
|
|
175
|
+
catch { /* refetch */ }
|
|
176
|
+
}
|
|
177
|
+
if (opts.offline)
|
|
178
|
+
throw new Error(`offline: profile ${id} is not cached`);
|
|
179
|
+
const res = await fetch(`${apiBase(opts.apiBase)}/api/v1/profiles?id=${encodeURIComponent(id)}`, {
|
|
180
|
+
headers: authHeaders(opts.licenseKey),
|
|
181
|
+
});
|
|
182
|
+
if (!res.ok) {
|
|
183
|
+
const body = await res.text().catch(() => "");
|
|
184
|
+
throw new Error(`profile fetch failed (${res.status}): ${body.slice(0, 200)}`);
|
|
185
|
+
}
|
|
186
|
+
const json = (await res.json());
|
|
187
|
+
try {
|
|
188
|
+
writeFileSync(cachePath, JSON.stringify(json.profile));
|
|
189
|
+
}
|
|
190
|
+
catch { /* non-fatal */ }
|
|
191
|
+
return json.profile;
|
|
192
|
+
}
|
|
193
|
+
/** Resolve a persona end to end: index -> score against this host -> fetch the winner. */
|
|
194
|
+
export async function resolveAutoProfile(host, opts = {}) {
|
|
195
|
+
const index = await fetchIndex(host, opts);
|
|
196
|
+
const selection = selectProfile(index, host, opts);
|
|
197
|
+
const profile = await fetchProfile(selection.entry.id, opts);
|
|
198
|
+
return { profile, selection };
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=profilesource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profilesource.js","sourceRoot":"","sources":["../src/profilesource.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clearcote",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.22.1",
|
|
4
4
|
"description": "Playwright drop-in for the Clearcote anti-fingerprint Chromium build. launch() returns a standard Playwright Browser backed by the verified Clearcote binary, which is auto-downloaded and SHA-256 checked.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|