@toffee-at/sdk 0.1.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.
- package/.turbo/turbo-build.log +23 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +44 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.global.js +1 -0
- package/dist/index.js +1 -0
- package/package.json +31 -0
- package/src/core.ts +196 -0
- package/src/detect/automation.ts +34 -0
- package/src/detect/behavioral.ts +382 -0
- package/src/detect/features.ts +126 -0
- package/src/detect/fingerprint.ts +115 -0
- package/src/detect/headless.ts +44 -0
- package/src/detect/index.ts +202 -0
- package/src/detect/navigator.ts +102 -0
- package/src/detect/types.ts +52 -0
- package/src/detect/user-agent.ts +21 -0
- package/src/index.ts +22 -0
- package/src/track/elements.ts +105 -0
- package/src/track/index.ts +15 -0
- package/src/track/page.ts +43 -0
- package/src/track/session.ts +19 -0
- package/src/track/types.ts +19 -0
- package/src/transport/batch.ts +119 -0
- package/src/transport/index.ts +60 -0
- package/tests/features.test.ts +94 -0
- package/tsconfig.json +8 -0
- package/tsup.config.ts +10 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
|
|
2
|
+
> @agent-radar/sdk@0.1.0 build /Users/vishi/toffee/toffee-sdk/packages/sdk
|
|
3
|
+
> tsup
|
|
4
|
+
|
|
5
|
+
CLI Building entry: src/index.ts
|
|
6
|
+
CLI Using tsconfig: tsconfig.json
|
|
7
|
+
CLI tsup v8.5.1
|
|
8
|
+
CLI Using tsup config: /Users/vishi/toffee/toffee-sdk/packages/sdk/tsup.config.ts
|
|
9
|
+
CLI Target: es2022
|
|
10
|
+
CLI Cleaning output folder
|
|
11
|
+
ESM Build start
|
|
12
|
+
CJS Build start
|
|
13
|
+
IIFE Build start
|
|
14
|
+
IIFE dist/index.global.js 21.55 KB
|
|
15
|
+
IIFE ⚡️ Build success in 17ms
|
|
16
|
+
ESM dist/index.js 18.48 KB
|
|
17
|
+
ESM ⚡️ Build success in 22ms
|
|
18
|
+
CJS dist/index.cjs 18.99 KB
|
|
19
|
+
CJS ⚡️ Build success in 22ms
|
|
20
|
+
DTS Build start
|
|
21
|
+
DTS ⚡️ Build success in 519ms
|
|
22
|
+
DTS dist/index.d.ts 1.47 KB
|
|
23
|
+
DTS dist/index.d.cts 1.47 KB
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var L=Object.defineProperty;var tt=Object.getOwnPropertyDescriptor;var et=Object.getOwnPropertyNames;var nt=Object.prototype.hasOwnProperty;var it=(e,t)=>{for(var n in t)L(e,n,{get:t[n],enumerable:!0})},st=(e,t,n,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of et(t))!nt.call(e,i)&&i!==n&&L(e,i,{get:()=>t[i],enumerable:!(s=tt(t,i))||s.enumerable});return e};var ot=e=>st(L({},"__esModule",{value:!0}),e);var Et={};it(Et,{getDetection:()=>wt,identify:()=>bt,init:()=>yt});module.exports=ot(Et);function rt(e){let t=2166136261;for(let n=0;n<e.length;n++)t^=e.charCodeAt(n),t=t*16777619>>>0;return t.toString(36)}function F(){let e=[navigator.userAgent,`${screen.width}x${screen.height}`,Intl.DateTimeFormat().resolvedOptions().timeZone,navigator.language,new Date().toDateString()];return rt(e.join("|"))}var G=require("@toffee-at/shared");function _(){let e=navigator.userAgent,t=(0,G.matchAgent)(e);return t?{detector:"user-agent",rawScore:100,signals:[`matched-agent:${t.name}`]}:{detector:"user-agent",rawScore:0,signals:[]}}function A(){let e=0,t=[];navigator.webdriver===!0&&(e+=40,t.push("webdriver-flag")),navigator.plugins.length===0&&(e+=20,t.push("no-plugins")),!window.chrome&&/Chrome/.test(navigator.userAgent)&&(e+=15,t.push("chrome-without-chrome-object")),(window.outerWidth===0||window.outerHeight===0)&&(e+=15,t.push("zero-dimensions"));try{let n=document.createElement("canvas");n.getContext("webgl")||n.getContext("experimental-webgl")||(e+=10,t.push("no-webgl"))}catch{e+=10,t.push("webgl-error")}return{detector:"headless",rawScore:Math.min(e,100),signals:t}}function O(){let e=0,t=[],n=window,s=document;return(n.__playwright||Object.keys(n).some(i=>i.startsWith("__pw_")))&&(e+=35,t.push("playwright")),(n.__selenium_unwrapped||s.__selenium_evaluate||s.__webdriver_evaluate)&&(e+=35,t.push("selenium")),n.__nightmare&&(e+=15,t.push("nightmare")),navigator.webdriver&&(e+=15,t.push("webdriver")),{detector:"automation",rawScore:Math.min(e,100),signals:t}}var R=class{mousePoints=[];clicks=[];scrollTimestamps=[];wheelTimestamps=[];keyTimings=[];clickDurations=[];allEventTimestamps=[];pendingMouseDown=null;lastInputTime=0;trustedEventCount=0;untrustedEventCount=0;active=!1;onMouseMove=t=>{this.mousePoints.push({x:t.clientX,y:t.clientY,t:performance.now()}),this.mousePoints.length>500&&this.mousePoints.shift(),t.isTrusted?(this.trustedEventCount++,this.lastInputTime=performance.now()):this.untrustedEventCount++,this.allEventTimestamps.push(performance.now())};onClick=t=>{this.clicks.push({x:t.clientX,y:t.clientY,t:performance.now()}),t.isTrusted&&(this.trustedEventCount++,this.lastInputTime=performance.now()),this.allEventTimestamps.push(performance.now())};onScroll=()=>{this.scrollTimestamps.push(performance.now()),this.allEventTimestamps.push(performance.now())};onWheel=()=>{this.wheelTimestamps.push(performance.now()),this.lastInputTime=performance.now()};onKeyDown=t=>{this.keyTimings.push(performance.now()),t.isTrusted&&(this.trustedEventCount++,this.lastInputTime=performance.now()),this.allEventTimestamps.push(performance.now())};onMouseDown(){this.pendingMouseDown=performance.now(),this.allEventTimestamps.push(this.pendingMouseDown)}onMouseUp(){let t=performance.now();this.pendingMouseDown!==null&&(this.clickDurations.push(t-this.pendingMouseDown),this.pendingMouseDown=null),this.allEventTimestamps.push(t)}getState(){return{mousePoints:this.mousePoints,clicks:this.clicks,clickDurations:this.clickDurations,scrollTimestamps:this.scrollTimestamps,keyTimings:this.keyTimings,allEventTimestamps:this.allEventTimestamps}}start(){this.active||(this.active=!0,document.addEventListener("mousemove",this.onMouseMove,{passive:!0}),document.addEventListener("click",this.onClick,{passive:!0}),document.addEventListener("scroll",this.onScroll,{passive:!0}),document.addEventListener("wheel",this.onWheel,{passive:!0}),document.addEventListener("keydown",this.onKeyDown,{passive:!0}))}stop(){this.active&&(this.active=!1,document.removeEventListener("mousemove",this.onMouseMove),document.removeEventListener("click",this.onClick),document.removeEventListener("scroll",this.onScroll),document.removeEventListener("wheel",this.onWheel),document.removeEventListener("keydown",this.onKeyDown))}score(){let t=0,n=[];if(this.mousePoints.length===0&&(t+=25,n.push("no-mouse-events")),this.scrollTimestamps.length===0&&(t+=10,n.push("no-scroll-events")),this.keyTimings.length===0&&(t+=5,n.push("no-keyboard-events")),this.untrustedEventCount>0&&this.trustedEventCount===0&&(t+=15,n.push("all-untrusted-events")),this.mousePoints.length>=3){let s=at(this.mousePoints);s<.5?(t+=20,n.push(`low-trajectory-entropy:${s.toFixed(2)}`)):s<1.2&&(t+=10,n.push(`medium-trajectory-entropy:${s.toFixed(2)}`));let i=ct(this.mousePoints);i>.95?(t+=15,n.push(`high-movement-efficiency:${i.toFixed(3)}`)):i>.9&&(t+=8,n.push(`elevated-movement-efficiency:${i.toFixed(3)}`)),!ut(this.mousePoints)&&this.mousePoints.length>=3&&(t+=15,n.push("no-micro-jitter"));let o=lt(this.mousePoints);o>0&&(t+=Math.min(o*10,20),n.push(`mouse-teleportation:${o}`));let c=this.mousePoints[this.mousePoints.length-1].t-this.mousePoints[0].t;if(c>500){let a=this.mousePoints.length/c*1e3;a<1?(t+=25,n.push(`very-sparse-mouse-events:${a.toFixed(2)}/s`)):a<5&&(t+=15,n.push(`sparse-mouse-events:${a.toFixed(1)}/s`))}if(this.clicks.length>0){let a=this.mousePoints.length/this.clicks.length;a<3&&(t+=15,n.push(`low-points-per-click:${a.toFixed(1)}`))}}if(this.clicks.length>0&&this.mousePoints.length>0){let s=mt(this.mousePoints,this.clicks);s.teleportClicks>0&&(t+=Math.min(s.teleportClicks*15,25),n.push(`teleport-clicks:${s.teleportClicks}/${this.clicks.length}`)),s.avgApproachPath<10&&this.clicks.length>=1&&(t+=15,n.push(`minimal-approach-path:${s.avgApproachPath.toFixed(1)}px`))}if(this.scrollTimestamps.length>=3){let s=0;for(let i of this.scrollTimestamps)this.wheelTimestamps.some(c=>Math.abs(c-i)<100)||s++;s>this.scrollTimestamps.length*.7&&(t+=10,n.push("programmatic-scrolling"))}if(this.keyTimings.length>=5){let s=[];for(let a=1;a<this.keyTimings.length;a++)s.push(this.keyTimings[a]-this.keyTimings[a-1]);let i=s.reduce((a,r)=>a+r,0)/s.length,o=s.reduce((a,r)=>a+(r-i)**2,0)/s.length,c=Math.sqrt(o)/i;c<.15&&(t+=10,n.push(`uniform-keystroke-timing:cv=${c.toFixed(3)}`))}return{detector:"behavioral",rawScore:Math.min(t,100),signals:n}}};function at(e){if(e.length<3)return 3;let t=8,n=new Array(t).fill(0);for(let o=1;o<e.length;o++){let c=e[o].x-e[o-1].x,a=e[o].y-e[o-1].y;if(c===0&&a===0)continue;let r=Math.atan2(a,c);r<0&&(r+=2*Math.PI),n[Math.floor(r/(2*Math.PI)*t)%t]++}let s=n.reduce((o,c)=>o+c,0);if(s===0)return 3;let i=0;for(let o of n){if(o===0)continue;let c=o/s;i-=c*Math.log2(c)}return i}function ct(e){if(e.length<2)return 0;let t=[],n=[e[0]];for(let i=1;i<e.length;i++)e[i].t-e[i-1].t>200?(n.length>=2&&t.push(n),n=[e[i]]):n.push(e[i]);if(n.length>=2&&t.push(n),t.length===0)return 0;let s=[];for(let i of t){let o=i[0],c=i[i.length-1],a=Math.sqrt((c.x-o.x)**2+(c.y-o.y)**2),r=0;for(let u=1;u<i.length;u++)r+=Math.sqrt((i[u].x-i[u-1].x)**2+(i[u].y-i[u-1].y)**2);r>5&&s.push(a/r)}return s.length===0?0:s.reduce((i,o)=>i+o,0)/s.length}function ut(e){if(e.length<10)return!0;let t=0,n=0;for(let i=2;i<e.length;i++){let o=e[i-1].x-e[i-2].x,c=e[i-1].y-e[i-2].y,a=e[i].x-e[i-1].x,r=e[i].y-e[i-1].y,u=Math.sqrt(a*a+r*r);u>0&&u<5&&(n++,(o*a<0||c*r<0)&&t++)}return(n>0?t/n:0)>.15||n>e.length*.1}function lt(e){let t=0;for(let n=1;n<e.length;n++){let s=e[n].x-e[n-1].x,i=e[n].y-e[n-1].y;Math.sqrt(s*s+i*i)>200&&t++}return t}function mt(e,t){let n=0,s=0;for(let i of t){let o=e.filter(a=>a.t>=i.t-500&&a.t<=i.t);if(o.length<=1){n++;continue}let c=0;for(let a=1;a<o.length;a++)c+=Math.sqrt((o[a].x-o[a-1].x)**2+(o[a].y-o[a-1].y)**2);s+=c,c<10&&n++}return{teleportClicks:n,avgApproachPath:t.length>0?s/t.length:0}}function W(){let e=0,t=[],n=navigator.userAgent;try{let s=navigator.userAgentData;if(s){let i=s.platform?.toLowerCase()||"",o=/Macintosh|Mac OS X/i.test(n),c=/Windows/i.test(n),a=/Linux/i.test(n)&&!/Android/i.test(n);(o&&i&&i!=="macos"&&i!=="mac os x"||c&&i&&i!=="windows"||a&&i&&i!=="linux")&&(e+=30,t.push("client-hints-platform-mismatch"))}else/Chrome\/\d/.test(n)&&parseInt(n.match(/Chrome\/(\d+)/)?.[1]||"0")>=90&&(e+=15,t.push("missing-user-agent-data"))}catch{}navigator.hardwareConcurrency!==void 0&&navigator.hardwareConcurrency<=1&&(e+=15,t.push("low-hardware-concurrency")),/Chrome/.test(n)&&navigator.deviceMemory===void 0&&(e+=10,t.push("missing-device-memory")),/Chrome/.test(n)&&!navigator.connection&&(e+=10,t.push("missing-connection-api"));try{let s=navigator.platform?.toLowerCase()||"",i=/Macintosh|Mac OS X/i.test(n),o=/Windows/i.test(n);(i&&s&&!s.includes("mac")||o&&s&&!s.includes("win"))&&(e+=25,t.push("platform-ua-mismatch"))}catch{}return window.outerHeight>0&&window.innerHeight>0&&window.outerHeight===window.innerHeight&&(e+=10,t.push("no-browser-chrome")),screen.width===screen.availWidth&&screen.height===screen.availHeight&&screen.width>0&&(e+=5,t.push("no-taskbar")),/Mobile|Android|iPhone/i.test(n)&&window.devicePixelRatio===1&&(e+=15,t.push("mobile-ua-low-dpr")),{detector:"navigator",rawScore:Math.min(e,100),signals:t}}function N(){let e=0,t=[];try{let n=document.createElement("canvas"),s=n.getContext("webgl")||n.getContext("experimental-webgl");if(s){let i=s.getExtension("WEBGL_debug_renderer_info");if(i){let c=s.getParameter(i.UNMASKED_RENDERER_WEBGL),a=s.getParameter(i.UNMASKED_VENDOR_WEBGL);/swiftshader|mesa offscreen|llvmpipe|softpipe|software rasterizer/i.test(c)&&(e+=35,t.push(`software-renderer:${c.toLowerCase().replace(/\s+/g,"-")}`));let u=s.getSupportedExtensions();u&&u.length<15&&(e+=15,t.push("low-webgl-extensions")),/google/i.test(a)&&/swiftshader/i.test(c)&&(e+=15,t.push("google-swiftshader"))}else e+=10,t.push("no-webgl-debug-info");let o=s.getParameter(s.MAX_TEXTURE_SIZE);o&&o<4096&&(e+=10,t.push("low-max-texture"))}}catch{}try{let n=document.createElement("canvas");n.width=200,n.height=50;let s=n.getContext("2d");if(s){let i=r=>{r.fillStyle="#f60",r.fillRect(10,1,62,20),r.fillStyle="#069",r.font="11pt Arial",r.fillText("Agent Radar \u{1F50D}",2,15),r.fillStyle="rgba(102, 204, 0, 0.7)",r.fillRect(50,25,40,15)};i(s);let o=n.toDataURL(),c=document.createElement("canvas");c.width=200,c.height=50;let a=c.getContext("2d");if(a){i(a);let r=c.toDataURL();o!==r&&(e+=25,t.push("canvas-tampered"));let u=document.createElement("canvas");u.width=200,u.height=50,o===u.toDataURL()&&(e+=20,t.push("canvas-blank"))}}}catch{e+=10,t.push("canvas-error")}return{detector:"fingerprint",rawScore:Math.min(e,100),signals:t}}var ht={"user-agent":[-.1,5],headless:[-.2,4],automation:[-.1,5],navigator:[-.1,3.5],fingerprint:[-.1,3.5],behavioral:[-2.5,6]};function pt(e,t){let[n,s]=ht[e]??[-.5,4];return n+t/100*(s-n)}function K(e){let n=Math.log(.17647058823529413),s=e.map(l=>{let v=pt(l.detector,l.rawScore);return n+=v,{detector:l.detector,fired:l.rawScore>0,llr:v,rawScore:l.rawScore,evidence:l.signals}}),i=1/(1+Math.exp(-n)),o=e.flatMap(l=>l.signals),c=o.includes("webdriver-flag")||o.includes("webdriver"),a=o.includes("no-mouse-events"),r=o.includes("no-scroll-events");c&&a?i=Math.max(i,.9):c&&r&&(i=Math.max(i,.8));let u=o.some(l=>l.startsWith("teleport-clicks:")),E=o.some(l=>l.startsWith("minimal-approach-path:")),x=o.some(l=>l.startsWith("low-trajectory-entropy:")),M=o.some(l=>l.startsWith("medium-trajectory-entropy:")),m=o.includes("no-micro-jitter"),d=o.some(l=>l.startsWith("very-sparse-mouse-events:")||l.startsWith("sparse-mouse-events:")),T=o.some(l=>l.startsWith("mouse-teleportation:")),f=o.some(l=>l.startsWith("low-points-per-click:"));T&&d&&(i=Math.max(i,.85)),u&&m&&(i=Math.max(i,.85)),u&&(x||M)&&(i=Math.max(i,.8)),E&&m&&(i=Math.max(i,.8)),d&&(x||M)&&T&&(i=Math.max(i,.9)),f&&d&&(i=Math.max(i,.85)),o.some(l=>l.startsWith("software-renderer:"))&&(i=Math.max(i,.8)),i=Math.max(0,Math.min(1,i));let g;i>=.95?g="definite-bot":i>=.8?g="likely-bot":i>=.5?g="suspicious":i>=.2?g="likely-human":g="definite-human";let k=Math.round(i*100),w=i>=.5;return{score:k,probability:i,riskTier:g,isAgent:w,results:e,signals:s,classification:{classification:w?"bot":"human",probabilities:{human:1-i,bot:i,agent:0},source:"heuristic"}}}function H(){let e=[_(),A(),O(),W(),N()];return K(e)}function j(e,t){let n=1-t.probabilities.human,s;return n>=.95?s="definite-bot":n>=.8?s="likely-bot":n>=.5?s="suspicious":n>=.2?s="likely-human":s="definite-human",{...e,probability:n,riskTier:s,isAgent:t.classification!=="human",classification:{classification:t.classification,probabilities:t.probabilities,source:"model"}}}function B(e){let t=[_(),A(),O(),W(),N()];return K([...t,e])}function dt(e,t,n){let s=[...e,...t,...n].sort((o,c)=>o-c);if(s.length<2)return 0;let i=0;for(let o=1;o<s.length;o++)s[o]-s[o-1]<1e3&&i++;return i/(s.length-1)}function ft(e){if(e.length<2)return 0;let t=e.reduce((s,i)=>s+i,0)/e.length,n=e.reduce((s,i)=>s+(i-t)**2,0)/e.length;return Math.sqrt(n)}function gt(e){if(e.length<2)return 0;let t=[...e].sort((r,u)=>r-u),n=[];for(let r=1;r<t.length;r++)n.push(t[r]-t[r-1]);if(n.length===0)return 0;let s=Math.max(...n),i=Math.min(...n);if(s===i)return 0;let o=(s-i)/20,c=new Array(20).fill(0);for(let r of n){let u=Math.min(Math.floor((r-i)/o),19);c[u]++}let a=0;for(let r of c)if(r>0){let u=r/n.length;a-=u*Math.log2(u)}return a}function vt(e){let t=0;for(let n=1;n<e.length;n++){let s=e[n].x-e[n-1].x,i=e[n].y-e[n-1].y;Math.sqrt(s*s+i*i)>100&&t++}return t}function C(e){return{action_burst_ratio:dt(e.clicks.map(t=>t.t),e.scrollTimestamps,e.keyTimings),click_duration_std:ft(e.clickDurations),inter_event_entropy:gt(e.allEventTimestamps),mouse_teleportation:vt(e.mousePoints)}}function q(e){let t=Date.now();function n(){let c={type:"pageview",url:location.href,referrer:document.referrer,timestamp:Date.now()};t=Date.now(),e(c)}n();let s=()=>n();window.addEventListener("popstate",s);let i=history.pushState.bind(history),o=history.replaceState.bind(history);return history.pushState=function(...c){i(...c),n()},history.replaceState=function(...c){o(...c),n()},()=>{window.removeEventListener("popstate",s),history.pushState=i,history.replaceState=o}}var U="h1,h2,h3,h4,h5,h6,p,a,button,form,input,img,[data-ar-track]";function $(e){if(e.id)return`#${e.id}`;let t=e.getAttribute("data-ar-track");if(t)return`[data-ar-track="${t}"]`;let n=e.tagName.toLowerCase(),s=e.className&&typeof e.className=="string"?`.${e.className.trim().split(/\s+/).slice(0,2).join(".")}`:"";return`${n}${s}`}function X(e){return e.textContent?.trim().slice(0,100)||void 0}function Y(e){let t=new Map,n=new IntersectionObserver(a=>{for(let r of a)if(r.isIntersecting)t.set(r.target,Date.now());else{let u=t.get(r.target);if(u){let E={type:"interaction",action:"view",selector:$(r.target),tag:r.target.tagName.toLowerCase(),text:X(r.target),dwellMs:Date.now()-u,url:location.href,timestamp:Date.now()};e(E),t.delete(r.target)}}},{threshold:.5});function s(){let a=document.querySelectorAll(U);for(let r of a)n.observe(r)}s();let i=new MutationObserver(()=>s());i.observe(document.body,{childList:!0,subtree:!0});let o=a=>{let r=a.target?.closest?.(U);if(!r)return;let u={type:"interaction",action:"click",selector:$(r),tag:r.tagName.toLowerCase(),text:X(r),url:location.href,timestamp:Date.now()};e(u)},c=a=>{let r=a.target;if(!r?.matches?.("input,textarea,select,[data-ar-track]"))return;let u={type:"interaction",action:"input",selector:$(r),tag:r.tagName.toLowerCase(),url:location.href,timestamp:Date.now()};e(u)};return document.body.addEventListener("click",o,{passive:!0}),document.body.addEventListener("input",c,{passive:!0}),()=>{n.disconnect(),i.disconnect(),document.body.removeEventListener("click",o),document.body.removeEventListener("input",c)}}function Z(e){let t=q(e),n=Y(e);return()=>{t(),n()}}var S=class{queue=[];timer=null;endpoint;siteId;sessionId;apiKey;metadata;onClassification;constructor(t){this.endpoint=t.endpoint,this.siteId=t.siteId,this.sessionId=t.sessionId,this.apiKey=t.apiKey,this.onClassification=t.onClassification}start(){this.timer=setInterval(()=>this.flush(),5e3)}stop(){this.timer&&(clearInterval(this.timer),this.timer=null)}setMetadata(t){this.metadata=t}push(t){this.queue.push(t),this.queue.length>=20&&this.flush()}flush(){if(this.queue.length===0)return;let t={siteId:this.siteId,sessionId:this.sessionId,events:this.queue.splice(0),metadata:this.metadata,timestamp:Date.now()},n=JSON.stringify(t);this.send(n)}flushBeacon(){if(this.queue.length===0)return;let t={siteId:this.siteId,sessionId:this.sessionId,events:this.queue.splice(0),metadata:this.metadata,timestamp:Date.now()},n=JSON.stringify(t),s=`${this.endpoint}?apiKey=${encodeURIComponent(this.apiKey)}`;navigator.sendBeacon(s,n)}send(t){try{fetch(this.endpoint,{method:"POST",headers:{"Content-Type":"application/json","X-Api-Key":this.apiKey},body:t,keepalive:!0}).then(n=>n.ok&&this.onClassification?n.json():null).then(n=>{n?.classifications&&this.onClassification&&this.onClassification(n.classifications)}).catch(()=>{})}catch{}}};function J(e){let t=new S(e);t.start();let n=Date.now(),s=0,i=0,o=()=>{document.visibilityState==="hidden"&&(a(),t.flushBeacon())},c=()=>{a(),t.flushBeacon()};function a(){t.push({type:"session_end",timestamp:Date.now(),url:location.href,duration:Date.now()-n,pageCount:s,interactionCount:i})}return document.addEventListener("visibilitychange",o),window.addEventListener("pagehide",c),{push:r=>{let u=r;u.type==="pageview"&&s++,u.type==="interaction"&&i++,t.push(r)},setMetadata:r=>t.setMetadata(r),flush:()=>t.flush(),destroy:()=>{t.stop(),a(),t.flush(),document.removeEventListener("visibilitychange",o),window.removeEventListener("pagehide",c)}}}function V(e){let t=e.endpoint??"/api/v1/events",n=F(),s=null,i=null,o,c=null;function a(...m){e.debug&&console.log("[agent-radar]",...m)}function r(m,d){c=m,a(`detection [${d}]`,m),e.onDetection?.(m)}function u(){i=J({endpoint:t,siteId:e.siteId,sessionId:n,apiKey:e.apiKey,onClassification:h=>{let y=Object.values(h);if(y.length>0&&c){let p=y[y.length-1];c=j(c,p),e.onDetection?.(c)}}});let m=new R;m.start();let d=()=>m.onMouseDown(),T=()=>m.onMouseUp();document.addEventListener("mousedown",d,!0),document.addEventListener("mouseup",T,!0);let f=H();r(f,"instant"),i.push({type:"detection",score:f.score,probability:f.probability,riskTier:f.riskTier,isAgent:f.isAgent,results:f.results,stage:"instant",timestamp:Date.now(),features:C(m.getState())}),s=Z(h=>{a("event",h),i?.push(h)}),i.flush();let P=[3e3,1e4,3e4],g=["early","session","extended"],k=[];function w(h){let y=m.score(),p=B(y);r(p,h),i?.push({type:"detection",score:p.score,probability:p.probability,riskTier:p.riskTier,isAgent:p.isAgent,results:p.results,stage:h,full:!0,timestamp:Date.now(),features:C(m.getState())}),i?.flush()}for(let h=0;h<P.length;h++)k.push(setTimeout(()=>w(g[h]),P[h]));let l=setInterval(()=>{w("continuous")},15e3),v=null,D=()=>{v&&clearTimeout(v),v=setTimeout(()=>{w("interaction")},1e3)};document.addEventListener("click",D,{passive:!0}),document.addEventListener("scroll",D,{passive:!0});let I=navigator.webdriver,z=setInterval(()=>{let h=navigator.webdriver;if(h&&!I){a("webdriver state changed \u2014 re-running detection"),I=h;let y=m.score(),p=B(y);r(p,"webdriver-change"),i?.push({type:"detection",score:p.score,probability:p.probability,riskTier:p.riskTier,isAgent:p.isAgent,results:p.results,stage:"webdriver-change",full:!0,timestamp:Date.now(),features:C(m.getState())}),i?.flush()}I=h},5e3),Q=s;s=()=>{k.forEach(clearTimeout),clearInterval(l),clearInterval(z),v&&clearTimeout(v),document.removeEventListener("click",D),document.removeEventListener("scroll",D),document.removeEventListener("mousedown",d,!0),document.removeEventListener("mouseup",T,!0),m.stop(),Q?.()}}function E(m){o=m,i?.setMetadata(m),a("identify",m)}function x(){return c}function M(){s?.(),i?.destroy(),s=null,i=null}return{start:u,identify:E,getDetection:x,destroy:M}}var b=null;function yt(e){return b||(b=V(e),b.start(),b)}function bt(e){b?.identify(e)}function wt(){return b?.getDetection()??null}0&&(module.exports={getDetection,identify,init});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ModelClassification } from '@toffee-at/shared';
|
|
2
|
+
|
|
3
|
+
type DetectorName = 'user-agent' | 'headless' | 'automation' | 'behavioral' | 'navigator' | 'fingerprint';
|
|
4
|
+
interface DetectorResult {
|
|
5
|
+
detector: DetectorName;
|
|
6
|
+
rawScore: number;
|
|
7
|
+
signals: string[];
|
|
8
|
+
}
|
|
9
|
+
type RiskTier = 'definite-bot' | 'likely-bot' | 'suspicious' | 'likely-human' | 'definite-human';
|
|
10
|
+
type AgentCategory = 'human' | 'ai-assistant' | 'ai-crawler' | 'ai-agent' | 'search-engine' | 'social-preview' | 'monitoring' | 'scraper' | 'automation-tool' | 'unknown-bot';
|
|
11
|
+
interface DetectionOutput {
|
|
12
|
+
score: number;
|
|
13
|
+
probability: number;
|
|
14
|
+
riskTier: RiskTier;
|
|
15
|
+
isAgent: boolean;
|
|
16
|
+
results: DetectorResult[];
|
|
17
|
+
signals: {
|
|
18
|
+
detector: string;
|
|
19
|
+
fired: boolean;
|
|
20
|
+
llr: number;
|
|
21
|
+
rawScore: number;
|
|
22
|
+
evidence: string[];
|
|
23
|
+
}[];
|
|
24
|
+
classification: ModelClassification;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface AgentRadarConfig {
|
|
28
|
+
siteId: string;
|
|
29
|
+
apiKey: string;
|
|
30
|
+
endpoint?: string;
|
|
31
|
+
debug?: boolean;
|
|
32
|
+
onDetection?: (result: DetectionOutput) => void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
declare function init(config: AgentRadarConfig): {
|
|
36
|
+
start: () => void;
|
|
37
|
+
identify: (metadata: Record<string, string>) => void;
|
|
38
|
+
getDetection: () => DetectionOutput | null;
|
|
39
|
+
destroy: () => void;
|
|
40
|
+
};
|
|
41
|
+
declare function identify(metadata: Record<string, string>): void;
|
|
42
|
+
declare function getDetection(): DetectionOutput | null;
|
|
43
|
+
|
|
44
|
+
export { type AgentCategory, type AgentRadarConfig, type DetectionOutput, type RiskTier, getDetection, identify, init };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ModelClassification } from '@toffee-at/shared';
|
|
2
|
+
|
|
3
|
+
type DetectorName = 'user-agent' | 'headless' | 'automation' | 'behavioral' | 'navigator' | 'fingerprint';
|
|
4
|
+
interface DetectorResult {
|
|
5
|
+
detector: DetectorName;
|
|
6
|
+
rawScore: number;
|
|
7
|
+
signals: string[];
|
|
8
|
+
}
|
|
9
|
+
type RiskTier = 'definite-bot' | 'likely-bot' | 'suspicious' | 'likely-human' | 'definite-human';
|
|
10
|
+
type AgentCategory = 'human' | 'ai-assistant' | 'ai-crawler' | 'ai-agent' | 'search-engine' | 'social-preview' | 'monitoring' | 'scraper' | 'automation-tool' | 'unknown-bot';
|
|
11
|
+
interface DetectionOutput {
|
|
12
|
+
score: number;
|
|
13
|
+
probability: number;
|
|
14
|
+
riskTier: RiskTier;
|
|
15
|
+
isAgent: boolean;
|
|
16
|
+
results: DetectorResult[];
|
|
17
|
+
signals: {
|
|
18
|
+
detector: string;
|
|
19
|
+
fired: boolean;
|
|
20
|
+
llr: number;
|
|
21
|
+
rawScore: number;
|
|
22
|
+
evidence: string[];
|
|
23
|
+
}[];
|
|
24
|
+
classification: ModelClassification;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface AgentRadarConfig {
|
|
28
|
+
siteId: string;
|
|
29
|
+
apiKey: string;
|
|
30
|
+
endpoint?: string;
|
|
31
|
+
debug?: boolean;
|
|
32
|
+
onDetection?: (result: DetectionOutput) => void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
declare function init(config: AgentRadarConfig): {
|
|
36
|
+
start: () => void;
|
|
37
|
+
identify: (metadata: Record<string, string>) => void;
|
|
38
|
+
getDetection: () => DetectionOutput | null;
|
|
39
|
+
destroy: () => void;
|
|
40
|
+
};
|
|
41
|
+
declare function identify(metadata: Record<string, string>): void;
|
|
42
|
+
declare function getDetection(): DetectionOutput | null;
|
|
43
|
+
|
|
44
|
+
export { type AgentCategory, type AgentRadarConfig, type DetectionOutput, type RiskTier, getDetection, identify, init };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var AgentRadar=(()=>{var A=Object.defineProperty;var tt=Object.getOwnPropertyDescriptor;var et=Object.getOwnPropertyNames;var nt=Object.prototype.hasOwnProperty;var it=(e,t)=>{for(var n in t)A(e,n,{get:t[n],enumerable:!0})},st=(e,t,n,s)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of et(t))!nt.call(e,i)&&i!==n&&A(e,i,{get:()=>t[i],enumerable:!(s=tt(t,i))||s.enumerable});return e};var ot=e=>st(A({},"__esModule",{value:!0}),e);var xt={};it(xt,{getDetection:()=>Et,identify:()=>wt,init:()=>bt});function rt(e){let t=2166136261;for(let n=0;n<e.length;n++)t^=e.charCodeAt(n),t=t*16777619>>>0;return t.toString(36)}function H(){let e=[navigator.userAgent,`${screen.width}x${screen.height}`,Intl.DateTimeFormat().resolvedOptions().timeZone,navigator.language,new Date().toDateString()];return rt(e.join("|"))}var at=[{name:"GPTBot",pattern:/GPTBot/i,category:"ai-crawler",url:"https://openai.com/gptbot"},{name:"ChatGPT-User",pattern:/ChatGPT-User/i,category:"ai-assistant",url:"https://openai.com/chatgpt"},{name:"ClaudeBot",pattern:/ClaudeBot/i,category:"ai-crawler",url:"https://anthropic.com"},{name:"Claude-Web",pattern:/Claude-Web/i,category:"ai-assistant",url:"https://anthropic.com"},{name:"PerplexityBot",pattern:/PerplexityBot/i,category:"ai-assistant",url:"https://perplexity.ai"},{name:"Bytespider",pattern:/Bytespider/i,category:"ai-crawler"},{name:"CCBot",pattern:/CCBot/i,category:"ai-crawler",url:"https://commoncrawl.org"},{name:"Google-Extended",pattern:/Google-Extended/i,category:"ai-crawler"},{name:"Googlebot",pattern:/Googlebot/i,category:"search-engine"},{name:"Bingbot",pattern:/bingbot/i,category:"search-engine"},{name:"FacebookBot",pattern:/facebookexternalhit/i,category:"search-engine"},{name:"Applebot",pattern:/Applebot/i,category:"search-engine"},{name:"Selenium",pattern:/Selenium/i,category:"automation"},{name:"Puppeteer",pattern:/Puppeteer|HeadlessChrome/i,category:"automation"},{name:"Playwright",pattern:/Playwright/i,category:"automation"},{name:"PhantomJS",pattern:/PhantomJS/i,category:"automation"},{name:"Cohere-AI",pattern:/cohere-ai/i,category:"ai-crawler"},{name:"Anthropic-AI",pattern:/anthropic-ai/i,category:"ai-crawler"},{name:"YouBot",pattern:/YouBot/i,category:"ai-crawler"},{name:"AI2Bot",pattern:/AI2Bot/i,category:"ai-crawler"}];function $(e){for(let t of at)if(t.pattern.test(e))return t;return null}function _(){let e=navigator.userAgent,t=$(e);return t?{detector:"user-agent",rawScore:100,signals:[`matched-agent:${t.name}`]}:{detector:"user-agent",rawScore:0,signals:[]}}function L(){let e=0,t=[];navigator.webdriver===!0&&(e+=40,t.push("webdriver-flag")),navigator.plugins.length===0&&(e+=20,t.push("no-plugins")),!window.chrome&&/Chrome/.test(navigator.userAgent)&&(e+=15,t.push("chrome-without-chrome-object")),(window.outerWidth===0||window.outerHeight===0)&&(e+=15,t.push("zero-dimensions"));try{let n=document.createElement("canvas");n.getContext("webgl")||n.getContext("experimental-webgl")||(e+=10,t.push("no-webgl"))}catch{e+=10,t.push("webgl-error")}return{detector:"headless",rawScore:Math.min(e,100),signals:t}}function O(){let e=0,t=[],n=window,s=document;return(n.__playwright||Object.keys(n).some(i=>i.startsWith("__pw_")))&&(e+=35,t.push("playwright")),(n.__selenium_unwrapped||s.__selenium_evaluate||s.__webdriver_evaluate)&&(e+=35,t.push("selenium")),n.__nightmare&&(e+=15,t.push("nightmare")),navigator.webdriver&&(e+=15,t.push("webdriver")),{detector:"automation",rawScore:Math.min(e,100),signals:t}}var S=class{mousePoints=[];clicks=[];scrollTimestamps=[];wheelTimestamps=[];keyTimings=[];clickDurations=[];allEventTimestamps=[];pendingMouseDown=null;lastInputTime=0;trustedEventCount=0;untrustedEventCount=0;active=!1;onMouseMove=t=>{this.mousePoints.push({x:t.clientX,y:t.clientY,t:performance.now()}),this.mousePoints.length>500&&this.mousePoints.shift(),t.isTrusted?(this.trustedEventCount++,this.lastInputTime=performance.now()):this.untrustedEventCount++,this.allEventTimestamps.push(performance.now())};onClick=t=>{this.clicks.push({x:t.clientX,y:t.clientY,t:performance.now()}),t.isTrusted&&(this.trustedEventCount++,this.lastInputTime=performance.now()),this.allEventTimestamps.push(performance.now())};onScroll=()=>{this.scrollTimestamps.push(performance.now()),this.allEventTimestamps.push(performance.now())};onWheel=()=>{this.wheelTimestamps.push(performance.now()),this.lastInputTime=performance.now()};onKeyDown=t=>{this.keyTimings.push(performance.now()),t.isTrusted&&(this.trustedEventCount++,this.lastInputTime=performance.now()),this.allEventTimestamps.push(performance.now())};onMouseDown(){this.pendingMouseDown=performance.now(),this.allEventTimestamps.push(this.pendingMouseDown)}onMouseUp(){let t=performance.now();this.pendingMouseDown!==null&&(this.clickDurations.push(t-this.pendingMouseDown),this.pendingMouseDown=null),this.allEventTimestamps.push(t)}getState(){return{mousePoints:this.mousePoints,clicks:this.clicks,clickDurations:this.clickDurations,scrollTimestamps:this.scrollTimestamps,keyTimings:this.keyTimings,allEventTimestamps:this.allEventTimestamps}}start(){this.active||(this.active=!0,document.addEventListener("mousemove",this.onMouseMove,{passive:!0}),document.addEventListener("click",this.onClick,{passive:!0}),document.addEventListener("scroll",this.onScroll,{passive:!0}),document.addEventListener("wheel",this.onWheel,{passive:!0}),document.addEventListener("keydown",this.onKeyDown,{passive:!0}))}stop(){this.active&&(this.active=!1,document.removeEventListener("mousemove",this.onMouseMove),document.removeEventListener("click",this.onClick),document.removeEventListener("scroll",this.onScroll),document.removeEventListener("wheel",this.onWheel),document.removeEventListener("keydown",this.onKeyDown))}score(){let t=0,n=[];if(this.mousePoints.length===0&&(t+=25,n.push("no-mouse-events")),this.scrollTimestamps.length===0&&(t+=10,n.push("no-scroll-events")),this.keyTimings.length===0&&(t+=5,n.push("no-keyboard-events")),this.untrustedEventCount>0&&this.trustedEventCount===0&&(t+=15,n.push("all-untrusted-events")),this.mousePoints.length>=3){let s=ct(this.mousePoints);s<.5?(t+=20,n.push(`low-trajectory-entropy:${s.toFixed(2)}`)):s<1.2&&(t+=10,n.push(`medium-trajectory-entropy:${s.toFixed(2)}`));let i=ut(this.mousePoints);i>.95?(t+=15,n.push(`high-movement-efficiency:${i.toFixed(3)}`)):i>.9&&(t+=8,n.push(`elevated-movement-efficiency:${i.toFixed(3)}`)),!lt(this.mousePoints)&&this.mousePoints.length>=3&&(t+=15,n.push("no-micro-jitter"));let o=mt(this.mousePoints);o>0&&(t+=Math.min(o*10,20),n.push(`mouse-teleportation:${o}`));let c=this.mousePoints[this.mousePoints.length-1].t-this.mousePoints[0].t;if(c>500){let a=this.mousePoints.length/c*1e3;a<1?(t+=25,n.push(`very-sparse-mouse-events:${a.toFixed(2)}/s`)):a<5&&(t+=15,n.push(`sparse-mouse-events:${a.toFixed(1)}/s`))}if(this.clicks.length>0){let a=this.mousePoints.length/this.clicks.length;a<3&&(t+=15,n.push(`low-points-per-click:${a.toFixed(1)}`))}}if(this.clicks.length>0&&this.mousePoints.length>0){let s=pt(this.mousePoints,this.clicks);s.teleportClicks>0&&(t+=Math.min(s.teleportClicks*15,25),n.push(`teleport-clicks:${s.teleportClicks}/${this.clicks.length}`)),s.avgApproachPath<10&&this.clicks.length>=1&&(t+=15,n.push(`minimal-approach-path:${s.avgApproachPath.toFixed(1)}px`))}if(this.scrollTimestamps.length>=3){let s=0;for(let i of this.scrollTimestamps)this.wheelTimestamps.some(c=>Math.abs(c-i)<100)||s++;s>this.scrollTimestamps.length*.7&&(t+=10,n.push("programmatic-scrolling"))}if(this.keyTimings.length>=5){let s=[];for(let a=1;a<this.keyTimings.length;a++)s.push(this.keyTimings[a]-this.keyTimings[a-1]);let i=s.reduce((a,r)=>a+r,0)/s.length,o=s.reduce((a,r)=>a+(r-i)**2,0)/s.length,c=Math.sqrt(o)/i;c<.15&&(t+=10,n.push(`uniform-keystroke-timing:cv=${c.toFixed(3)}`))}return{detector:"behavioral",rawScore:Math.min(t,100),signals:n}}};function ct(e){if(e.length<3)return 3;let t=8,n=new Array(t).fill(0);for(let o=1;o<e.length;o++){let c=e[o].x-e[o-1].x,a=e[o].y-e[o-1].y;if(c===0&&a===0)continue;let r=Math.atan2(a,c);r<0&&(r+=2*Math.PI),n[Math.floor(r/(2*Math.PI)*t)%t]++}let s=n.reduce((o,c)=>o+c,0);if(s===0)return 3;let i=0;for(let o of n){if(o===0)continue;let c=o/s;i-=c*Math.log2(c)}return i}function ut(e){if(e.length<2)return 0;let t=[],n=[e[0]];for(let i=1;i<e.length;i++)e[i].t-e[i-1].t>200?(n.length>=2&&t.push(n),n=[e[i]]):n.push(e[i]);if(n.length>=2&&t.push(n),t.length===0)return 0;let s=[];for(let i of t){let o=i[0],c=i[i.length-1],a=Math.sqrt((c.x-o.x)**2+(c.y-o.y)**2),r=0;for(let u=1;u<i.length;u++)r+=Math.sqrt((i[u].x-i[u-1].x)**2+(i[u].y-i[u-1].y)**2);r>5&&s.push(a/r)}return s.length===0?0:s.reduce((i,o)=>i+o,0)/s.length}function lt(e){if(e.length<10)return!0;let t=0,n=0;for(let i=2;i<e.length;i++){let o=e[i-1].x-e[i-2].x,c=e[i-1].y-e[i-2].y,a=e[i].x-e[i-1].x,r=e[i].y-e[i-1].y,u=Math.sqrt(a*a+r*r);u>0&&u<5&&(n++,(o*a<0||c*r<0)&&t++)}return(n>0?t/n:0)>.15||n>e.length*.1}function mt(e){let t=0;for(let n=1;n<e.length;n++){let s=e[n].x-e[n-1].x,i=e[n].y-e[n-1].y;Math.sqrt(s*s+i*i)>200&&t++}return t}function pt(e,t){let n=0,s=0;for(let i of t){let o=e.filter(a=>a.t>=i.t-500&&a.t<=i.t);if(o.length<=1){n++;continue}let c=0;for(let a=1;a<o.length;a++)c+=Math.sqrt((o[a].x-o[a-1].x)**2+(o[a].y-o[a-1].y)**2);s+=c,c<10&&n++}return{teleportClicks:n,avgApproachPath:t.length>0?s/t.length:0}}function B(){let e=0,t=[],n=navigator.userAgent;try{let s=navigator.userAgentData;if(s){let i=s.platform?.toLowerCase()||"",o=/Macintosh|Mac OS X/i.test(n),c=/Windows/i.test(n),a=/Linux/i.test(n)&&!/Android/i.test(n);(o&&i&&i!=="macos"&&i!=="mac os x"||c&&i&&i!=="windows"||a&&i&&i!=="linux")&&(e+=30,t.push("client-hints-platform-mismatch"))}else/Chrome\/\d/.test(n)&&parseInt(n.match(/Chrome\/(\d+)/)?.[1]||"0")>=90&&(e+=15,t.push("missing-user-agent-data"))}catch{}navigator.hardwareConcurrency!==void 0&&navigator.hardwareConcurrency<=1&&(e+=15,t.push("low-hardware-concurrency")),/Chrome/.test(n)&&navigator.deviceMemory===void 0&&(e+=10,t.push("missing-device-memory")),/Chrome/.test(n)&&!navigator.connection&&(e+=10,t.push("missing-connection-api"));try{let s=navigator.platform?.toLowerCase()||"",i=/Macintosh|Mac OS X/i.test(n),o=/Windows/i.test(n);(i&&s&&!s.includes("mac")||o&&s&&!s.includes("win"))&&(e+=25,t.push("platform-ua-mismatch"))}catch{}return window.outerHeight>0&&window.innerHeight>0&&window.outerHeight===window.innerHeight&&(e+=10,t.push("no-browser-chrome")),screen.width===screen.availWidth&&screen.height===screen.availHeight&&screen.width>0&&(e+=5,t.push("no-taskbar")),/Mobile|Android|iPhone/i.test(n)&&window.devicePixelRatio===1&&(e+=15,t.push("mobile-ua-low-dpr")),{detector:"navigator",rawScore:Math.min(e,100),signals:t}}function N(){let e=0,t=[];try{let n=document.createElement("canvas"),s=n.getContext("webgl")||n.getContext("experimental-webgl");if(s){let i=s.getExtension("WEBGL_debug_renderer_info");if(i){let c=s.getParameter(i.UNMASKED_RENDERER_WEBGL),a=s.getParameter(i.UNMASKED_VENDOR_WEBGL);/swiftshader|mesa offscreen|llvmpipe|softpipe|software rasterizer/i.test(c)&&(e+=35,t.push(`software-renderer:${c.toLowerCase().replace(/\s+/g,"-")}`));let u=s.getSupportedExtensions();u&&u.length<15&&(e+=15,t.push("low-webgl-extensions")),/google/i.test(a)&&/swiftshader/i.test(c)&&(e+=15,t.push("google-swiftshader"))}else e+=10,t.push("no-webgl-debug-info");let o=s.getParameter(s.MAX_TEXTURE_SIZE);o&&o<4096&&(e+=10,t.push("low-max-texture"))}}catch{}try{let n=document.createElement("canvas");n.width=200,n.height=50;let s=n.getContext("2d");if(s){let i=r=>{r.fillStyle="#f60",r.fillRect(10,1,62,20),r.fillStyle="#069",r.font="11pt Arial",r.fillText("Agent Radar \u{1F50D}",2,15),r.fillStyle="rgba(102, 204, 0, 0.7)",r.fillRect(50,25,40,15)};i(s);let o=n.toDataURL(),c=document.createElement("canvas");c.width=200,c.height=50;let a=c.getContext("2d");if(a){i(a);let r=c.toDataURL();o!==r&&(e+=25,t.push("canvas-tampered"));let u=document.createElement("canvas");u.width=200,u.height=50,o===u.toDataURL()&&(e+=20,t.push("canvas-blank"))}}}catch{e+=10,t.push("canvas-error")}return{detector:"fingerprint",rawScore:Math.min(e,100),signals:t}}var ht={"user-agent":[-.1,5],headless:[-.2,4],automation:[-.1,5],navigator:[-.1,3.5],fingerprint:[-.1,3.5],behavioral:[-2.5,6]};function dt(e,t){let[n,s]=ht[e]??[-.5,4];return n+t/100*(s-n)}function F(e){let n=Math.log(.17647058823529413),s=e.map(l=>{let v=dt(l.detector,l.rawScore);return n+=v,{detector:l.detector,fired:l.rawScore>0,llr:v,rawScore:l.rawScore,evidence:l.signals}}),i=1/(1+Math.exp(-n)),o=e.flatMap(l=>l.signals),c=o.includes("webdriver-flag")||o.includes("webdriver"),a=o.includes("no-mouse-events"),r=o.includes("no-scroll-events");c&&a?i=Math.max(i,.9):c&&r&&(i=Math.max(i,.8));let u=o.some(l=>l.startsWith("teleport-clicks:")),E=o.some(l=>l.startsWith("minimal-approach-path:")),T=o.some(l=>l.startsWith("low-trajectory-entropy:")),M=o.some(l=>l.startsWith("medium-trajectory-entropy:")),m=o.includes("no-micro-jitter"),d=o.some(l=>l.startsWith("very-sparse-mouse-events:")||l.startsWith("sparse-mouse-events:")),x=o.some(l=>l.startsWith("mouse-teleportation:")),g=o.some(l=>l.startsWith("low-points-per-click:"));x&&d&&(i=Math.max(i,.85)),u&&m&&(i=Math.max(i,.85)),u&&(T||M)&&(i=Math.max(i,.8)),E&&m&&(i=Math.max(i,.8)),d&&(T||M)&&x&&(i=Math.max(i,.9)),g&&d&&(i=Math.max(i,.85)),o.some(l=>l.startsWith("software-renderer:"))&&(i=Math.max(i,.8)),i=Math.max(0,Math.min(1,i));let f;i>=.95?f="definite-bot":i>=.8?f="likely-bot":i>=.5?f="suspicious":i>=.2?f="likely-human":f="definite-human";let C=Math.round(i*100),w=i>=.5;return{score:C,probability:i,riskTier:f,isAgent:w,results:e,signals:s,classification:{classification:w?"bot":"human",probabilities:{human:1-i,bot:i,agent:0},source:"heuristic"}}}function K(){let e=[_(),L(),O(),B(),N()];return F(e)}function U(e,t){let n=1-t.probabilities.human,s;return n>=.95?s="definite-bot":n>=.8?s="likely-bot":n>=.5?s="suspicious":n>=.2?s="likely-human":s="definite-human",{...e,probability:n,riskTier:s,isAgent:t.classification!=="human",classification:{classification:t.classification,probabilities:t.probabilities,source:"model"}}}function W(e){let t=[_(),L(),O(),B(),N()];return F([...t,e])}function gt(e,t,n){let s=[...e,...t,...n].sort((o,c)=>o-c);if(s.length<2)return 0;let i=0;for(let o=1;o<s.length;o++)s[o]-s[o-1]<1e3&&i++;return i/(s.length-1)}function ft(e){if(e.length<2)return 0;let t=e.reduce((s,i)=>s+i,0)/e.length,n=e.reduce((s,i)=>s+(i-t)**2,0)/e.length;return Math.sqrt(n)}function vt(e){if(e.length<2)return 0;let t=[...e].sort((r,u)=>r-u),n=[];for(let r=1;r<t.length;r++)n.push(t[r]-t[r-1]);if(n.length===0)return 0;let s=Math.max(...n),i=Math.min(...n);if(s===i)return 0;let o=(s-i)/20,c=new Array(20).fill(0);for(let r of n){let u=Math.min(Math.floor((r-i)/o),19);c[u]++}let a=0;for(let r of c)if(r>0){let u=r/n.length;a-=u*Math.log2(u)}return a}function yt(e){let t=0;for(let n=1;n<e.length;n++){let s=e[n].x-e[n-1].x,i=e[n].y-e[n-1].y;Math.sqrt(s*s+i*i)>100&&t++}return t}function R(e){return{action_burst_ratio:gt(e.clicks.map(t=>t.t),e.scrollTimestamps,e.keyTimings),click_duration_std:ft(e.clickDurations),inter_event_entropy:vt(e.allEventTimestamps),mouse_teleportation:yt(e.mousePoints)}}function j(e){let t=Date.now();function n(){let c={type:"pageview",url:location.href,referrer:document.referrer,timestamp:Date.now()};t=Date.now(),e(c)}n();let s=()=>n();window.addEventListener("popstate",s);let i=history.pushState.bind(history),o=history.replaceState.bind(history);return history.pushState=function(...c){i(...c),n()},history.replaceState=function(...c){o(...c),n()},()=>{window.removeEventListener("popstate",s),history.pushState=i,history.replaceState=o}}var q="h1,h2,h3,h4,h5,h6,p,a,button,form,input,img,[data-ar-track]";function G(e){if(e.id)return`#${e.id}`;let t=e.getAttribute("data-ar-track");if(t)return`[data-ar-track="${t}"]`;let n=e.tagName.toLowerCase(),s=e.className&&typeof e.className=="string"?`.${e.className.trim().split(/\s+/).slice(0,2).join(".")}`:"";return`${n}${s}`}function X(e){return e.textContent?.trim().slice(0,100)||void 0}function V(e){let t=new Map,n=new IntersectionObserver(a=>{for(let r of a)if(r.isIntersecting)t.set(r.target,Date.now());else{let u=t.get(r.target);if(u){let E={type:"interaction",action:"view",selector:G(r.target),tag:r.target.tagName.toLowerCase(),text:X(r.target),dwellMs:Date.now()-u,url:location.href,timestamp:Date.now()};e(E),t.delete(r.target)}}},{threshold:.5});function s(){let a=document.querySelectorAll(q);for(let r of a)n.observe(r)}s();let i=new MutationObserver(()=>s());i.observe(document.body,{childList:!0,subtree:!0});let o=a=>{let r=a.target?.closest?.(q);if(!r)return;let u={type:"interaction",action:"click",selector:G(r),tag:r.tagName.toLowerCase(),text:X(r),url:location.href,timestamp:Date.now()};e(u)},c=a=>{let r=a.target;if(!r?.matches?.("input,textarea,select,[data-ar-track]"))return;let u={type:"interaction",action:"input",selector:G(r),tag:r.tagName.toLowerCase(),url:location.href,timestamp:Date.now()};e(u)};return document.body.addEventListener("click",o,{passive:!0}),document.body.addEventListener("input",c,{passive:!0}),()=>{n.disconnect(),i.disconnect(),document.body.removeEventListener("click",o),document.body.removeEventListener("input",c)}}function Y(e){let t=j(e),n=V(e);return()=>{t(),n()}}var D=class{queue=[];timer=null;endpoint;siteId;sessionId;apiKey;metadata;onClassification;constructor(t){this.endpoint=t.endpoint,this.siteId=t.siteId,this.sessionId=t.sessionId,this.apiKey=t.apiKey,this.onClassification=t.onClassification}start(){this.timer=setInterval(()=>this.flush(),5e3)}stop(){this.timer&&(clearInterval(this.timer),this.timer=null)}setMetadata(t){this.metadata=t}push(t){this.queue.push(t),this.queue.length>=20&&this.flush()}flush(){if(this.queue.length===0)return;let t={siteId:this.siteId,sessionId:this.sessionId,events:this.queue.splice(0),metadata:this.metadata,timestamp:Date.now()},n=JSON.stringify(t);this.send(n)}flushBeacon(){if(this.queue.length===0)return;let t={siteId:this.siteId,sessionId:this.sessionId,events:this.queue.splice(0),metadata:this.metadata,timestamp:Date.now()},n=JSON.stringify(t),s=`${this.endpoint}?apiKey=${encodeURIComponent(this.apiKey)}`;navigator.sendBeacon(s,n)}send(t){try{fetch(this.endpoint,{method:"POST",headers:{"Content-Type":"application/json","X-Api-Key":this.apiKey},body:t,keepalive:!0}).then(n=>n.ok&&this.onClassification?n.json():null).then(n=>{n?.classifications&&this.onClassification&&this.onClassification(n.classifications)}).catch(()=>{})}catch{}}};function J(e){let t=new D(e);t.start();let n=Date.now(),s=0,i=0,o=()=>{document.visibilityState==="hidden"&&(a(),t.flushBeacon())},c=()=>{a(),t.flushBeacon()};function a(){t.push({type:"session_end",timestamp:Date.now(),url:location.href,duration:Date.now()-n,pageCount:s,interactionCount:i})}return document.addEventListener("visibilitychange",o),window.addEventListener("pagehide",c),{push:r=>{let u=r;u.type==="pageview"&&s++,u.type==="interaction"&&i++,t.push(r)},setMetadata:r=>t.setMetadata(r),flush:()=>t.flush(),destroy:()=>{t.stop(),a(),t.flush(),document.removeEventListener("visibilitychange",o),window.removeEventListener("pagehide",c)}}}function Z(e){let t=e.endpoint??"/api/v1/events",n=H(),s=null,i=null,o,c=null;function a(...m){e.debug&&console.log("[agent-radar]",...m)}function r(m,d){c=m,a(`detection [${d}]`,m),e.onDetection?.(m)}function u(){i=J({endpoint:t,siteId:e.siteId,sessionId:n,apiKey:e.apiKey,onClassification:p=>{let y=Object.values(p);if(y.length>0&&c){let h=y[y.length-1];c=U(c,h),e.onDetection?.(c)}}});let m=new S;m.start();let d=()=>m.onMouseDown(),x=()=>m.onMouseUp();document.addEventListener("mousedown",d,!0),document.addEventListener("mouseup",x,!0);let g=K();r(g,"instant"),i.push({type:"detection",score:g.score,probability:g.probability,riskTier:g.riskTier,isAgent:g.isAgent,results:g.results,stage:"instant",timestamp:Date.now(),features:R(m.getState())}),s=Y(p=>{a("event",p),i?.push(p)}),i.flush();let P=[3e3,1e4,3e4],f=["early","session","extended"],C=[];function w(p){let y=m.score(),h=W(y);r(h,p),i?.push({type:"detection",score:h.score,probability:h.probability,riskTier:h.riskTier,isAgent:h.isAgent,results:h.results,stage:p,full:!0,timestamp:Date.now(),features:R(m.getState())}),i?.flush()}for(let p=0;p<P.length;p++)C.push(setTimeout(()=>w(f[p]),P[p]));let l=setInterval(()=>{w("continuous")},15e3),v=null,k=()=>{v&&clearTimeout(v),v=setTimeout(()=>{w("interaction")},1e3)};document.addEventListener("click",k,{passive:!0}),document.addEventListener("scroll",k,{passive:!0});let I=navigator.webdriver,z=setInterval(()=>{let p=navigator.webdriver;if(p&&!I){a("webdriver state changed \u2014 re-running detection"),I=p;let y=m.score(),h=W(y);r(h,"webdriver-change"),i?.push({type:"detection",score:h.score,probability:h.probability,riskTier:h.riskTier,isAgent:h.isAgent,results:h.results,stage:"webdriver-change",full:!0,timestamp:Date.now(),features:R(m.getState())}),i?.flush()}I=p},5e3),Q=s;s=()=>{C.forEach(clearTimeout),clearInterval(l),clearInterval(z),v&&clearTimeout(v),document.removeEventListener("click",k),document.removeEventListener("scroll",k),document.removeEventListener("mousedown",d,!0),document.removeEventListener("mouseup",x,!0),m.stop(),Q?.()}}function E(m){o=m,i?.setMetadata(m),a("identify",m)}function T(){return c}function M(){s?.(),i?.destroy(),s=null,i=null}return{start:u,identify:E,getDetection:T,destroy:M}}var b=null;function bt(e){return b||(b=Z(e),b.start(),b)}function wt(e){b?.identify(e)}function Et(){return b?.getDetection()??null}return ot(xt);})();
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function z(e){let t=2166136261;for(let n=0;n<e.length;n++)t^=e.charCodeAt(n),t=t*16777619>>>0;return t.toString(36)}function $(){let e=[navigator.userAgent,`${screen.width}x${screen.height}`,Intl.DateTimeFormat().resolvedOptions().timeZone,navigator.language,new Date().toDateString()];return z(e.join("|"))}import{matchAgent as Q}from"@toffee-at/shared";function L(){let e=navigator.userAgent,t=Q(e);return t?{detector:"user-agent",rawScore:100,signals:[`matched-agent:${t.name}`]}:{detector:"user-agent",rawScore:0,signals:[]}}function _(){let e=0,t=[];navigator.webdriver===!0&&(e+=40,t.push("webdriver-flag")),navigator.plugins.length===0&&(e+=20,t.push("no-plugins")),!window.chrome&&/Chrome/.test(navigator.userAgent)&&(e+=15,t.push("chrome-without-chrome-object")),(window.outerWidth===0||window.outerHeight===0)&&(e+=15,t.push("zero-dimensions"));try{let n=document.createElement("canvas");n.getContext("webgl")||n.getContext("experimental-webgl")||(e+=10,t.push("no-webgl"))}catch{e+=10,t.push("webgl-error")}return{detector:"headless",rawScore:Math.min(e,100),signals:t}}function A(){let e=0,t=[],n=window,s=document;return(n.__playwright||Object.keys(n).some(i=>i.startsWith("__pw_")))&&(e+=35,t.push("playwright")),(n.__selenium_unwrapped||s.__selenium_evaluate||s.__webdriver_evaluate)&&(e+=35,t.push("selenium")),n.__nightmare&&(e+=15,t.push("nightmare")),navigator.webdriver&&(e+=15,t.push("webdriver")),{detector:"automation",rawScore:Math.min(e,100),signals:t}}var R=class{mousePoints=[];clicks=[];scrollTimestamps=[];wheelTimestamps=[];keyTimings=[];clickDurations=[];allEventTimestamps=[];pendingMouseDown=null;lastInputTime=0;trustedEventCount=0;untrustedEventCount=0;active=!1;onMouseMove=t=>{this.mousePoints.push({x:t.clientX,y:t.clientY,t:performance.now()}),this.mousePoints.length>500&&this.mousePoints.shift(),t.isTrusted?(this.trustedEventCount++,this.lastInputTime=performance.now()):this.untrustedEventCount++,this.allEventTimestamps.push(performance.now())};onClick=t=>{this.clicks.push({x:t.clientX,y:t.clientY,t:performance.now()}),t.isTrusted&&(this.trustedEventCount++,this.lastInputTime=performance.now()),this.allEventTimestamps.push(performance.now())};onScroll=()=>{this.scrollTimestamps.push(performance.now()),this.allEventTimestamps.push(performance.now())};onWheel=()=>{this.wheelTimestamps.push(performance.now()),this.lastInputTime=performance.now()};onKeyDown=t=>{this.keyTimings.push(performance.now()),t.isTrusted&&(this.trustedEventCount++,this.lastInputTime=performance.now()),this.allEventTimestamps.push(performance.now())};onMouseDown(){this.pendingMouseDown=performance.now(),this.allEventTimestamps.push(this.pendingMouseDown)}onMouseUp(){let t=performance.now();this.pendingMouseDown!==null&&(this.clickDurations.push(t-this.pendingMouseDown),this.pendingMouseDown=null),this.allEventTimestamps.push(t)}getState(){return{mousePoints:this.mousePoints,clicks:this.clicks,clickDurations:this.clickDurations,scrollTimestamps:this.scrollTimestamps,keyTimings:this.keyTimings,allEventTimestamps:this.allEventTimestamps}}start(){this.active||(this.active=!0,document.addEventListener("mousemove",this.onMouseMove,{passive:!0}),document.addEventListener("click",this.onClick,{passive:!0}),document.addEventListener("scroll",this.onScroll,{passive:!0}),document.addEventListener("wheel",this.onWheel,{passive:!0}),document.addEventListener("keydown",this.onKeyDown,{passive:!0}))}stop(){this.active&&(this.active=!1,document.removeEventListener("mousemove",this.onMouseMove),document.removeEventListener("click",this.onClick),document.removeEventListener("scroll",this.onScroll),document.removeEventListener("wheel",this.onWheel),document.removeEventListener("keydown",this.onKeyDown))}score(){let t=0,n=[];if(this.mousePoints.length===0&&(t+=25,n.push("no-mouse-events")),this.scrollTimestamps.length===0&&(t+=10,n.push("no-scroll-events")),this.keyTimings.length===0&&(t+=5,n.push("no-keyboard-events")),this.untrustedEventCount>0&&this.trustedEventCount===0&&(t+=15,n.push("all-untrusted-events")),this.mousePoints.length>=3){let s=tt(this.mousePoints);s<.5?(t+=20,n.push(`low-trajectory-entropy:${s.toFixed(2)}`)):s<1.2&&(t+=10,n.push(`medium-trajectory-entropy:${s.toFixed(2)}`));let i=et(this.mousePoints);i>.95?(t+=15,n.push(`high-movement-efficiency:${i.toFixed(3)}`)):i>.9&&(t+=8,n.push(`elevated-movement-efficiency:${i.toFixed(3)}`)),!nt(this.mousePoints)&&this.mousePoints.length>=3&&(t+=15,n.push("no-micro-jitter"));let o=it(this.mousePoints);o>0&&(t+=Math.min(o*10,20),n.push(`mouse-teleportation:${o}`));let c=this.mousePoints[this.mousePoints.length-1].t-this.mousePoints[0].t;if(c>500){let a=this.mousePoints.length/c*1e3;a<1?(t+=25,n.push(`very-sparse-mouse-events:${a.toFixed(2)}/s`)):a<5&&(t+=15,n.push(`sparse-mouse-events:${a.toFixed(1)}/s`))}if(this.clicks.length>0){let a=this.mousePoints.length/this.clicks.length;a<3&&(t+=15,n.push(`low-points-per-click:${a.toFixed(1)}`))}}if(this.clicks.length>0&&this.mousePoints.length>0){let s=st(this.mousePoints,this.clicks);s.teleportClicks>0&&(t+=Math.min(s.teleportClicks*15,25),n.push(`teleport-clicks:${s.teleportClicks}/${this.clicks.length}`)),s.avgApproachPath<10&&this.clicks.length>=1&&(t+=15,n.push(`minimal-approach-path:${s.avgApproachPath.toFixed(1)}px`))}if(this.scrollTimestamps.length>=3){let s=0;for(let i of this.scrollTimestamps)this.wheelTimestamps.some(c=>Math.abs(c-i)<100)||s++;s>this.scrollTimestamps.length*.7&&(t+=10,n.push("programmatic-scrolling"))}if(this.keyTimings.length>=5){let s=[];for(let a=1;a<this.keyTimings.length;a++)s.push(this.keyTimings[a]-this.keyTimings[a-1]);let i=s.reduce((a,r)=>a+r,0)/s.length,o=s.reduce((a,r)=>a+(r-i)**2,0)/s.length,c=Math.sqrt(o)/i;c<.15&&(t+=10,n.push(`uniform-keystroke-timing:cv=${c.toFixed(3)}`))}return{detector:"behavioral",rawScore:Math.min(t,100),signals:n}}};function tt(e){if(e.length<3)return 3;let t=8,n=new Array(t).fill(0);for(let o=1;o<e.length;o++){let c=e[o].x-e[o-1].x,a=e[o].y-e[o-1].y;if(c===0&&a===0)continue;let r=Math.atan2(a,c);r<0&&(r+=2*Math.PI),n[Math.floor(r/(2*Math.PI)*t)%t]++}let s=n.reduce((o,c)=>o+c,0);if(s===0)return 3;let i=0;for(let o of n){if(o===0)continue;let c=o/s;i-=c*Math.log2(c)}return i}function et(e){if(e.length<2)return 0;let t=[],n=[e[0]];for(let i=1;i<e.length;i++)e[i].t-e[i-1].t>200?(n.length>=2&&t.push(n),n=[e[i]]):n.push(e[i]);if(n.length>=2&&t.push(n),t.length===0)return 0;let s=[];for(let i of t){let o=i[0],c=i[i.length-1],a=Math.sqrt((c.x-o.x)**2+(c.y-o.y)**2),r=0;for(let u=1;u<i.length;u++)r+=Math.sqrt((i[u].x-i[u-1].x)**2+(i[u].y-i[u-1].y)**2);r>5&&s.push(a/r)}return s.length===0?0:s.reduce((i,o)=>i+o,0)/s.length}function nt(e){if(e.length<10)return!0;let t=0,n=0;for(let i=2;i<e.length;i++){let o=e[i-1].x-e[i-2].x,c=e[i-1].y-e[i-2].y,a=e[i].x-e[i-1].x,r=e[i].y-e[i-1].y,u=Math.sqrt(a*a+r*r);u>0&&u<5&&(n++,(o*a<0||c*r<0)&&t++)}return(n>0?t/n:0)>.15||n>e.length*.1}function it(e){let t=0;for(let n=1;n<e.length;n++){let s=e[n].x-e[n-1].x,i=e[n].y-e[n-1].y;Math.sqrt(s*s+i*i)>200&&t++}return t}function st(e,t){let n=0,s=0;for(let i of t){let o=e.filter(a=>a.t>=i.t-500&&a.t<=i.t);if(o.length<=1){n++;continue}let c=0;for(let a=1;a<o.length;a++)c+=Math.sqrt((o[a].x-o[a-1].x)**2+(o[a].y-o[a-1].y)**2);s+=c,c<10&&n++}return{teleportClicks:n,avgApproachPath:t.length>0?s/t.length:0}}function O(){let e=0,t=[],n=navigator.userAgent;try{let s=navigator.userAgentData;if(s){let i=s.platform?.toLowerCase()||"",o=/Macintosh|Mac OS X/i.test(n),c=/Windows/i.test(n),a=/Linux/i.test(n)&&!/Android/i.test(n);(o&&i&&i!=="macos"&&i!=="mac os x"||c&&i&&i!=="windows"||a&&i&&i!=="linux")&&(e+=30,t.push("client-hints-platform-mismatch"))}else/Chrome\/\d/.test(n)&&parseInt(n.match(/Chrome\/(\d+)/)?.[1]||"0")>=90&&(e+=15,t.push("missing-user-agent-data"))}catch{}navigator.hardwareConcurrency!==void 0&&navigator.hardwareConcurrency<=1&&(e+=15,t.push("low-hardware-concurrency")),/Chrome/.test(n)&&navigator.deviceMemory===void 0&&(e+=10,t.push("missing-device-memory")),/Chrome/.test(n)&&!navigator.connection&&(e+=10,t.push("missing-connection-api"));try{let s=navigator.platform?.toLowerCase()||"",i=/Macintosh|Mac OS X/i.test(n),o=/Windows/i.test(n);(i&&s&&!s.includes("mac")||o&&s&&!s.includes("win"))&&(e+=25,t.push("platform-ua-mismatch"))}catch{}return window.outerHeight>0&&window.innerHeight>0&&window.outerHeight===window.innerHeight&&(e+=10,t.push("no-browser-chrome")),screen.width===screen.availWidth&&screen.height===screen.availHeight&&screen.width>0&&(e+=5,t.push("no-taskbar")),/Mobile|Android|iPhone/i.test(n)&&window.devicePixelRatio===1&&(e+=15,t.push("mobile-ua-low-dpr")),{detector:"navigator",rawScore:Math.min(e,100),signals:t}}function W(){let e=0,t=[];try{let n=document.createElement("canvas"),s=n.getContext("webgl")||n.getContext("experimental-webgl");if(s){let i=s.getExtension("WEBGL_debug_renderer_info");if(i){let c=s.getParameter(i.UNMASKED_RENDERER_WEBGL),a=s.getParameter(i.UNMASKED_VENDOR_WEBGL);/swiftshader|mesa offscreen|llvmpipe|softpipe|software rasterizer/i.test(c)&&(e+=35,t.push(`software-renderer:${c.toLowerCase().replace(/\s+/g,"-")}`));let u=s.getSupportedExtensions();u&&u.length<15&&(e+=15,t.push("low-webgl-extensions")),/google/i.test(a)&&/swiftshader/i.test(c)&&(e+=15,t.push("google-swiftshader"))}else e+=10,t.push("no-webgl-debug-info");let o=s.getParameter(s.MAX_TEXTURE_SIZE);o&&o<4096&&(e+=10,t.push("low-max-texture"))}}catch{}try{let n=document.createElement("canvas");n.width=200,n.height=50;let s=n.getContext("2d");if(s){let i=r=>{r.fillStyle="#f60",r.fillRect(10,1,62,20),r.fillStyle="#069",r.font="11pt Arial",r.fillText("Agent Radar \u{1F50D}",2,15),r.fillStyle="rgba(102, 204, 0, 0.7)",r.fillRect(50,25,40,15)};i(s);let o=n.toDataURL(),c=document.createElement("canvas");c.width=200,c.height=50;let a=c.getContext("2d");if(a){i(a);let r=c.toDataURL();o!==r&&(e+=25,t.push("canvas-tampered"));let u=document.createElement("canvas");u.width=200,u.height=50,o===u.toDataURL()&&(e+=20,t.push("canvas-blank"))}}}catch{e+=10,t.push("canvas-error")}return{detector:"fingerprint",rawScore:Math.min(e,100),signals:t}}var ot={"user-agent":[-.1,5],headless:[-.2,4],automation:[-.1,5],navigator:[-.1,3.5],fingerprint:[-.1,3.5],behavioral:[-2.5,6]};function rt(e,t){let[n,s]=ot[e]??[-.5,4];return n+t/100*(s-n)}function F(e){let n=Math.log(.17647058823529413),s=e.map(l=>{let v=rt(l.detector,l.rawScore);return n+=v,{detector:l.detector,fired:l.rawScore>0,llr:v,rawScore:l.rawScore,evidence:l.signals}}),i=1/(1+Math.exp(-n)),o=e.flatMap(l=>l.signals),c=o.includes("webdriver-flag")||o.includes("webdriver"),a=o.includes("no-mouse-events"),r=o.includes("no-scroll-events");c&&a?i=Math.max(i,.9):c&&r&&(i=Math.max(i,.8));let u=o.some(l=>l.startsWith("teleport-clicks:")),E=o.some(l=>l.startsWith("minimal-approach-path:")),x=o.some(l=>l.startsWith("low-trajectory-entropy:")),M=o.some(l=>l.startsWith("medium-trajectory-entropy:")),m=o.includes("no-micro-jitter"),d=o.some(l=>l.startsWith("very-sparse-mouse-events:")||l.startsWith("sparse-mouse-events:")),T=o.some(l=>l.startsWith("mouse-teleportation:")),f=o.some(l=>l.startsWith("low-points-per-click:"));T&&d&&(i=Math.max(i,.85)),u&&m&&(i=Math.max(i,.85)),u&&(x||M)&&(i=Math.max(i,.8)),E&&m&&(i=Math.max(i,.8)),d&&(x||M)&&T&&(i=Math.max(i,.9)),f&&d&&(i=Math.max(i,.85)),o.some(l=>l.startsWith("software-renderer:"))&&(i=Math.max(i,.8)),i=Math.max(0,Math.min(1,i));let g;i>=.95?g="definite-bot":i>=.8?g="likely-bot":i>=.5?g="suspicious":i>=.2?g="likely-human":g="definite-human";let k=Math.round(i*100),w=i>=.5;return{score:k,probability:i,riskTier:g,isAgent:w,results:e,signals:s,classification:{classification:w?"bot":"human",probabilities:{human:1-i,bot:i,agent:0},source:"heuristic"}}}function G(){let e=[L(),_(),A(),O(),W()];return F(e)}function K(e,t){let n=1-t.probabilities.human,s;return n>=.95?s="definite-bot":n>=.8?s="likely-bot":n>=.5?s="suspicious":n>=.2?s="likely-human":s="definite-human",{...e,probability:n,riskTier:s,isAgent:t.classification!=="human",classification:{classification:t.classification,probabilities:t.probabilities,source:"model"}}}function N(e){let t=[L(),_(),A(),O(),W()];return F([...t,e])}function at(e,t,n){let s=[...e,...t,...n].sort((o,c)=>o-c);if(s.length<2)return 0;let i=0;for(let o=1;o<s.length;o++)s[o]-s[o-1]<1e3&&i++;return i/(s.length-1)}function ct(e){if(e.length<2)return 0;let t=e.reduce((s,i)=>s+i,0)/e.length,n=e.reduce((s,i)=>s+(i-t)**2,0)/e.length;return Math.sqrt(n)}function ut(e){if(e.length<2)return 0;let t=[...e].sort((r,u)=>r-u),n=[];for(let r=1;r<t.length;r++)n.push(t[r]-t[r-1]);if(n.length===0)return 0;let s=Math.max(...n),i=Math.min(...n);if(s===i)return 0;let o=(s-i)/20,c=new Array(20).fill(0);for(let r of n){let u=Math.min(Math.floor((r-i)/o),19);c[u]++}let a=0;for(let r of c)if(r>0){let u=r/n.length;a-=u*Math.log2(u)}return a}function lt(e){let t=0;for(let n=1;n<e.length;n++){let s=e[n].x-e[n-1].x,i=e[n].y-e[n-1].y;Math.sqrt(s*s+i*i)>100&&t++}return t}function C(e){return{action_burst_ratio:at(e.clicks.map(t=>t.t),e.scrollTimestamps,e.keyTimings),click_duration_std:ct(e.clickDurations),inter_event_entropy:ut(e.allEventTimestamps),mouse_teleportation:lt(e.mousePoints)}}function H(e){let t=Date.now();function n(){let c={type:"pageview",url:location.href,referrer:document.referrer,timestamp:Date.now()};t=Date.now(),e(c)}n();let s=()=>n();window.addEventListener("popstate",s);let i=history.pushState.bind(history),o=history.replaceState.bind(history);return history.pushState=function(...c){i(...c),n()},history.replaceState=function(...c){o(...c),n()},()=>{window.removeEventListener("popstate",s),history.pushState=i,history.replaceState=o}}var j="h1,h2,h3,h4,h5,h6,p,a,button,form,input,img,[data-ar-track]";function B(e){if(e.id)return`#${e.id}`;let t=e.getAttribute("data-ar-track");if(t)return`[data-ar-track="${t}"]`;let n=e.tagName.toLowerCase(),s=e.className&&typeof e.className=="string"?`.${e.className.trim().split(/\s+/).slice(0,2).join(".")}`:"";return`${n}${s}`}function q(e){return e.textContent?.trim().slice(0,100)||void 0}function U(e){let t=new Map,n=new IntersectionObserver(a=>{for(let r of a)if(r.isIntersecting)t.set(r.target,Date.now());else{let u=t.get(r.target);if(u){let E={type:"interaction",action:"view",selector:B(r.target),tag:r.target.tagName.toLowerCase(),text:q(r.target),dwellMs:Date.now()-u,url:location.href,timestamp:Date.now()};e(E),t.delete(r.target)}}},{threshold:.5});function s(){let a=document.querySelectorAll(j);for(let r of a)n.observe(r)}s();let i=new MutationObserver(()=>s());i.observe(document.body,{childList:!0,subtree:!0});let o=a=>{let r=a.target?.closest?.(j);if(!r)return;let u={type:"interaction",action:"click",selector:B(r),tag:r.tagName.toLowerCase(),text:q(r),url:location.href,timestamp:Date.now()};e(u)},c=a=>{let r=a.target;if(!r?.matches?.("input,textarea,select,[data-ar-track]"))return;let u={type:"interaction",action:"input",selector:B(r),tag:r.tagName.toLowerCase(),url:location.href,timestamp:Date.now()};e(u)};return document.body.addEventListener("click",o,{passive:!0}),document.body.addEventListener("input",c,{passive:!0}),()=>{n.disconnect(),i.disconnect(),document.body.removeEventListener("click",o),document.body.removeEventListener("input",c)}}function X(e){let t=H(e),n=U(e);return()=>{t(),n()}}var S=class{queue=[];timer=null;endpoint;siteId;sessionId;apiKey;metadata;onClassification;constructor(t){this.endpoint=t.endpoint,this.siteId=t.siteId,this.sessionId=t.sessionId,this.apiKey=t.apiKey,this.onClassification=t.onClassification}start(){this.timer=setInterval(()=>this.flush(),5e3)}stop(){this.timer&&(clearInterval(this.timer),this.timer=null)}setMetadata(t){this.metadata=t}push(t){this.queue.push(t),this.queue.length>=20&&this.flush()}flush(){if(this.queue.length===0)return;let t={siteId:this.siteId,sessionId:this.sessionId,events:this.queue.splice(0),metadata:this.metadata,timestamp:Date.now()},n=JSON.stringify(t);this.send(n)}flushBeacon(){if(this.queue.length===0)return;let t={siteId:this.siteId,sessionId:this.sessionId,events:this.queue.splice(0),metadata:this.metadata,timestamp:Date.now()},n=JSON.stringify(t),s=`${this.endpoint}?apiKey=${encodeURIComponent(this.apiKey)}`;navigator.sendBeacon(s,n)}send(t){try{fetch(this.endpoint,{method:"POST",headers:{"Content-Type":"application/json","X-Api-Key":this.apiKey},body:t,keepalive:!0}).then(n=>n.ok&&this.onClassification?n.json():null).then(n=>{n?.classifications&&this.onClassification&&this.onClassification(n.classifications)}).catch(()=>{})}catch{}}};function Y(e){let t=new S(e);t.start();let n=Date.now(),s=0,i=0,o=()=>{document.visibilityState==="hidden"&&(a(),t.flushBeacon())},c=()=>{a(),t.flushBeacon()};function a(){t.push({type:"session_end",timestamp:Date.now(),url:location.href,duration:Date.now()-n,pageCount:s,interactionCount:i})}return document.addEventListener("visibilitychange",o),window.addEventListener("pagehide",c),{push:r=>{let u=r;u.type==="pageview"&&s++,u.type==="interaction"&&i++,t.push(r)},setMetadata:r=>t.setMetadata(r),flush:()=>t.flush(),destroy:()=>{t.stop(),a(),t.flush(),document.removeEventListener("visibilitychange",o),window.removeEventListener("pagehide",c)}}}function Z(e){let t=e.endpoint??"/api/v1/events",n=$(),s=null,i=null,o,c=null;function a(...m){e.debug&&console.log("[agent-radar]",...m)}function r(m,d){c=m,a(`detection [${d}]`,m),e.onDetection?.(m)}function u(){i=Y({endpoint:t,siteId:e.siteId,sessionId:n,apiKey:e.apiKey,onClassification:h=>{let y=Object.values(h);if(y.length>0&&c){let p=y[y.length-1];c=K(c,p),e.onDetection?.(c)}}});let m=new R;m.start();let d=()=>m.onMouseDown(),T=()=>m.onMouseUp();document.addEventListener("mousedown",d,!0),document.addEventListener("mouseup",T,!0);let f=G();r(f,"instant"),i.push({type:"detection",score:f.score,probability:f.probability,riskTier:f.riskTier,isAgent:f.isAgent,results:f.results,stage:"instant",timestamp:Date.now(),features:C(m.getState())}),s=X(h=>{a("event",h),i?.push(h)}),i.flush();let P=[3e3,1e4,3e4],g=["early","session","extended"],k=[];function w(h){let y=m.score(),p=N(y);r(p,h),i?.push({type:"detection",score:p.score,probability:p.probability,riskTier:p.riskTier,isAgent:p.isAgent,results:p.results,stage:h,full:!0,timestamp:Date.now(),features:C(m.getState())}),i?.flush()}for(let h=0;h<P.length;h++)k.push(setTimeout(()=>w(g[h]),P[h]));let l=setInterval(()=>{w("continuous")},15e3),v=null,D=()=>{v&&clearTimeout(v),v=setTimeout(()=>{w("interaction")},1e3)};document.addEventListener("click",D,{passive:!0}),document.addEventListener("scroll",D,{passive:!0});let I=navigator.webdriver,J=setInterval(()=>{let h=navigator.webdriver;if(h&&!I){a("webdriver state changed \u2014 re-running detection"),I=h;let y=m.score(),p=N(y);r(p,"webdriver-change"),i?.push({type:"detection",score:p.score,probability:p.probability,riskTier:p.riskTier,isAgent:p.isAgent,results:p.results,stage:"webdriver-change",full:!0,timestamp:Date.now(),features:C(m.getState())}),i?.flush()}I=h},5e3),V=s;s=()=>{k.forEach(clearTimeout),clearInterval(l),clearInterval(J),v&&clearTimeout(v),document.removeEventListener("click",D),document.removeEventListener("scroll",D),document.removeEventListener("mousedown",d,!0),document.removeEventListener("mouseup",T,!0),m.stop(),V?.()}}function E(m){o=m,i?.setMetadata(m),a("identify",m)}function x(){return c}function M(){s?.(),i?.destroy(),s=null,i=null}return{start:u,identify:E,getDetection:x,destroy:M}}var b=null;function Ut(e){return b||(b=Z(e),b.start(),b)}function Xt(e){b?.identify(e)}function Yt(){return b?.getDetection()??null}export{Yt as getDetection,Xt as identify,Ut as init};
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@toffee-at/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/index.cjs"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup",
|
|
20
|
+
"dev": "tsup --watch",
|
|
21
|
+
"clean": "rm -rf dist"
|
|
22
|
+
},
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@toffee-at/shared": "workspace:*"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"tsup": "^8.0.0",
|
|
28
|
+
"typescript": "^5.7.0",
|
|
29
|
+
"vitest": "^4.1.0"
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/core.ts
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { createSessionId } from './track/session';
|
|
2
|
+
import { runSync, runFull, runWithBehavioral, applyModelClassification } from './detect/index';
|
|
3
|
+
import { BehavioralMonitor } from './detect/behavioral';
|
|
4
|
+
import { extractModelFeatures } from './detect/features';
|
|
5
|
+
import type { DetectionOutput } from './detect/index';
|
|
6
|
+
import { setupTracking } from './track/index';
|
|
7
|
+
import { createTransport } from './transport/index';
|
|
8
|
+
|
|
9
|
+
export interface AgentRadarConfig {
|
|
10
|
+
siteId: string;
|
|
11
|
+
apiKey: string;
|
|
12
|
+
endpoint?: string; // default '/api/v1/events'
|
|
13
|
+
debug?: boolean;
|
|
14
|
+
onDetection?: (result: DetectionOutput) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function createCore(config: AgentRadarConfig) {
|
|
18
|
+
const endpoint = config.endpoint ?? '/api/v1/events';
|
|
19
|
+
const sessionId = createSessionId();
|
|
20
|
+
let destroyTracking: (() => void) | null = null;
|
|
21
|
+
let transport: ReturnType<typeof createTransport> | null = null;
|
|
22
|
+
let customMetadata: Record<string, string> | undefined;
|
|
23
|
+
let latestDetection: DetectionOutput | null = null;
|
|
24
|
+
|
|
25
|
+
function log(...args: unknown[]) {
|
|
26
|
+
if (config.debug) {
|
|
27
|
+
console.log('[agent-radar]', ...args);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function emitDetection(result: DetectionOutput, stage: string) {
|
|
32
|
+
latestDetection = result;
|
|
33
|
+
log(`detection [${stage}]`, result);
|
|
34
|
+
config.onDetection?.(result);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function start() {
|
|
38
|
+
// Set up transport
|
|
39
|
+
transport = createTransport({
|
|
40
|
+
endpoint,
|
|
41
|
+
siteId: config.siteId,
|
|
42
|
+
sessionId,
|
|
43
|
+
apiKey: config.apiKey,
|
|
44
|
+
onClassification: (classifications) => {
|
|
45
|
+
const entries = Object.values(classifications);
|
|
46
|
+
if (entries.length > 0 && latestDetection) {
|
|
47
|
+
const latest = entries[entries.length - 1];
|
|
48
|
+
latestDetection = applyModelClassification(latestDetection, latest);
|
|
49
|
+
config.onDetection?.(latestDetection);
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Start persistent behavioral monitor immediately
|
|
55
|
+
const behaviorMonitor = new BehavioralMonitor();
|
|
56
|
+
behaviorMonitor.start();
|
|
57
|
+
|
|
58
|
+
// Register mousedown/mouseup for click duration tracking
|
|
59
|
+
const onMouseDown = () => behaviorMonitor.onMouseDown();
|
|
60
|
+
const onMouseUp = () => behaviorMonitor.onMouseUp();
|
|
61
|
+
document.addEventListener('mousedown', onMouseDown, true);
|
|
62
|
+
document.addEventListener('mouseup', onMouseUp, true);
|
|
63
|
+
|
|
64
|
+
// Phase 1: Instant sync detection (no behavioral data yet)
|
|
65
|
+
const syncResult = runSync();
|
|
66
|
+
emitDetection(syncResult, 'instant');
|
|
67
|
+
|
|
68
|
+
transport.push({
|
|
69
|
+
type: 'detection',
|
|
70
|
+
score: syncResult.score,
|
|
71
|
+
probability: syncResult.probability,
|
|
72
|
+
riskTier: syncResult.riskTier,
|
|
73
|
+
isAgent: syncResult.isAgent,
|
|
74
|
+
results: syncResult.results,
|
|
75
|
+
stage: 'instant',
|
|
76
|
+
timestamp: Date.now(),
|
|
77
|
+
features: extractModelFeatures(behaviorMonitor.getState()),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Start tracking (page + elements)
|
|
81
|
+
destroyTracking = setupTracking((event) => {
|
|
82
|
+
log('event', event);
|
|
83
|
+
transport?.push(event);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Flush the initial detection + pageview immediately
|
|
87
|
+
transport.flush();
|
|
88
|
+
|
|
89
|
+
// Progressive detection: re-score at fixed intervals AND after interactions
|
|
90
|
+
// This handles both fast bots (caught early) and slow bots like AI extensions
|
|
91
|
+
// (whose interactions arrive seconds/minutes after page load)
|
|
92
|
+
const SCORING_STAGES = [3000, 10000, 30000]; // early, session, extended
|
|
93
|
+
const stageNames = ['early', 'session', 'extended'];
|
|
94
|
+
const stageTimers: ReturnType<typeof setTimeout>[] = [];
|
|
95
|
+
|
|
96
|
+
function rescoreAndEmit(stage: string) {
|
|
97
|
+
const behavioral = behaviorMonitor.score();
|
|
98
|
+
const result = runWithBehavioral(behavioral);
|
|
99
|
+
emitDetection(result, stage);
|
|
100
|
+
transport?.push({
|
|
101
|
+
type: 'detection',
|
|
102
|
+
score: result.score,
|
|
103
|
+
probability: result.probability,
|
|
104
|
+
riskTier: result.riskTier,
|
|
105
|
+
isAgent: result.isAgent,
|
|
106
|
+
results: result.results,
|
|
107
|
+
stage,
|
|
108
|
+
full: true,
|
|
109
|
+
timestamp: Date.now(),
|
|
110
|
+
features: extractModelFeatures(behaviorMonitor.getState()),
|
|
111
|
+
});
|
|
112
|
+
transport?.flush();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
for (let i = 0; i < SCORING_STAGES.length; i++) {
|
|
116
|
+
stageTimers.push(setTimeout(() => rescoreAndEmit(stageNames[i]), SCORING_STAGES[i]));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Continuous re-scoring: every 15 seconds, re-evaluate with latest behavioral data
|
|
120
|
+
// This catches extension-based agents whose interactions arrive late
|
|
121
|
+
const continuousInterval = setInterval(() => {
|
|
122
|
+
rescoreAndEmit('continuous');
|
|
123
|
+
}, 15000);
|
|
124
|
+
|
|
125
|
+
// Also re-score on meaningful interactions (click/scroll) with a debounce
|
|
126
|
+
let interactionDebounce: ReturnType<typeof setTimeout> | null = null;
|
|
127
|
+
const onInteraction = () => {
|
|
128
|
+
if (interactionDebounce) clearTimeout(interactionDebounce);
|
|
129
|
+
interactionDebounce = setTimeout(() => {
|
|
130
|
+
rescoreAndEmit('interaction');
|
|
131
|
+
}, 1000); // 1 second after last interaction
|
|
132
|
+
};
|
|
133
|
+
document.addEventListener('click', onInteraction, { passive: true });
|
|
134
|
+
document.addEventListener('scroll', onInteraction, { passive: true });
|
|
135
|
+
|
|
136
|
+
// Re-run detection if webdriver state changes (mid-session agent takeover)
|
|
137
|
+
let lastWebdriver = navigator.webdriver;
|
|
138
|
+
const redetectInterval = setInterval(() => {
|
|
139
|
+
const currentWebdriver = navigator.webdriver;
|
|
140
|
+
if (currentWebdriver && !lastWebdriver) {
|
|
141
|
+
log('webdriver state changed — re-running detection');
|
|
142
|
+
lastWebdriver = currentWebdriver;
|
|
143
|
+
const behavioral = behaviorMonitor.score();
|
|
144
|
+
const result = runWithBehavioral(behavioral);
|
|
145
|
+
emitDetection(result, 'webdriver-change');
|
|
146
|
+
transport?.push({
|
|
147
|
+
type: 'detection',
|
|
148
|
+
score: result.score,
|
|
149
|
+
probability: result.probability,
|
|
150
|
+
riskTier: result.riskTier,
|
|
151
|
+
isAgent: result.isAgent,
|
|
152
|
+
results: result.results,
|
|
153
|
+
stage: 'webdriver-change',
|
|
154
|
+
full: true,
|
|
155
|
+
timestamp: Date.now(),
|
|
156
|
+
features: extractModelFeatures(behaviorMonitor.getState()),
|
|
157
|
+
});
|
|
158
|
+
transport?.flush();
|
|
159
|
+
}
|
|
160
|
+
lastWebdriver = currentWebdriver;
|
|
161
|
+
}, 5000);
|
|
162
|
+
|
|
163
|
+
const origDestroy = destroyTracking;
|
|
164
|
+
destroyTracking = () => {
|
|
165
|
+
stageTimers.forEach(clearTimeout);
|
|
166
|
+
clearInterval(continuousInterval);
|
|
167
|
+
clearInterval(redetectInterval);
|
|
168
|
+
if (interactionDebounce) clearTimeout(interactionDebounce);
|
|
169
|
+
document.removeEventListener('click', onInteraction);
|
|
170
|
+
document.removeEventListener('scroll', onInteraction);
|
|
171
|
+
document.removeEventListener('mousedown', onMouseDown, true);
|
|
172
|
+
document.removeEventListener('mouseup', onMouseUp, true);
|
|
173
|
+
behaviorMonitor.stop();
|
|
174
|
+
origDestroy?.();
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function identify(metadata: Record<string, string>) {
|
|
179
|
+
customMetadata = metadata;
|
|
180
|
+
transport?.setMetadata(metadata);
|
|
181
|
+
log('identify', metadata);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function getDetection(): DetectionOutput | null {
|
|
185
|
+
return latestDetection;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function destroy() {
|
|
189
|
+
destroyTracking?.();
|
|
190
|
+
transport?.destroy();
|
|
191
|
+
destroyTracking = null;
|
|
192
|
+
transport = null;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return { start, identify, getDetection, destroy };
|
|
196
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { DetectorResult } from './types';
|
|
2
|
+
|
|
3
|
+
export function detectAutomation(): DetectorResult {
|
|
4
|
+
let score = 0;
|
|
5
|
+
const signals: string[] = [];
|
|
6
|
+
const win = window as any;
|
|
7
|
+
const doc = document as any;
|
|
8
|
+
|
|
9
|
+
if (win.__playwright || Object.keys(win).some((k) => k.startsWith('__pw_'))) {
|
|
10
|
+
score += 35;
|
|
11
|
+
signals.push('playwright');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (win.__selenium_unwrapped || doc.__selenium_evaluate || doc.__webdriver_evaluate) {
|
|
15
|
+
score += 35;
|
|
16
|
+
signals.push('selenium');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (win.__nightmare) {
|
|
20
|
+
score += 15;
|
|
21
|
+
signals.push('nightmare');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (navigator.webdriver) {
|
|
25
|
+
score += 15;
|
|
26
|
+
signals.push('webdriver');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
detector: 'automation',
|
|
31
|
+
rawScore: Math.min(score, 100),
|
|
32
|
+
signals,
|
|
33
|
+
};
|
|
34
|
+
}
|