@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.
@@ -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});
@@ -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,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
+ }