@agentstats/sdk 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bots.cjs ADDED
@@ -0,0 +1,1022 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ // src/bots.curated.ts
3
+ const CURATED = [
4
+ {
5
+ id: "chatgpt-agent",
6
+ name: "ChatGPT agent",
7
+ product: "chatgpt",
8
+ category: "agent",
9
+ pattern: /ChatGPT[- ]Agent/i
10
+ },
11
+ {
12
+ id: "chatgpt-user",
13
+ name: "ChatGPT-User",
14
+ product: "chatgpt",
15
+ category: "assistant",
16
+ pattern: /ChatGPT-User/i
17
+ },
18
+ {
19
+ id: "oai-searchbot",
20
+ name: "OAI-SearchBot",
21
+ product: "chatgpt",
22
+ category: "search",
23
+ pattern: /OAI-SearchBot/i
24
+ },
25
+ {
26
+ id: "gptbot",
27
+ name: "GPTBot",
28
+ product: "chatgpt",
29
+ category: "training",
30
+ pattern: /GPTBot/i
31
+ },
32
+ {
33
+ id: "oai-adsbot",
34
+ name: "OAI-AdsBot",
35
+ product: "chatgpt",
36
+ category: "other",
37
+ pattern: /OAI-AdsBot/i
38
+ },
39
+ {
40
+ id: "claude-code",
41
+ name: "Claude Code",
42
+ product: "claude",
43
+ category: "assistant",
44
+ pattern: /claude-code/i
45
+ },
46
+ {
47
+ id: "claude-user",
48
+ name: "Claude-User",
49
+ product: "claude",
50
+ category: "assistant",
51
+ pattern: /Claude-User/i
52
+ },
53
+ {
54
+ id: "claude-searchbot",
55
+ name: "Claude-SearchBot",
56
+ product: "claude",
57
+ category: "search",
58
+ pattern: /Claude-SearchBot/i
59
+ },
60
+ {
61
+ id: "claudebot",
62
+ name: "ClaudeBot",
63
+ product: "claude",
64
+ category: "training",
65
+ pattern: /ClaudeBot|Claude-Web|anthropic-ai/i
66
+ },
67
+ {
68
+ id: "perplexity-user",
69
+ name: "Perplexity-User",
70
+ product: "perplexity",
71
+ category: "assistant",
72
+ pattern: /Perplexity-?User/i
73
+ },
74
+ {
75
+ id: "perplexitybot",
76
+ name: "PerplexityBot",
77
+ product: "perplexity",
78
+ category: "search",
79
+ pattern: /PerplexityBot/i
80
+ },
81
+ {
82
+ id: "google-agent",
83
+ name: "Google-Agent",
84
+ product: "gemini",
85
+ category: "agent",
86
+ pattern: /Google-Agent\b/i
87
+ },
88
+ {
89
+ id: "googleagent-mariner",
90
+ name: "Project Mariner",
91
+ product: "gemini",
92
+ category: "agent",
93
+ pattern: /GoogleAgent-Mariner/i
94
+ },
95
+ {
96
+ id: "googleagent-urlcontext",
97
+ name: "Gemini URL context",
98
+ product: "gemini",
99
+ category: "assistant",
100
+ pattern: /GoogleAgent-URLContext/i
101
+ },
102
+ {
103
+ id: "gemini-deep-research",
104
+ name: "Gemini Deep Research",
105
+ product: "gemini",
106
+ category: "assistant",
107
+ pattern: /Gemini-Deep-Research/i
108
+ },
109
+ {
110
+ id: "google-gemininotebook",
111
+ name: "Gemini Notebook",
112
+ product: "gemini",
113
+ category: "assistant",
114
+ pattern: /Google-GeminiNotebook|Google-NotebookLM|NotebookLM/i
115
+ },
116
+ {
117
+ id: "gemini-cli",
118
+ name: "Gemini CLI",
119
+ product: "gemini",
120
+ category: "assistant",
121
+ pattern: /Gemini-?CLI/i
122
+ },
123
+ {
124
+ id: "google-cloudvertexbot",
125
+ name: "Google-CloudVertexBot",
126
+ product: "gemini",
127
+ category: "search",
128
+ pattern: /Google-CloudVertexBot/i
129
+ },
130
+ {
131
+ id: "google-read-aloud",
132
+ name: "Google Read Aloud",
133
+ product: "google",
134
+ category: "other",
135
+ pattern: /Google-Read-Aloud/i
136
+ },
137
+ {
138
+ id: "google-ads",
139
+ name: "Google Ads crawlers",
140
+ product: "google",
141
+ category: "other",
142
+ pattern: /AdsBot-Google|Mediapartners-Google|APIs-Google/i
143
+ },
144
+ {
145
+ id: "googleother",
146
+ name: "GoogleOther",
147
+ product: "google",
148
+ category: "other",
149
+ pattern: /GoogleOther|Google-InspectionTool|Storebot-Google/i
150
+ },
151
+ {
152
+ id: "googlebot",
153
+ name: "Googlebot",
154
+ product: "google",
155
+ category: "search_engine",
156
+ pattern: /Googlebot/i
157
+ },
158
+ {
159
+ id: "azureai-searchbot",
160
+ name: "AzureAI-SearchBot",
161
+ product: "copilot",
162
+ category: "search",
163
+ pattern: /AzureAI-SearchBot/i
164
+ },
165
+ {
166
+ id: "bingbot",
167
+ name: "Bingbot",
168
+ product: "copilot",
169
+ category: "search_engine",
170
+ pattern: /bingbot|BingPreview|msnbot/i
171
+ },
172
+ {
173
+ id: "meta-externalfetcher",
174
+ name: "Meta-ExternalFetcher",
175
+ product: "meta",
176
+ category: "assistant",
177
+ pattern: /meta-externalfetcher/i
178
+ },
179
+ {
180
+ id: "meta-webindexer",
181
+ name: "Meta-WebIndexer",
182
+ product: "meta",
183
+ category: "search",
184
+ pattern: /meta-webindexer/i
185
+ },
186
+ {
187
+ id: "meta-externalagent",
188
+ name: "Meta-ExternalAgent",
189
+ product: "meta",
190
+ category: "training",
191
+ pattern: /meta-externalagent|FacebookBot/i
192
+ },
193
+ {
194
+ id: "facebookexternalhit",
195
+ name: "Facebook link preview",
196
+ product: "meta",
197
+ category: "other",
198
+ pattern: /facebookexternalhit|facebookcatalog/i
199
+ },
200
+ {
201
+ id: "applebot",
202
+ name: "Applebot",
203
+ product: "apple",
204
+ category: "search_engine",
205
+ pattern: /Applebot/i
206
+ },
207
+ {
208
+ id: "amazonbuyforme",
209
+ name: "Amazon Buy for Me",
210
+ product: "amazon",
211
+ category: "agent",
212
+ pattern: /AmazonBuyForMe/i
213
+ },
214
+ {
215
+ id: "novaact",
216
+ name: "Nova Act",
217
+ product: "amazon",
218
+ category: "agent",
219
+ pattern: /NovaAct/i
220
+ },
221
+ {
222
+ id: "amzn-user",
223
+ name: "Amzn-User",
224
+ product: "amazon",
225
+ category: "assistant",
226
+ pattern: /Amzn-User/i
227
+ },
228
+ {
229
+ id: "amzn-searchbot",
230
+ name: "Amzn-SearchBot",
231
+ product: "amazon",
232
+ category: "search",
233
+ pattern: /Amzn-SearchBot|amazon-kendra/i
234
+ },
235
+ {
236
+ id: "bedrockbot",
237
+ name: "BedrockBot",
238
+ product: "amazon",
239
+ category: "training",
240
+ pattern: /bedrockbot/i
241
+ },
242
+ {
243
+ id: "amazonbot",
244
+ name: "Amazonbot",
245
+ product: "amazon",
246
+ category: "search",
247
+ pattern: /Amazonbot/i
248
+ },
249
+ {
250
+ id: "mistralai-user",
251
+ name: "MistralAI-User",
252
+ product: "mistral",
253
+ category: "assistant",
254
+ pattern: /MistralAI-User/i
255
+ },
256
+ {
257
+ id: "mistralai-index",
258
+ name: "MistralAI-Index",
259
+ product: "mistral",
260
+ category: "search",
261
+ pattern: /MistralAI-Index/i
262
+ },
263
+ {
264
+ id: "mistralai-training",
265
+ name: "MistralAI-Training",
266
+ product: "mistral",
267
+ category: "training",
268
+ pattern: /MistralAI-Training/i
269
+ },
270
+ {
271
+ id: "deepseekbot",
272
+ name: "DeepSeekBot",
273
+ product: "deepseek",
274
+ category: "training",
275
+ pattern: /DeepSeekBot/i
276
+ },
277
+ {
278
+ id: "doubaobot",
279
+ name: "DoubaoBot",
280
+ product: "bytedance",
281
+ category: "search",
282
+ pattern: /DoubaoBot/i
283
+ },
284
+ {
285
+ id: "bytespider",
286
+ name: "Bytespider",
287
+ product: "bytedance",
288
+ category: "training",
289
+ pattern: /Bytespider|TikTokSpider/i
290
+ },
291
+ {
292
+ id: "duckassistbot",
293
+ name: "DuckAssistBot",
294
+ product: "duckduckgo",
295
+ category: "assistant",
296
+ pattern: /DuckAssistBot/i
297
+ },
298
+ {
299
+ id: "duckduckbot",
300
+ name: "DuckDuckBot",
301
+ product: "duckduckgo",
302
+ category: "search_engine",
303
+ pattern: /DuckDuckBot/i
304
+ },
305
+ {
306
+ id: "ccbot",
307
+ name: "CCBot",
308
+ product: "commoncrawl",
309
+ category: "training",
310
+ pattern: /CCBot/i
311
+ },
312
+ {
313
+ id: "cohere-training",
314
+ name: "Cohere training crawler",
315
+ product: "cohere",
316
+ category: "training",
317
+ pattern: /cohere-training-data-crawler/i
318
+ },
319
+ {
320
+ id: "cohere-ai",
321
+ name: "cohere-ai",
322
+ product: "cohere",
323
+ category: "assistant",
324
+ pattern: /cohere-ai/i
325
+ },
326
+ {
327
+ id: "youbot",
328
+ name: "YouBot",
329
+ product: "you",
330
+ category: "search",
331
+ pattern: /YouBot/i
332
+ },
333
+ {
334
+ id: "tongyibot",
335
+ name: "TongyiBot",
336
+ product: "qwen",
337
+ category: "assistant",
338
+ pattern: /TongyiBot/i
339
+ },
340
+ {
341
+ id: "qwenbot",
342
+ name: "QwenBot",
343
+ product: "qwen",
344
+ category: "training",
345
+ pattern: /QwenBot/i
346
+ },
347
+ {
348
+ id: "kimi-user",
349
+ name: "Kimi-User",
350
+ product: "kimi",
351
+ category: "assistant",
352
+ pattern: /Kimi-User/i
353
+ },
354
+ {
355
+ id: "kimi-searchbot",
356
+ name: "Kimi-SearchBot",
357
+ product: "kimi",
358
+ category: "search",
359
+ pattern: /Kimi-SearchBot/i
360
+ },
361
+ {
362
+ id: "kimibot",
363
+ name: "KimiBot",
364
+ product: "kimi",
365
+ category: "training",
366
+ pattern: /KimiBot/i
367
+ },
368
+ {
369
+ id: "yiyanbot",
370
+ name: "YiyanBot",
371
+ product: "baidu",
372
+ category: "assistant",
373
+ pattern: /YiyanBot/i
374
+ },
375
+ {
376
+ id: "erniebot",
377
+ name: "ERNIEBot",
378
+ product: "baidu",
379
+ category: "training",
380
+ pattern: /ERNIEBot/i
381
+ },
382
+ {
383
+ id: "baiduspider",
384
+ name: "Baiduspider",
385
+ product: "baidu",
386
+ category: "search_engine",
387
+ pattern: /Baiduspider/i
388
+ },
389
+ {
390
+ id: "yandexadditional",
391
+ name: "YandexAdditional",
392
+ product: "yandex",
393
+ category: "training",
394
+ pattern: /YandexAdditional/i
395
+ },
396
+ {
397
+ id: "yandexbot",
398
+ name: "YandexBot",
399
+ product: "yandex",
400
+ category: "search_engine",
401
+ pattern: /YandexBot|YandexMobileBot/i
402
+ },
403
+ {
404
+ id: "pangubot",
405
+ name: "PanguBot",
406
+ product: "huawei",
407
+ category: "training",
408
+ pattern: /PanguBot/i
409
+ },
410
+ {
411
+ id: "petalbot",
412
+ name: "PetalBot",
413
+ product: "huawei",
414
+ category: "search_engine",
415
+ pattern: /PetalBot/i
416
+ },
417
+ {
418
+ id: "ai2bot",
419
+ name: "AI2Bot",
420
+ product: "ai2",
421
+ category: "training",
422
+ pattern: /AI2Bot|Ai2Bot-Dolma/i
423
+ },
424
+ {
425
+ id: "bravebot",
426
+ name: "Bravebot",
427
+ product: "brave",
428
+ category: "search",
429
+ pattern: /Bravebot/i
430
+ },
431
+ {
432
+ id: "kagi-fetcher",
433
+ name: "Kagi fetcher",
434
+ product: "kagi",
435
+ category: "assistant",
436
+ pattern: /kagi-fetcher/i
437
+ },
438
+ {
439
+ id: "phindbot",
440
+ name: "PhindBot",
441
+ product: "phind",
442
+ category: "assistant",
443
+ pattern: /PhindBot/i
444
+ },
445
+ {
446
+ id: "manus-user",
447
+ name: "Manus",
448
+ product: "manus",
449
+ category: "agent",
450
+ pattern: /Manus-User/i
451
+ },
452
+ {
453
+ id: "cursor",
454
+ name: "Cursor",
455
+ product: "coding",
456
+ category: "assistant",
457
+ pattern: /\bCursor\//i
458
+ },
459
+ {
460
+ id: "devin",
461
+ name: "Devin",
462
+ product: "coding",
463
+ category: "agent",
464
+ pattern: /\bDevin\b/i
465
+ },
466
+ {
467
+ id: "opencode",
468
+ name: "opencode",
469
+ product: "coding",
470
+ category: "assistant",
471
+ pattern: /\bopencode\b/i
472
+ },
473
+ {
474
+ id: "firecrawl",
475
+ name: "Firecrawl",
476
+ product: "aidata",
477
+ category: "assistant",
478
+ pattern: /FirecrawlAgent|Firecrawl/i
479
+ },
480
+ {
481
+ id: "exa-user",
482
+ name: "Exa (live fetch)",
483
+ product: "aidata",
484
+ category: "assistant",
485
+ pattern: /ExaUser/i
486
+ },
487
+ {
488
+ id: "exabot",
489
+ name: "Exa",
490
+ product: "aidata",
491
+ category: "search",
492
+ pattern: /ExaBot|ExaSearchBot/i
493
+ },
494
+ {
495
+ id: "tavilybot",
496
+ name: "Tavily",
497
+ product: "aidata",
498
+ category: "search",
499
+ pattern: /TavilyBot/i
500
+ },
501
+ {
502
+ id: "linkupbot",
503
+ name: "Linkup",
504
+ product: "aidata",
505
+ category: "search",
506
+ pattern: /LinkupBot/i
507
+ },
508
+ {
509
+ id: "shapbot",
510
+ name: "Parallel (ShapBot)",
511
+ product: "aidata",
512
+ category: "search",
513
+ pattern: /ShapBot|Shap-User/i
514
+ },
515
+ {
516
+ id: "jina",
517
+ name: "Jina Reader",
518
+ product: "aidata",
519
+ category: "assistant",
520
+ pattern: /Jina-?Reader|\bJina\b/i
521
+ },
522
+ {
523
+ id: "diffbot",
524
+ name: "Diffbot",
525
+ product: "aidata",
526
+ category: "training",
527
+ pattern: /Diffbot/i
528
+ },
529
+ {
530
+ id: "brightbot",
531
+ name: "Bright Data",
532
+ product: "aidata",
533
+ category: "training",
534
+ pattern: /Brightbot/i
535
+ },
536
+ {
537
+ id: "apifybot",
538
+ name: "Apify",
539
+ product: "aidata",
540
+ category: "training",
541
+ pattern: /ApifyBot|ApifyWebsiteContentCrawler/i
542
+ },
543
+ {
544
+ id: "crawl4ai",
545
+ name: "Crawl4AI",
546
+ product: "aidata",
547
+ category: "training",
548
+ pattern: /Crawl4AI/i
549
+ },
550
+ {
551
+ id: "webzio",
552
+ name: "Webz.io",
553
+ product: "aidata",
554
+ category: "training",
555
+ pattern: /omgili|Webzio-Extended/i
556
+ },
557
+ {
558
+ id: "timpibot",
559
+ name: "Timpibot",
560
+ product: "aidata",
561
+ category: "training",
562
+ pattern: /Timpibot/i
563
+ },
564
+ {
565
+ id: "imagesiftbot",
566
+ name: "ImagesiftBot",
567
+ product: "aidata",
568
+ category: "training",
569
+ pattern: /ImagesiftBot|img2dataset|LAIONDownloader/i
570
+ },
571
+ {
572
+ id: "signed-agent",
573
+ name: "Signed agent",
574
+ product: "automation",
575
+ category: "agent",
576
+ pattern: null
577
+ },
578
+ {
579
+ id: "headless",
580
+ name: "Headless browser",
581
+ product: "automation",
582
+ category: "other",
583
+ pattern: /HeadlessChrome|PhantomJS|Puppeteer|Playwright|Selenium|Lightpanda|webdriver/i
584
+ },
585
+ {
586
+ id: "ahrefs",
587
+ name: "Ahrefs",
588
+ product: "seo",
589
+ category: "other",
590
+ pattern: /AhrefsBot|AhrefsSiteAudit/i
591
+ },
592
+ {
593
+ id: "seo",
594
+ name: "SEO crawler",
595
+ product: "seo",
596
+ category: "other",
597
+ pattern: /SemrushBot|SiteAuditBot|MJ12bot|DotBot|rogerbot|Screaming Frog|DataForSeoBot|BLEXBot|serpstatbot|SEOkicks|barkrowler/i
598
+ },
599
+ {
600
+ id: "link-preview",
601
+ name: "Link preview",
602
+ product: "social",
603
+ category: "other",
604
+ pattern: /Twitterbot|LinkedInBot|Slackbot|Slack-ImgProxy|Discordbot|WhatsApp|TelegramBot|Pinterestbot|redditbot|Embedly|Iframely|SkypeUriPreview|vkShare|Mastodon/i
605
+ },
606
+ {
607
+ id: "monitor",
608
+ name: "Uptime monitor",
609
+ product: "monitor",
610
+ category: "other",
611
+ pattern: /UptimeRobot|Pingdom|StatusCake|Better ?Uptime|BetterStack|Checkly|Site24x7|NewRelicPinger|Datadog|UptimeKuma|Uptime-Kuma|Hetrix/i
612
+ },
613
+ {
614
+ id: "search-other",
615
+ name: "Other search engine",
616
+ product: "other",
617
+ category: "search_engine",
618
+ pattern: /Slurp|SeznamBot|Qwantbot|Qwantify|MojeekBot|Ecosia|coccocbot|Sogou|NaverBot|Yeti\//i
619
+ },
620
+ {
621
+ id: "http-client",
622
+ name: "HTTP client / script",
623
+ product: "tools",
624
+ category: "other",
625
+ pattern: /^curl\/|\bWget\b|python-requests|python-urllib|Python\/\d|aiohttp|python-httpx|Go-http-client|okhttp|axios\/|node-fetch|undici|^node$|Java\/\d|Apache-HttpClient|libwww-perl|^Ruby|Faraday|Scrapy|^Deno\/|^Bun\/|GuzzleHttp|^PostmanRuntime|HTTPie/i
626
+ }
627
+ ];
628
+ /** Matched last, after the generated lists: anything that still calls itself a bot, and no User-Agent at all. */
629
+ const CATCH_ALL = [{
630
+ id: "other-bot",
631
+ name: "Other bot",
632
+ product: "other",
633
+ category: "other",
634
+ pattern: /bot\b|bot\/|robot|crawl|spider|scrape|scraping|fetcher|preview|archiver|indexer/i
635
+ }, {
636
+ id: "no-ua",
637
+ name: "No user agent",
638
+ product: "other",
639
+ category: "other",
640
+ pattern: null
641
+ }];
642
+ // src/bots.ts
643
+ /**
644
+ * Known bots, AI crawlers and AI agents, recognised by their User-Agent: the ones we've
645
+ * checked against each vendor's documentation (bots.curated.ts). `detectBot()` uses them in
646
+ * your app, and the middleware uses them to decide what to send.
647
+ *
648
+ * The AgentStats server makes the final call with a much bigger catalogue (these plus public
649
+ * bot lists), so the dashboard knows bots this SDK version doesn't, without an upgrade.
650
+ */
651
+ /** Categories that count as "AI traffic". Search engines and other bots are shown separately. */
652
+ const AI_CATEGORIES = [
653
+ "training",
654
+ "search",
655
+ "assistant",
656
+ "agent"
657
+ ];
658
+ const isAiCategory = (c) => AI_CATEGORIES.includes(c);
659
+ /** Products group several bots from one vendor ("ChatGPT" = GPTBot + OAI-SearchBot + ChatGPT-User + ChatGPT agent). */
660
+ const BOT_PRODUCTS = {
661
+ chatgpt: {
662
+ name: "ChatGPT",
663
+ operator: "OpenAI"
664
+ },
665
+ claude: {
666
+ name: "Claude",
667
+ operator: "Anthropic"
668
+ },
669
+ perplexity: {
670
+ name: "Perplexity",
671
+ operator: "Perplexity"
672
+ },
673
+ gemini: {
674
+ name: "Gemini",
675
+ operator: "Google"
676
+ },
677
+ copilot: {
678
+ name: "Copilot & Bing",
679
+ operator: "Microsoft"
680
+ },
681
+ meta: {
682
+ name: "Meta AI",
683
+ operator: "Meta"
684
+ },
685
+ apple: {
686
+ name: "Apple",
687
+ operator: "Apple"
688
+ },
689
+ amazon: {
690
+ name: "Amazon",
691
+ operator: "Amazon"
692
+ },
693
+ mistral: {
694
+ name: "Mistral",
695
+ operator: "Mistral AI"
696
+ },
697
+ deepseek: {
698
+ name: "DeepSeek",
699
+ operator: "DeepSeek"
700
+ },
701
+ bytedance: {
702
+ name: "ByteDance",
703
+ operator: "ByteDance"
704
+ },
705
+ xai: {
706
+ name: "Grok",
707
+ operator: "xAI"
708
+ },
709
+ duckduckgo: {
710
+ name: "DuckDuckGo",
711
+ operator: "DuckDuckGo"
712
+ },
713
+ commoncrawl: {
714
+ name: "Common Crawl",
715
+ operator: "Common Crawl Foundation"
716
+ },
717
+ cohere: {
718
+ name: "Cohere",
719
+ operator: "Cohere"
720
+ },
721
+ you: {
722
+ name: "You.com",
723
+ operator: "You.com"
724
+ },
725
+ qwen: {
726
+ name: "Qwen",
727
+ operator: "Alibaba"
728
+ },
729
+ kimi: {
730
+ name: "Kimi",
731
+ operator: "Moonshot AI"
732
+ },
733
+ baidu: {
734
+ name: "Baidu",
735
+ operator: "Baidu"
736
+ },
737
+ yandex: {
738
+ name: "Yandex",
739
+ operator: "Yandex"
740
+ },
741
+ huawei: {
742
+ name: "Huawei",
743
+ operator: "Huawei"
744
+ },
745
+ ai2: {
746
+ name: "Ai2",
747
+ operator: "Allen Institute for AI"
748
+ },
749
+ brave: {
750
+ name: "Brave",
751
+ operator: "Brave"
752
+ },
753
+ kagi: {
754
+ name: "Kagi",
755
+ operator: "Kagi"
756
+ },
757
+ phind: {
758
+ name: "Phind",
759
+ operator: "Phind"
760
+ },
761
+ poe: {
762
+ name: "Poe",
763
+ operator: "Quora"
764
+ },
765
+ manus: {
766
+ name: "Manus",
767
+ operator: "Butterfly Effect"
768
+ },
769
+ ai: {
770
+ name: "Other AI bots",
771
+ operator: "Various"
772
+ },
773
+ coding: {
774
+ name: "Coding agents",
775
+ operator: "Various"
776
+ },
777
+ aidata: {
778
+ name: "AI data providers",
779
+ operator: "Various"
780
+ },
781
+ google: {
782
+ name: "Google Search",
783
+ operator: "Google"
784
+ },
785
+ searchengines: {
786
+ name: "Other search engines",
787
+ operator: "Various"
788
+ },
789
+ automation: {
790
+ name: "Automated browsers",
791
+ operator: "Various"
792
+ },
793
+ seo: {
794
+ name: "SEO tools",
795
+ operator: "Various"
796
+ },
797
+ social: {
798
+ name: "Link previews",
799
+ operator: "Various"
800
+ },
801
+ monitor: {
802
+ name: "Monitoring",
803
+ operator: "Various"
804
+ },
805
+ feeds: {
806
+ name: "Feed readers",
807
+ operator: "Various"
808
+ },
809
+ security: {
810
+ name: "Security scanners",
811
+ operator: "Various"
812
+ },
813
+ archive: {
814
+ name: "Archivers",
815
+ operator: "Various"
816
+ },
817
+ research: {
818
+ name: "Research crawlers",
819
+ operator: "Various"
820
+ },
821
+ ads: {
822
+ name: "Advertising bots",
823
+ operator: "Various"
824
+ },
825
+ tools: {
826
+ name: "HTTP clients & scripts",
827
+ operator: "Various"
828
+ },
829
+ other: {
830
+ name: "Other bots",
831
+ operator: "Unknown"
832
+ }
833
+ };
834
+ /** Every bot this SDK recognises, in the order they're tried: the first match wins, catch-alls last. */
835
+ const BOT_PATTERNS = [...CURATED, ...CATCH_ALL];
836
+ const BY_ID = new Map(BOT_PATTERNS.map((b) => [b.id, b]));
837
+ /** Real browsers whose UA happens to contain a bot-ish word. */
838
+ const HUMAN_LOOKALIKES = /cubot|\bbotswana\b/i;
839
+ /** The first bot in `list` whose pattern matches `ua` (trimmed, non-empty). Shared with the server's bigger catalogue. */
840
+ function firstMatch(list, s) {
841
+ for (const b of list) {
842
+ if (!b.pattern || !b.pattern.test(s)) continue;
843
+ if ((b.id === "other-bot" || b.id === "link-preview") && HUMAN_LOOKALIKES.test(s)) continue;
844
+ return b;
845
+ }
846
+ return null;
847
+ }
848
+ /** Identify a bot from its User-Agent. Returns null for (what looks like) a normal browser. */
849
+ function classifyUserAgent(ua) {
850
+ const s = (ua ?? "").trim();
851
+ if (!s) return BY_ID.get("no-ua");
852
+ return firstMatch(BOT_PATTERNS, s);
853
+ }
854
+ /** Hostnames of AI assistants that send people to websites, and the `utm_source` values they add. */
855
+ const AI_REFERRERS = [
856
+ {
857
+ product: "chatgpt",
858
+ hosts: /(^|\.)(chatgpt\.com|chat\.openai\.com)$/,
859
+ utm: /^(chatgpt(\.com)?|openai)$/
860
+ },
861
+ {
862
+ product: "perplexity",
863
+ hosts: /(^|\.)perplexity\.ai$/,
864
+ utm: /^perplexity(\.ai)?$/
865
+ },
866
+ {
867
+ product: "claude",
868
+ hosts: /(^|\.)claude\.ai$/,
869
+ utm: /^claude(\.ai)?$/
870
+ },
871
+ {
872
+ product: "gemini",
873
+ hosts: /^(gemini|bard)\.google\.com$/,
874
+ utm: /^(gemini|bard)$/
875
+ },
876
+ {
877
+ product: "copilot",
878
+ hosts: /^copilot\.microsoft\.com$|(^|\.)copilot\.cloud\.microsoft$|^copilot\.com$/,
879
+ utm: /^copilot(\.com)?$/
880
+ },
881
+ {
882
+ product: "meta",
883
+ hosts: /(^|\.)meta\.ai$/,
884
+ utm: /^meta\.?ai$/
885
+ },
886
+ {
887
+ product: "deepseek",
888
+ hosts: /(^|\.)deepseek\.com$/,
889
+ utm: /^deepseek$/
890
+ },
891
+ {
892
+ product: "xai",
893
+ hosts: /(^|\.)grok\.com$/,
894
+ utm: /^grok$/
895
+ },
896
+ {
897
+ product: "mistral",
898
+ hosts: /^chat\.mistral\.ai$/,
899
+ utm: /^(le-?chat|mistral)$/
900
+ },
901
+ {
902
+ product: "you",
903
+ hosts: /(^|\.)you\.com$/
904
+ },
905
+ {
906
+ product: "phind",
907
+ hosts: /(^|\.)phind\.com$/
908
+ },
909
+ {
910
+ product: "poe",
911
+ hosts: /(^|\.)poe\.com$/
912
+ },
913
+ {
914
+ product: "duckduckgo",
915
+ hosts: /^duck\.ai$/
916
+ },
917
+ {
918
+ product: "qwen",
919
+ hosts: /^chat\.qwen\.ai$/
920
+ },
921
+ {
922
+ product: "kimi",
923
+ hosts: /(^|\.)kimi\.(com|ai)$|^kimi\.moonshot\.cn$/
924
+ },
925
+ {
926
+ product: "bytedance",
927
+ hosts: /(^|\.)doubao\.com$/
928
+ }
929
+ ];
930
+ function hostOf(raw) {
931
+ try {
932
+ return new URL(raw.includes("://") ? raw : `https://${raw}`).hostname.toLowerCase().replace(/^www\./, "");
933
+ } catch {
934
+ return "";
935
+ }
936
+ }
937
+ /**
938
+ * Which AI assistant sent a human to this page, from the Referer header and/or
939
+ * the landing URL's `utm_source`. Returns the product id, or null.
940
+ */
941
+ function classifyReferral(referrer, url) {
942
+ if (referrer) {
943
+ const host = hostOf(referrer);
944
+ const hit = host && AI_REFERRERS.find((r) => r.hosts.test(host));
945
+ if (hit) return hit.product;
946
+ }
947
+ if (url && url.includes("utm_source=")) {
948
+ const m = url.match(/[?&]utm_source=([^&#]+)/);
949
+ const src = m ? decodeURIComponent(m[1]).toLowerCase().replace(/^www\./, "") : "";
950
+ const hit = src && AI_REFERRERS.find((r) => r.utm?.test(src) || r.hosts.test(src));
951
+ if (hit) return hit.product;
952
+ }
953
+ return null;
954
+ }
955
+ function readHeader(h, name) {
956
+ if (!h) return null;
957
+ if (typeof h.get === "function") return h.get(name);
958
+ const v = h[name];
959
+ return Array.isArray(v) ? v.join(", ") : v ?? null;
960
+ }
961
+ /**
962
+ * What kind of client sent this request, from its User-Agent, Web Bot Auth headers and referrer.
963
+ * Synchronous and dependency-free, so it works in any server, middleware or edge function.
964
+ *
965
+ * It reports who the client *claims* to be. Anyone can send `User-Agent: GPTBot`, so don't
966
+ * use it to grant access; your dashboard verifies each hit against the vendor's IP ranges,
967
+ * reverse DNS and signatures.
968
+ *
969
+ * detectBot(request) // fetch Request (Next, SvelteKit, Astro, Hono, Workers…)
970
+ * detectBot(await headers()) // Next.js server components
971
+ * detectBot(req) // Node / Express IncomingMessage
972
+ */
973
+ function detectBot(input) {
974
+ const headers = input && typeof input.get === "function" ? input : input?.headers;
975
+ return detectFromHeaders((name) => readHeader(headers, name), input && "url" in input && typeof input.url === "string" ? input.url : null);
976
+ }
977
+ /** `detectBot` over a header getter (used by the server adapters). */
978
+ function detectFromHeaders(header, url) {
979
+ const ua = header("user-agent") ?? null;
980
+ const signed = !!(header("signature-agent") && header("signature-input"));
981
+ const bot = classifyUserAgent(ua) ?? (signed ? BY_ID.get("signed-agent") : null);
982
+ if (!bot) {
983
+ const referral = classifyReferral(header("referer"), url);
984
+ return {
985
+ isBot: false,
986
+ isAI: false,
987
+ id: null,
988
+ name: null,
989
+ category: null,
990
+ product: null,
991
+ operator: null,
992
+ browser: false,
993
+ signed: false,
994
+ referral: referral ? BOT_PRODUCTS[referral].name : null
995
+ };
996
+ }
997
+ const product = BOT_PRODUCTS[bot.product];
998
+ return {
999
+ isBot: true,
1000
+ isAI: isAiCategory(bot.category),
1001
+ id: bot.id,
1002
+ name: bot.name,
1003
+ category: bot.category,
1004
+ product: product.name,
1005
+ operator: bot.operator ?? product.operator,
1006
+ browser: bot.category === "agent" || bot.product === "automation",
1007
+ signed,
1008
+ referral: null
1009
+ };
1010
+ }
1011
+ exports.AI_CATEGORIES = AI_CATEGORIES;
1012
+ exports.AI_REFERRERS = AI_REFERRERS;
1013
+ exports.BOT_PATTERNS = BOT_PATTERNS;
1014
+ exports.BOT_PRODUCTS = BOT_PRODUCTS;
1015
+ exports.CATCH_ALL = CATCH_ALL;
1016
+ exports.CURATED = CURATED;
1017
+ exports.classifyReferral = classifyReferral;
1018
+ exports.classifyUserAgent = classifyUserAgent;
1019
+ exports.detectBot = detectBot;
1020
+ exports.detectFromHeaders = detectFromHeaders;
1021
+ exports.firstMatch = firstMatch;
1022
+ exports.isAiCategory = isAiCategory;