@waniwani/relay 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -3
- package/dist/index.d.ts +4 -1
- package/dist/index.js +51 -15
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -68,6 +68,7 @@ For each matched request, `recordAgentVisit` posts one JSON record of type `Agen
|
|
|
68
68
|
| `sourceEventId` | A random UUID for this request |
|
|
69
69
|
| `relayVersion` | The version of this library |
|
|
70
70
|
| `platform` | `vercel`, `cloudflare` or `cloudfront`, when the library knows the platform it runs on |
|
|
71
|
+
| `uses` | The relay functions this process called so far: `recordAgentVisit`, `serveAgentPage` and `handleViewerRequest`. Waniwani reads it to tell whether the code has the calls that agent pages and blocking need |
|
|
71
72
|
|
|
72
73
|
The `headers` field can hold only these request headers:
|
|
73
74
|
|
|
@@ -114,6 +115,8 @@ Serves the agent page of a request: a text-first copy of the page that Waniwani
|
|
|
114
115
|
|
|
115
116
|
You can block AI agents per site in Waniwani. When the `User-Agent` of a `GET` or `HEAD` request contains a blocked token, the function answers `403` with the text body `Forbidden`. That check runs before the agent page checks and before the `x-waniwani-render` check, so it works with agent pages off. The response carries `x-waniwani-blocked: 1`, `cache-control: private, no-store` and `vary: user-agent`.
|
|
116
117
|
|
|
118
|
+
The agent gets the page as Markdown when its `Accept` header lists `text/markdown` with a `q` above 0. Every other agent gets the page as HTML, including an agent that sends `*/*` or no `Accept` header.
|
|
119
|
+
|
|
117
120
|
The page response carries the stored content type, `x-waniwani-agent-page: hit`, `cache-control: private, no-store` and `vary: user-agent, accept`. A `HEAD` request gets the same headers with no body. The settings load on the first call, with a 300 ms limit, and stay in memory for 60 seconds. After that the relay keeps serving the cached settings while it loads new ones in the background. The function never throws, and the promise never rejects.
|
|
118
121
|
|
|
119
122
|
On Vercel, in `middleware.ts`:
|
|
@@ -161,7 +164,7 @@ The two events do these jobs:
|
|
|
161
164
|
|
|
162
165
|
- **Viewer request** runs on every request, before the CloudFront cache. `handleViewerRequest` decides what to do with the request:
|
|
163
166
|
- When the site blocks this `User-Agent`, it answers `403`. CloudFront does not cache a response from this event, so a change to your block list applies to the next request.
|
|
164
|
-
- When an AI agent that acts for a user asks for a path that has an agent page, it marks the request. To mark it, it adds `/__waniwani-agent/<host>` in front of the path in `request.uri`. On CloudFront, `request.uri` is the path only, with no host and no query string. For `https://example.com/pricing?a=1
|
|
167
|
+
- When an AI agent that acts for a user asks for a path that has an agent page, it marks the request. To mark it, it adds `/__waniwani-agent/<format>/<host>` in front of the path in `request.uri`. The format is `markdown` or `html`, from the agent's `Accept` header. On CloudFront, `request.uri` is the path only, with no host and no query string. For `https://example.com/pricing?a=1` from an agent that asks for HTML, `request.uri` changes from `/pricing` to `/__waniwani-agent/html/example.com/pricing`, and the query string stays as it is.
|
|
165
168
|
- In all other cases it returns `null`, and the request continues with no change.
|
|
166
169
|
- **Origin request** runs only on a cache miss, which is when CloudFront has no stored response for the request. The agent page has `cache-control: private, no-store`, so CloudFront never stores it, and a marked request is always a cache miss. `serveAgentPage` sends the agent page for a marked request. It removes the mark first. So when it cannot send the page, `?? request` sends the original path to your origin.
|
|
167
170
|
|
|
@@ -171,10 +174,14 @@ Viewer-request handler:
|
|
|
171
174
|
|
|
172
175
|
```ts
|
|
173
176
|
const request = event.Records[0].cf.request;
|
|
174
|
-
|
|
175
|
-
|
|
177
|
+
const visit = isAgent(request) ? recordAgentVisit(request, { key }) : null;
|
|
178
|
+
const answer = await handleViewerRequest(request, { key });
|
|
179
|
+
await visit;
|
|
180
|
+
return answer ?? request;
|
|
176
181
|
```
|
|
177
182
|
|
|
183
|
+
`recordAgentVisit` reads the path when you call it, so the record keeps the original path although `handleViewerRequest` can mark it. Start the record before `handleViewerRequest` and await it after: the record then reports both calls in `uses`.
|
|
184
|
+
|
|
178
185
|
Origin-request handler:
|
|
179
186
|
|
|
180
187
|
```ts
|
package/dist/index.d.ts
CHANGED
|
@@ -68,7 +68,10 @@ type AgentVisitRecord = {
|
|
|
68
68
|
sourceEventId: string;
|
|
69
69
|
relayVersion: string;
|
|
70
70
|
platform?: Platform;
|
|
71
|
+
/** The relay functions this process called, so WaniWani can show which setup steps the code has. */
|
|
72
|
+
uses: RelayFunction[];
|
|
71
73
|
};
|
|
74
|
+
type RelayFunction = "recordAgentVisit" | "serveAgentPage" | "handleViewerRequest";
|
|
72
75
|
/**
|
|
73
76
|
* Tells whether a request, or the `cf.request` of a Lambda@Edge event, comes from an AI agent. Reads headers only and never waits on the network.
|
|
74
77
|
* Every 12 hours it starts one background fetch of the newest AI bot list, unless `rules` is `"bundled"`.
|
|
@@ -96,4 +99,4 @@ declare function serveAgentPage(request: CloudFrontRequest, options?: ServeAgent
|
|
|
96
99
|
declare function handleViewerRequest(request: CloudFrontRequest, options?: ServeAgentPageOptions): Promise<CloudFrontResultResponse | null>;
|
|
97
100
|
|
|
98
101
|
export { handleViewerRequest, isAgent, recordAgentVisit, serveAgentPage };
|
|
99
|
-
export type { AgentVisitRecord, CloudFrontRequest, CloudFrontResultResponse, IsAgentOptions, Region, RelayOptions, ServeAgentPageOptions };
|
|
102
|
+
export type { AgentVisitRecord, CloudFrontRequest, CloudFrontResultResponse, IsAgentOptions, Region, RelayFunction, RelayOptions, ServeAgentPageOptions };
|
package/dist/index.js
CHANGED
|
@@ -17,7 +17,7 @@ var settings = {
|
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
// src/version.ts
|
|
20
|
-
var RELAY_VERSION = true ? "0.
|
|
20
|
+
var RELAY_VERSION = true ? "0.5.0" : "0.0.0-dev";
|
|
21
21
|
|
|
22
22
|
// src/agent-page.ts
|
|
23
23
|
var isObject = (value) => typeof value === "object" && value !== null;
|
|
@@ -173,13 +173,23 @@ async function readCapped(body, deadline, maxBytes) {
|
|
|
173
173
|
}
|
|
174
174
|
return out;
|
|
175
175
|
}
|
|
176
|
-
|
|
176
|
+
function pageFormat(accept) {
|
|
177
|
+
return accept.split(",").some((range) => {
|
|
178
|
+
const [type = "", ...params] = range.split(";").map((part) => part.trim().toLowerCase());
|
|
179
|
+
if (type !== "text/markdown") {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
const q = params.find((param) => param.startsWith("q="));
|
|
183
|
+
return q === void 0 || Number(q.slice(2)) > 0;
|
|
184
|
+
}) ? "markdown" : "html";
|
|
185
|
+
}
|
|
186
|
+
async function fetchAgentPage(rules, hostname, path, format) {
|
|
177
187
|
const controller = new AbortController();
|
|
178
188
|
try {
|
|
179
189
|
const response = await within(
|
|
180
190
|
fetch(`${rules.pagesUrl}/${hostname}${path === "/" ? "" : path}`, {
|
|
181
191
|
headers: {
|
|
182
|
-
accept: "text/markdown
|
|
192
|
+
accept: format === "markdown" ? "text/markdown" : "text/html",
|
|
183
193
|
"x-waniwani-relay": RELAY_VERSION
|
|
184
194
|
},
|
|
185
195
|
signal: controller.signal
|
|
@@ -360,7 +370,7 @@ import { BlockList } from "node:net";
|
|
|
360
370
|
|
|
361
371
|
// src/logic/agents.generated.json
|
|
362
372
|
var agents_generated_default = {
|
|
363
|
-
source: "Generated by scripts/build-rules.ts from the WaniWani rules file
|
|
373
|
+
source: "Generated by scripts/build-rules.ts from the WaniWani rules file ee1b010ce61b7660, do not edit. Each token is matched case-insensitively inside the User-Agent header.",
|
|
364
374
|
tokens: [
|
|
365
375
|
"addsearchbot",
|
|
366
376
|
"agentdatabot",
|
|
@@ -472,6 +482,7 @@ var agents_generated_default = {
|
|
|
472
482
|
"linerbot",
|
|
473
483
|
"linkupbot",
|
|
474
484
|
"manus-user",
|
|
485
|
+
"mediavinebot/",
|
|
475
486
|
"meta-externalagent",
|
|
476
487
|
"meta-externalfetcher",
|
|
477
488
|
"meta-webindexer",
|
|
@@ -497,6 +508,7 @@ var agents_generated_default = {
|
|
|
497
508
|
"phindbot",
|
|
498
509
|
"poggio-citations",
|
|
499
510
|
"poseidon research crawler",
|
|
511
|
+
"publogic crawler",
|
|
500
512
|
"qodercli",
|
|
501
513
|
"qualifiedbot",
|
|
502
514
|
"querit-searchbot",
|
|
@@ -1707,6 +1719,7 @@ function toRelayRequest(input) {
|
|
|
1707
1719
|
}
|
|
1708
1720
|
|
|
1709
1721
|
// src/index.ts
|
|
1722
|
+
var used = /* @__PURE__ */ new Set();
|
|
1710
1723
|
function isAgent(request, options = {}) {
|
|
1711
1724
|
startRulesRefresh(options);
|
|
1712
1725
|
try {
|
|
@@ -1734,6 +1747,7 @@ function startRulesRefresh(options) {
|
|
|
1734
1747
|
}
|
|
1735
1748
|
}
|
|
1736
1749
|
function recordAgentVisit(request, options = {}) {
|
|
1750
|
+
used.add("recordAgentVisit");
|
|
1737
1751
|
try {
|
|
1738
1752
|
const key = readKey(options.key);
|
|
1739
1753
|
if (!key) {
|
|
@@ -1769,7 +1783,8 @@ function recordAgentVisit(request, options = {}) {
|
|
|
1769
1783
|
path: url.pathname + url.search,
|
|
1770
1784
|
headers,
|
|
1771
1785
|
sourceEventId: crypto.randomUUID(),
|
|
1772
|
-
relayVersion: RELAY_VERSION
|
|
1786
|
+
relayVersion: RELAY_VERSION,
|
|
1787
|
+
uses: []
|
|
1773
1788
|
};
|
|
1774
1789
|
if (capped["user-agent"] !== void 0 && !omitted.has("user-agent")) {
|
|
1775
1790
|
record.userAgent = capped["user-agent"];
|
|
@@ -1783,6 +1798,8 @@ function recordAgentVisit(request, options = {}) {
|
|
|
1783
1798
|
const policy = platform === "cloudfront" ? { timeoutMs: settings.cloudFrontTimeoutMs, retry: false } : { timeoutMs: settings.requestTimeoutMs, retry: true };
|
|
1784
1799
|
const ingestUrl = `${apiUrl}/api/v2/agent-traffic/ingest/middleware/${key.ingestToken}`;
|
|
1785
1800
|
const withMatchKey = async () => {
|
|
1801
|
+
await Promise.resolve();
|
|
1802
|
+
record.uses = [...used];
|
|
1786
1803
|
if (ip !== null && !omitted.has("matchkey")) {
|
|
1787
1804
|
record.matchKey = await deriveMatchKey(key.orgKey, ip).catch(
|
|
1788
1805
|
() => void 0
|
|
@@ -1809,6 +1826,7 @@ var BLOCKED = {
|
|
|
1809
1826
|
}
|
|
1810
1827
|
};
|
|
1811
1828
|
async function serveAgentPage(request, options = {}) {
|
|
1829
|
+
used.add("serveAgentPage");
|
|
1812
1830
|
try {
|
|
1813
1831
|
return isCloudFrontRequest(request) ? await serveCloudFront(request, options) : await serveFetch(request, options);
|
|
1814
1832
|
} catch {
|
|
@@ -1823,6 +1841,7 @@ async function serveFetch(request, options) {
|
|
|
1823
1841
|
hostname: url.hostname,
|
|
1824
1842
|
pathname: url.pathname,
|
|
1825
1843
|
userAgent: request.headers.get("user-agent") ?? "",
|
|
1844
|
+
accept: request.headers.get("accept") ?? "",
|
|
1826
1845
|
rendering: request.headers.has("x-waniwani-render")
|
|
1827
1846
|
},
|
|
1828
1847
|
options
|
|
@@ -1836,10 +1855,11 @@ async function serveFetch(request, options) {
|
|
|
1836
1855
|
});
|
|
1837
1856
|
}
|
|
1838
1857
|
async function serveCloudFront(request, options) {
|
|
1839
|
-
const
|
|
1840
|
-
if (
|
|
1858
|
+
const mark = takeMarkFromUri(request);
|
|
1859
|
+
if (mark === null) {
|
|
1841
1860
|
return null;
|
|
1842
1861
|
}
|
|
1862
|
+
const { hostname, format } = mark;
|
|
1843
1863
|
const method = request.method.toUpperCase();
|
|
1844
1864
|
if (!isPageMethod(method)) {
|
|
1845
1865
|
return null;
|
|
@@ -1852,7 +1872,8 @@ async function serveCloudFront(request, options) {
|
|
|
1852
1872
|
match.rules,
|
|
1853
1873
|
hostname,
|
|
1854
1874
|
normalizePath(request.uri),
|
|
1855
|
-
method
|
|
1875
|
+
method,
|
|
1876
|
+
format
|
|
1856
1877
|
);
|
|
1857
1878
|
if (!answer) {
|
|
1858
1879
|
return null;
|
|
@@ -1930,10 +1951,10 @@ async function answerAgentPage(request, options) {
|
|
|
1930
1951
|
if (!rules.userAgentTokens.some((token) => userAgent.includes(token))) {
|
|
1931
1952
|
return null;
|
|
1932
1953
|
}
|
|
1933
|
-
return pageAnswer(rules, hostname, path, method);
|
|
1954
|
+
return pageAnswer(rules, hostname, path, method, pageFormat(request.accept));
|
|
1934
1955
|
}
|
|
1935
|
-
async function pageAnswer(rules, hostname, path, method) {
|
|
1936
|
-
const page = await fetchAgentPage(rules, hostname, path);
|
|
1956
|
+
async function pageAnswer(rules, hostname, path, method, format) {
|
|
1957
|
+
const page = await fetchAgentPage(rules, hostname, path, format);
|
|
1937
1958
|
if (!page) {
|
|
1938
1959
|
return null;
|
|
1939
1960
|
}
|
|
@@ -1949,13 +1970,22 @@ async function pageAnswer(rules, hostname, path, method) {
|
|
|
1949
1970
|
};
|
|
1950
1971
|
}
|
|
1951
1972
|
var AGENT_URI_PREFIX = "/__waniwani-agent/";
|
|
1973
|
+
var PAGE_FORMATS = ["markdown", "html"];
|
|
1952
1974
|
function splitAgentUri(uri) {
|
|
1953
1975
|
if (!uri.startsWith(AGENT_URI_PREFIX)) {
|
|
1954
1976
|
return null;
|
|
1955
1977
|
}
|
|
1956
1978
|
const rest = uri.slice(AGENT_URI_PREFIX.length);
|
|
1957
|
-
const
|
|
1958
|
-
|
|
1979
|
+
const formatEnd = rest.indexOf("/");
|
|
1980
|
+
const formatName = formatEnd === -1 ? rest : rest.slice(0, formatEnd);
|
|
1981
|
+
const format = PAGE_FORMATS.find((known) => known === formatName) ?? null;
|
|
1982
|
+
const afterFormat = formatEnd === -1 ? "" : rest.slice(formatEnd + 1);
|
|
1983
|
+
const slash = afterFormat.indexOf("/");
|
|
1984
|
+
return slash === -1 ? { format, encodedHost: afterFormat, originalUri: "/" } : {
|
|
1985
|
+
format,
|
|
1986
|
+
encodedHost: afterFormat.slice(0, slash),
|
|
1987
|
+
originalUri: afterFormat.slice(slash)
|
|
1988
|
+
};
|
|
1959
1989
|
}
|
|
1960
1990
|
function stripForgedMark(request) {
|
|
1961
1991
|
let forged = splitAgentUri(request.uri);
|
|
@@ -1970,13 +2000,18 @@ function takeMarkFromUri(request) {
|
|
|
1970
2000
|
return null;
|
|
1971
2001
|
}
|
|
1972
2002
|
request.uri = parts.originalUri;
|
|
2003
|
+
if (parts.format === null) {
|
|
2004
|
+
return null;
|
|
2005
|
+
}
|
|
1973
2006
|
try {
|
|
1974
|
-
|
|
2007
|
+
const hostname = hostnameOf(decodeURIComponent(parts.encodedHost));
|
|
2008
|
+
return hostname === null ? null : { hostname, format: parts.format };
|
|
1975
2009
|
} catch {
|
|
1976
2010
|
return null;
|
|
1977
2011
|
}
|
|
1978
2012
|
}
|
|
1979
2013
|
async function handleViewerRequest(request, options = {}) {
|
|
2014
|
+
used.add("handleViewerRequest");
|
|
1980
2015
|
try {
|
|
1981
2016
|
stripForgedMark(request);
|
|
1982
2017
|
const method = request.method.toUpperCase();
|
|
@@ -2000,7 +2035,8 @@ async function handleViewerRequest(request, options = {}) {
|
|
|
2000
2035
|
if (cloudFrontHeader(request, "x-waniwani-render") !== null || !site.agentPages.enabled || !matchesPath(normalizePath(request.uri), site.agentPages.paths) || !rules.userAgentTokens.some((token) => userAgent.includes(token))) {
|
|
2001
2036
|
return null;
|
|
2002
2037
|
}
|
|
2003
|
-
const
|
|
2038
|
+
const format = pageFormat(cloudFrontHeader(request, "accept") ?? "");
|
|
2039
|
+
const uri = `${AGENT_URI_PREFIX}${format}/${encodeURIComponent(hostname)}${request.uri}`;
|
|
2004
2040
|
if (new TextEncoder().encode(uri).byteLength <= settings.cloudFrontMaxUriBytes) {
|
|
2005
2041
|
request.uri = uri;
|
|
2006
2042
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/settings.ts","../src/version.ts","../src/agent-page.ts","../src/headers.ts","../src/http.ts","../src/ingest.ts","../src/key.ts","../src/logic/decide.ts","../src/logic/agents.generated.json","../src/logic/ip-ranges.generated.json","../src/match-key.ts","../src/refresh.ts","../src/wait-until.ts","../src/platform.ts","../src/request.ts","../src/index.ts"],"sourcesContent":["export const settings = {\n\tapiUrls: {\n\t\tus: \"https://app.waniwani.ai\",\n\t\teu: \"https://eu.app.waniwani.ai\",\n\t},\n\trequestTimeoutMs: 2000,\n\tcloudFrontTimeoutMs: 1000,\n\tcloudFrontMaxUriBytes: 8192,\n\tcloudFrontMaxBodyBytes: 1_000_000 - 16_384,\n\tretryDelayMs: 500,\n\trulesRefreshMs: 12 * 60 * 60 * 1000,\n\trulesRetryMs: 5 * 60 * 1000,\n\tsiteRulesTimeoutMs: 300,\n\tsiteRulesRefreshMs: 60_000,\n\tsiteRulesRetryMs: 5_000,\n};\n","declare const __RELAY_PACKAGE_VERSION__: string | undefined;\n\nexport const RELAY_VERSION: string =\n\ttypeof __RELAY_PACKAGE_VERSION__ === \"string\"\n\t\t? __RELAY_PACKAGE_VERSION__\n\t\t: \"0.0.0-dev\";\n","import { settings } from \"./settings\";\nimport { RELAY_VERSION } from \"./version\";\n\nexport type SiteRules = {\n\tversion: 1;\n\tpagesUrl: string;\n\tuserAgentTokens: string[];\n\tdeadlines: { headersMs: number; bodyMs: number };\n\tmaxBodyBytes: number;\n\tsites: Array<{\n\t\thostname: string;\n\t\tblockedUserAgentTokens: string[];\n\t\tagentPages: { enabled: boolean; paths: string[] };\n\t}>;\n};\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n\ttypeof value === \"object\" && value !== null;\n\nconst isStringArray = (value: unknown): value is string[] =>\n\tArray.isArray(value) && value.every((item) => typeof item === \"string\");\n\nconst isPositive = (value: unknown): value is number =>\n\ttypeof value === \"number\" && Number.isFinite(value) && value > 0;\n\n/** Lowercase like the user agent they are matched against, with blank tokens dropped since they would match every request. */\nconst cleanTokens = (tokens: string[]): string[] =>\n\ttokens\n\t\t.map((token) => token.trim().toLowerCase())\n\t\t.filter((token) => token !== \"\");\n\n/** The site rules in a serve rules file from the app (the `data` of its answer), or null when it does not have the expected form. */\nexport function parseSiteRules(body: unknown): SiteRules | null {\n\tif (!isObject(body) || body.version !== 1) {\n\t\treturn null;\n\t}\n\tconst { pagesUrl, userAgentTokens, deadlines, maxBodyBytes, sites } = body;\n\tif (\n\t\ttypeof pagesUrl !== \"string\" ||\n\t\t!isStringArray(userAgentTokens) ||\n\t\t!isObject(deadlines) ||\n\t\t!isPositive(deadlines.headersMs) ||\n\t\t!isPositive(deadlines.bodyMs) ||\n\t\t!isPositive(maxBodyBytes) ||\n\t\t!Array.isArray(sites)\n\t) {\n\t\treturn null;\n\t}\n\tconst parsedSites: SiteRules[\"sites\"] = [];\n\tfor (const site of sites) {\n\t\tif (!isObject(site)) {\n\t\t\treturn null;\n\t\t}\n\t\tconst blocked = site.blockedUserAgentTokens ?? [];\n\t\tif (\n\t\t\ttypeof site.hostname !== \"string\" ||\n\t\t\t!isStringArray(blocked) ||\n\t\t\t!isObject(site.agentPages) ||\n\t\t\ttypeof site.agentPages.enabled !== \"boolean\" ||\n\t\t\t!isStringArray(site.agentPages.paths)\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\t\tparsedSites.push({\n\t\t\thostname: site.hostname.toLowerCase(),\n\t\t\tblockedUserAgentTokens: cleanTokens(blocked),\n\t\t\tagentPages: {\n\t\t\t\tenabled: site.agentPages.enabled,\n\t\t\t\tpaths: site.agentPages.paths,\n\t\t\t},\n\t\t});\n\t}\n\treturn {\n\t\tversion: 1,\n\t\tpagesUrl: pagesUrl.replace(/\\/+$/, \"\"),\n\t\tuserAgentTokens: cleanTokens(userAgentTokens),\n\t\tdeadlines: { headersMs: deadlines.headersMs, bodyMs: deadlines.bodyMs },\n\t\tmaxBodyBytes,\n\t\tsites: parsedSites,\n\t};\n}\n\ntype CacheEntry = {\n\trules: SiteRules | null;\n\tfetchedAt: number;\n\tlastAttemptAt: number;\n\tlastAttemptOk: boolean;\n\tinFlight: Promise<SiteRules | null> | null;\n};\n\nconst cache = new Map<string, CacheEntry>();\n\nasync function fetchSiteRules(\n\turl: string,\n\ttimeoutMs: number,\n): Promise<SiteRules | null> {\n\tconst response = await fetch(url, {\n\t\tsignal: AbortSignal.timeout(timeoutMs),\n\t});\n\tif (!response.ok) {\n\t\tawait response.body?.cancel().catch(() => {});\n\t\treturn null;\n\t}\n\tconst body: unknown = await response.json();\n\treturn parseSiteRules(isObject(body) ? body.data : null);\n}\n\nfunction startFetch(\n\tentry: CacheEntry,\n\turl: string,\n\ttimeoutMs: number,\n): Promise<SiteRules | null> {\n\tconst now = Date.now();\n\tentry.lastAttemptAt = now;\n\tentry.lastAttemptOk = false;\n\tconst work = fetchSiteRules(url, timeoutMs)\n\t\t.catch(() => null)\n\t\t.then((rules) => {\n\t\t\tif (rules) {\n\t\t\t\tentry.rules = rules;\n\t\t\t\tentry.fetchedAt = Date.now();\n\t\t\t\tentry.lastAttemptOk = true;\n\t\t\t}\n\t\t\tentry.inFlight = null;\n\t\t\treturn rules;\n\t\t});\n\tentry.inFlight = work;\n\treturn work;\n}\n\n/**\n * The rules for this org. Awaits the first fetch, then serves the cache and refreshes it in the background once it is stale.\n * At most one fetch per org is in flight.\n */\nexport async function loadSiteRules(\n\tapiUrl: string,\n\tingestToken: string,\n\tbackground: (work: Promise<unknown>) => void,\n): Promise<SiteRules | null> {\n\tconst url = `${apiUrl}/api/v2/agent-traffic/serve/${ingestToken}/rules`;\n\tlet entry = cache.get(url);\n\tif (!entry) {\n\t\tentry = {\n\t\t\trules: null,\n\t\t\tfetchedAt: 0,\n\t\t\tlastAttemptAt: Number.NEGATIVE_INFINITY,\n\t\t\tlastAttemptOk: false,\n\t\t\tinFlight: null,\n\t\t};\n\t\tcache.set(url, entry);\n\t}\n\tconst now = Date.now();\n\tif (entry.rules) {\n\t\tconst stale = now - entry.fetchedAt >= settings.siteRulesRefreshMs;\n\t\tconst retryDue =\n\t\t\tentry.lastAttemptOk ||\n\t\t\tnow - entry.lastAttemptAt >= settings.siteRulesRetryMs;\n\t\tif (stale && retryDue && !entry.inFlight) {\n\t\t\tbackground(startFetch(entry, url, settings.requestTimeoutMs));\n\t\t}\n\t\treturn entry.rules;\n\t}\n\tif (entry.inFlight) {\n\t\treturn entry.inFlight;\n\t}\n\tif (now - entry.lastAttemptAt < settings.siteRulesRetryMs) {\n\t\treturn null;\n\t}\n\treturn startFetch(entry, url, settings.siteRulesTimeoutMs);\n}\n\n/** The pathname without its trailing slashes, except for the root. */\nexport function normalizePath(pathname: string): string {\n\tconst trimmed = pathname.replace(/\\/+$/, \"\");\n\treturn trimmed === \"\" ? \"/\" : trimmed;\n}\n\n/** Whether a path matches an exact entry like `/pricing`, or a folder entry like `/products/*` that also covers `/products`. */\nexport function matchesPath(path: string, patterns: string[]): boolean {\n\treturn patterns.some((pattern) => {\n\t\tif (pattern === \"*\" || pattern === \"/*\") {\n\t\t\treturn true;\n\t\t}\n\t\tif (pattern.endsWith(\"/*\")) {\n\t\t\tconst folder = normalizePath(pattern.slice(0, -2));\n\t\t\treturn path === folder || path.startsWith(`${folder}/`);\n\t\t}\n\t\treturn path === normalizePath(pattern);\n\t});\n}\n\nconst TIMEOUT = Symbol(\"timeout\");\n\nfunction within<T>(\n\tpromise: Promise<T>,\n\tms: number,\n): Promise<T | typeof TIMEOUT> {\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tconst deadline = new Promise<typeof TIMEOUT>((resolve) => {\n\t\ttimer = setTimeout(() => resolve(TIMEOUT), ms);\n\t});\n\treturn Promise.race([promise, deadline]).finally(() => clearTimeout(timer));\n}\n\nasync function readCapped(\n\tbody: ReadableStream<Uint8Array>,\n\tdeadline: Promise<typeof TIMEOUT>,\n\tmaxBytes: number,\n): Promise<Uint8Array<ArrayBuffer> | null> {\n\tconst reader = body.getReader();\n\tconst chunks: Uint8Array[] = [];\n\tlet size = 0;\n\ttry {\n\t\twhile (true) {\n\t\t\tconst result = await Promise.race([reader.read(), deadline]);\n\t\t\tif (result === TIMEOUT) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (result.done) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tsize += result.value.byteLength;\n\t\t\tif (size > maxBytes) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tchunks.push(result.value);\n\t\t}\n\t} finally {\n\t\treader.cancel().catch(() => {});\n\t}\n\tconst out = new Uint8Array(size);\n\tlet offset = 0;\n\tfor (const chunk of chunks) {\n\t\tout.set(chunk, offset);\n\t\toffset += chunk.byteLength;\n\t}\n\treturn out;\n}\n\n/** Fetches the stored page within both deadlines and the size cap. Returns its body and content type, or null. The home page URL has no trailing slash, which the app would answer with a redirect. */\nexport async function fetchAgentPage(\n\trules: SiteRules,\n\thostname: string,\n\tpath: string,\n): Promise<{ body: Uint8Array<ArrayBuffer>; contentType: string } | null> {\n\tconst controller = new AbortController();\n\ttry {\n\t\tconst response = await within(\n\t\t\tfetch(`${rules.pagesUrl}/${hostname}${path === \"/\" ? \"\" : path}`, {\n\t\t\t\theaders: {\n\t\t\t\t\taccept: \"text/markdown, text/html;q=0.9\",\n\t\t\t\t\t\"x-waniwani-relay\": RELAY_VERSION,\n\t\t\t\t},\n\t\t\t\tsignal: controller.signal,\n\t\t\t}),\n\t\t\trules.deadlines.headersMs,\n\t\t);\n\t\tif (response === TIMEOUT) {\n\t\t\treturn null;\n\t\t}\n\t\tconst contentType = response.headers.get(\"content-type\") ?? \"\";\n\t\tif (\n\t\t\tresponse.status !== 200 ||\n\t\t\t!contentType.toLowerCase().startsWith(\"text/\") ||\n\t\t\t!response.body\n\t\t) {\n\t\t\tawait response.body?.cancel().catch(() => {});\n\t\t\treturn null;\n\t\t}\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\t\tconst deadline = new Promise<typeof TIMEOUT>((resolve) => {\n\t\t\ttimer = setTimeout(() => resolve(TIMEOUT), rules.deadlines.bodyMs);\n\t\t});\n\t\ttry {\n\t\t\tconst body = await readCapped(\n\t\t\t\tresponse.body,\n\t\t\t\tdeadline,\n\t\t\t\trules.maxBodyBytes,\n\t\t\t);\n\t\t\treturn body ? { body, contentType } : null;\n\t\t} finally {\n\t\t\tclearTimeout(timer);\n\t\t}\n\t} catch {\n\t\treturn null;\n\t} finally {\n\t\tcontroller.abort();\n\t}\n}\n","export const HEADER_CAP: readonly string[] = [\n\t\"user-agent\",\n\t\"referer\",\n\t\"accept\",\n\t\"accept-language\",\n\t\"from\",\n\t\"signature-agent\",\n\t\"signature-input\",\n\t\"signature\",\n\t\"dpop\",\n\t\"sec-ch-ua\",\n\t\"sec-ch-ua-platform\",\n\t\"sec-ch-ua-mobile\",\n\t\"authorization\",\n];\n\nconst KNOWN_SCHEMES = new Map(\n\t[\n\t\t\"Basic\",\n\t\t\"Bearer\",\n\t\t\"Digest\",\n\t\t\"DPoP\",\n\t\t\"HOBA\",\n\t\t\"Mutual\",\n\t\t\"Negotiate\",\n\t\t\"NTLM\",\n\t\t\"OAuth\",\n\t\t\"PrivateToken\",\n\t\t\"SCRAM-SHA-1\",\n\t\t\"SCRAM-SHA-256\",\n\t\t\"vapid\",\n\t\t\"AWS4-HMAC-SHA256\",\n\t].map((scheme) => [scheme.toLowerCase(), scheme]),\n);\n\n/** The canonical name of a known Authorization scheme, kept only when a credential follows it. */\nexport function authorizationScheme(value: string): string | null {\n\tconst parts = value.trim().split(/\\s+/);\n\tif (parts.length < 2) {\n\t\treturn null;\n\t}\n\treturn KNOWN_SCHEMES.get((parts[0] ?? \"\").toLowerCase()) ?? null;\n}\n\nexport function readCappedHeaders(headers: Headers): Record<string, string> {\n\tconst out: Record<string, string> = {};\n\tfor (const name of HEADER_CAP) {\n\t\tconst value = headers.get(name);\n\t\tif (value === null) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (name === \"authorization\") {\n\t\t\tconst scheme = authorizationScheme(value);\n\t\t\tif (scheme !== null) {\n\t\t\t\tout[name] = scheme;\n\t\t\t}\n\t\t} else {\n\t\t\tout[name] = value;\n\t\t}\n\t}\n\treturn out;\n}\n\n/** Cloudflare appends the real IP to a client-sent `X-Forwarded-For`, so `CF-Connecting-IP` comes first. */\nexport function clientIp(headers: Headers): string | null {\n\tconst forwarded = headers.get(\"x-forwarded-for\");\n\tconst candidate =\n\t\theaders.get(\"cf-connecting-ip\") ??\n\t\t(forwarded !== null ? forwarded.split(\",\")[0] : headers.get(\"x-real-ip\"));\n\tconst ip = candidate?.trim().toLowerCase();\n\treturn ip ? ip : null;\n}\n","import { settings } from \"./settings\";\n\nexport type Outcome = { status: number } | { failure: \"timeout\" | \"network\" };\n\n/** One fetch bounded by the timeout. Never rejects. */\nexport async function fetchWithTimeout(\n\turl: string,\n\tinit: RequestInit,\n\ttimeoutMs: number = settings.requestTimeoutMs,\n): Promise<Outcome> {\n\tconst controller = new AbortController();\n\tconst timer = setTimeout(() => controller.abort(), timeoutMs);\n\ttry {\n\t\tconst response = await fetch(url, { ...init, signal: controller.signal });\n\t\tawait response.body?.cancel().catch(() => {});\n\t\treturn { status: response.status };\n\t} catch {\n\t\treturn { failure: controller.signal.aborted ? \"timeout\" : \"network\" };\n\t} finally {\n\t\tclearTimeout(timer);\n\t}\n}\n\nexport const delay = (ms: number) =>\n\tnew Promise<void>((resolve) => setTimeout(resolve, ms));\n","import { delay, fetchWithTimeout, type Outcome } from \"./http\";\nimport { settings } from \"./settings\";\n\nconst isSuccess = (outcome: Outcome) =>\n\t\"status\" in outcome && outcome.status >= 200 && outcome.status < 300;\n\nconst isRetryable = (outcome: Outcome) =>\n\t\"failure\" in outcome || outcome.status === 429 || outcome.status >= 500;\n\nconst describe = (outcome: Outcome) =>\n\t\"failure\" in outcome ? outcome.failure : `status ${outcome.status}`;\n\nexport type PostPolicy = { timeoutMs: number; retry: boolean };\n\nconst post = (url: string, body: string, timeoutMs: number) =>\n\tfetchWithTimeout(\n\t\turl,\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\tbody,\n\t\t},\n\t\ttimeoutMs,\n\t);\n\n/** Posts once, retries once on a timeout, network error, 429 or 5xx when the policy allows it, and never rejects. */\nexport async function postEvent(\n\turl: string,\n\tbody: string,\n\tpolicy: PostPolicy = { timeoutMs: settings.requestTimeoutMs, retry: true },\n): Promise<boolean> {\n\tlet outcome = await post(url, body, policy.timeoutMs);\n\tif (policy.retry && !isSuccess(outcome) && isRetryable(outcome)) {\n\t\tawait delay(settings.retryDelayMs);\n\t\toutcome = await post(url, body, policy.timeoutMs);\n\t}\n\tif (isSuccess(outcome)) {\n\t\treturn true;\n\t}\n\tconsole.warn(\n\t\t`[@waniwani/relay] Could not record an agent visit (${describe(outcome)}). The event was dropped.`,\n\t);\n\treturn false;\n}\n","export type RelayKey = { ingestToken: string; orgKey: string };\n\nconst KEY = /^wwt_([A-Za-z0-9_-]+)\\.([A-Za-z0-9_-]+={0,2})$/;\n\nlet warnedMalformed = false;\n\nexport function readKey(explicit: string | undefined): RelayKey | null {\n\tconst raw = explicit ?? readEnv(\"WANIWANI_AGENT_TRAFFIC_KEY\");\n\tif (raw === undefined || raw.trim() === \"\") {\n\t\treturn null;\n\t}\n\tconst match = KEY.exec(raw.trim());\n\tif (!match?.[1] || !match[2]) {\n\t\tif (!warnedMalformed) {\n\t\t\twarnedMalformed = true;\n\t\t\tconsole.warn(\n\t\t\t\t\"[@waniwani/relay] WANIWANI_AGENT_TRAFFIC_KEY is malformed, so no agent visits are recorded. Copy the key again from the WaniWani setup screen.\",\n\t\t\t);\n\t\t}\n\t\treturn null;\n\t}\n\treturn { ingestToken: match[1], orgKey: match[2] };\n}\n\nfunction readEnv(name: string): string | undefined {\n\ttry {\n\t\treturn typeof process === \"undefined\" ? undefined : process.env[name];\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n","import { BlockList } from \"node:net\";\nimport agents from \"./agents.generated.json\";\nimport ranges from \"./ip-ranges.generated.json\";\n\nexport type DecideInput = {\n\theaders: Record<string, string>;\n\tip: string | null;\n\tuseBundled?: boolean;\n};\n\nexport type MatchRules = {\n\ttokens: string[];\n\tipRanges: Array<{ ipv4: string[]; ipv6: string[] }>;\n};\n\nconst MAX_SCANNED_USER_AGENT = 1024;\nconst PREFIX = 3;\n\ntype CompiledRules = {\n\ttokensByPrefix: Map<string, string[]>;\n\tagentRanges: BlockList;\n};\n\nfunction compile(rules: MatchRules): CompiledRules {\n\tconst tokensByPrefix = new Map<string, string[]>();\n\tfor (const token of rules.tokens) {\n\t\tconst key = token.slice(0, PREFIX);\n\t\tconst list = tokensByPrefix.get(key);\n\t\tif (list) {\n\t\t\tlist.push(token);\n\t\t} else {\n\t\t\ttokensByPrefix.set(key, [token]);\n\t\t}\n\t}\n\n\tconst agentRanges = new BlockList();\n\tconst addRanges = (cidrs: string[], type: \"ipv4\" | \"ipv6\") => {\n\t\tfor (const cidr of cidrs) {\n\t\t\tconst [address = \"\", prefix, extra] = cidr.split(\"/\");\n\t\t\tif (!prefix || extra !== undefined) {\n\t\t\t\tthrow new Error(`malformed range ${cidr}`);\n\t\t\t}\n\t\t\tagentRanges.addSubnet(address, Number(prefix), type);\n\t\t}\n\t};\n\tfor (const source of rules.ipRanges) {\n\t\taddRanges(source.ipv4, \"ipv4\");\n\t\taddRanges(source.ipv6, \"ipv6\");\n\t}\n\n\treturn { tokensByPrefix, agentRanges };\n}\n\nconst bundled = compile({ tokens: agents.tokens, ipRanges: ranges.sources });\nlet remote: CompiledRules | null = null;\n\n/** Sets the remote match rules. Throws, and keeps the current ones, when a range is malformed. */\nexport function applyRules(rules: MatchRules): void {\n\tremote = compile(rules);\n}\n\nconst pick = (useBundled: boolean): CompiledRules =>\n\tuseBundled ? bundled : (remote ?? bundled);\n\nexport const isAgentIp = (ip: string, useBundled = false): boolean =>\n\tpick(useBundled).agentRanges.check(ip, ip.includes(\":\") ? \"ipv6\" : \"ipv4\");\n\nexport function hasAgentToken(userAgent: string, useBundled = false): boolean {\n\tconst { tokensByPrefix } = pick(useBundled);\n\tconst text = userAgent.slice(0, MAX_SCANNED_USER_AGENT).toLowerCase();\n\tfor (let i = 0; i + PREFIX <= text.length; i++) {\n\t\tconst candidates = tokensByPrefix.get(text.slice(i, i + PREFIX));\n\t\tif (candidates) {\n\t\t\tfor (const token of candidates) {\n\t\t\t\tif (text.startsWith(token, i)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nexport function decide({\n\theaders,\n\tip,\n\tuseBundled = false,\n}: DecideInput): boolean {\n\tconst userAgent = headers[\"user-agent\"];\n\treturn (\n\t\t(userAgent !== undefined && hasAgentToken(userAgent, useBundled)) ||\n\t\theaders[\"signature-agent\"] !== undefined ||\n\t\theaders.dpop !== undefined ||\n\t\theaders.authorization?.toLowerCase() === \"dpop\" ||\n\t\t(ip !== null && isAgentIp(ip, useBundled))\n\t);\n}\n","{\n\t\"source\": \"Generated by scripts/build-rules.ts from the WaniWani rules file d9b242ef451056c0, do not edit. Each token is matched case-insensitively inside the User-Agent header.\",\n\t\"tokens\": [\n\t\t\"addsearchbot\",\n\t\t\"agentdatabot\",\n\t\t\"agenttimes\",\n\t\t\"ai2bot\",\n\t\t\"ai2bot-deepresearcheval\",\n\t\t\"ai2bot-dolma\",\n\t\t\"aihitbot\",\n\t\t\"aiwebindex\",\n\t\t\"allenai.org\",\n\t\t\"amazon-kendra\",\n\t\t\"amazon-qbusiness\",\n\t\t\"amazonbot\",\n\t\t\"amazonbuyforme\",\n\t\t\"amzn-searchbot\",\n\t\t\"amzn-user\",\n\t\t\"andibot\",\n\t\t\"anthropic-ai\",\n\t\t\"apifybot\",\n\t\t\"apifywebsitecontentcrawler\",\n\t\t\"applebot\",\n\t\t\"aranet-searchbot\",\n\t\t\"atlassian-bot\",\n\t\t\"awario\",\n\t\t\"azureai-searchbot\",\n\t\t\"bedrockbot\",\n\t\t\"bigsur.ai\",\n\t\t\"bixelbot\",\n\t\t\"bravebot\",\n\t\t\"brightbot\",\n\t\t\"buddybot\",\n\t\t\"bytespider\",\n\t\t\"ccbot\",\n\t\t\"channel3bot\",\n\t\t\"chatglm-spider\",\n\t\t\"chatgpt agent\",\n\t\t\"chatgpt-user\",\n\t\t\"claude-code\",\n\t\t\"claude-searchbot\",\n\t\t\"claude-user\",\n\t\t\"claude-web\",\n\t\t\"claudebot\",\n\t\t\"cloudflare-autorag\",\n\t\t\"cloudflarebrowserrenderingcrawler\",\n\t\t\"cloudvertexbot\",\n\t\t\"cohere-ai\",\n\t\t\"cohere-training-data-crawler\",\n\t\t\"cotoyogi\",\n\t\t\"coveobot\",\n\t\t\"cragcrawler\",\n\t\t\"crawl4ai\",\n\t\t\"crawlspace\",\n\t\t\"cursor/1.0.0\",\n\t\t\"datenbank crawler\",\n\t\t\"deepseekbot\",\n\t\t\"diffbot\",\n\t\t\"diffbot-user\",\n\t\t\"doubaobot\",\n\t\t\"duckassistbot\",\n\t\t\"echobot bot\",\n\t\t\"echoboxbot\",\n\t\t\"erniebot\",\n\t\t\"exabot\",\n\t\t\"exasearchbot\",\n\t\t\"exauser\",\n\t\t\"facebookbot\",\n\t\t\"factset_spyderbot\",\n\t\t\"firecrawlagent\",\n\t\t\"friendlycrawler\",\n\t\t\"geisthaus-pagefetcher\",\n\t\t\"gemini-deep-research\",\n\t\t\"google-ads-creatives-assistant\",\n\t\t\"google-agent\",\n\t\t\"google-cloudvertexbot\",\n\t\t\"google-firebase\",\n\t\t\"google-gemini-cli\",\n\t\t\"google-gemininotebook\",\n\t\t\"google-lens\",\n\t\t\"google-notebooklm\",\n\t\t\"googleagent-mariner\",\n\t\t\"googleagent-urlcontext\",\n\t\t\"googleother\",\n\t\t\"googleother-image\",\n\t\t\"googleother-video\",\n\t\t\"gptbot\",\n\t\t\"grove/1.0 rss feed service\",\n\t\t\"henkbot\",\n\t\t\"iaskbot\",\n\t\t\"iaskspider\",\n\t\t\"icc-crawler\",\n\t\t\"imagesiftbot\",\n\t\t\"imagespider\",\n\t\t\"img2dataset\",\n\t\t\"isscyberriskcrawler\",\n\t\t\"jackjillcareersbot\",\n\t\t\"kagi-fetcher\",\n\t\t\"kangaroo bot\",\n\t\t\"keenablebot\",\n\t\t\"kernel searchbot\",\n\t\t\"kimi-agent\",\n\t\t\"kimi-searchbot\",\n\t\t\"kimi-user\",\n\t\t\"kimibot\",\n\t\t\"klaviyoaibot\",\n\t\t\"kunatocrawler\",\n\t\t\"laion-huggingface-processor\",\n\t\t\"laiondownloader\",\n\t\t\"lightpanda\",\n\t\t\"linerbot\",\n\t\t\"linkupbot\",\n\t\t\"manus-user\",\n\t\t\"meta-externalagent\",\n\t\t\"meta-externalfetcher\",\n\t\t\"meta-webindexer\",\n\t\t\"mistralai-index\",\n\t\t\"mistralai-training\",\n\t\t\"mistralai-user\",\n\t\t\"momentic\",\n\t\t\"mozilla-tabstack\",\n\t\t\"mycentralaiscraperbot\",\n\t\t\"nagetbot\",\n\t\t\"netestate imprint crawler\",\n\t\t\"notebooklm\",\n\t\t\"novaact\",\n\t\t\"oai-adsbot\",\n\t\t\"oai-searchbot\",\n\t\t\"omgili\",\n\t\t\"omgilibot\",\n\t\t\"pangubot\",\n\t\t\"panscient\",\n\t\t\"perplexity-user\",\n\t\t\"perplexitybot\",\n\t\t\"petalbot\",\n\t\t\"phindbot\",\n\t\t\"poggio-citations\",\n\t\t\"poseidon research crawler\",\n\t\t\"qodercli\",\n\t\t\"qualifiedbot\",\n\t\t\"querit-searchbot\",\n\t\t\"queritbot\",\n\t\t\"quillbot\",\n\t\t\"qwenbot\",\n\t\t\"reflectionbot\",\n\t\t\"ryebot\",\n\t\t\"sbintuitionsbot\",\n\t\t\"seltzsitemapfetcher\",\n\t\t\"semanticscholarbot\",\n\t\t\"semrushbot-ocob\",\n\t\t\"semrushbot-swa\",\n\t\t\"shap-user\",\n\t\t\"shapbot\",\n\t\t\"sidetrade indexer bot\",\n\t\t\"tavilybot\",\n\t\t\"terra cotta\",\n\t\t\"terracotta\",\n\t\t\"thinkbot\",\n\t\t\"tiktokspider\",\n\t\t\"timpibot\",\n\t\t\"tongyibot\",\n\t\t\"twilioknowledge\",\n\t\t\"twinagent\",\n\t\t\"v0bot\",\n\t\t\"velenpublicwebcrawler\",\n\t\t\"wardbot\",\n\t\t\"wrtnbot\",\n\t\t\"yandexadditional\",\n\t\t\"yandexadditionalbot\",\n\t\t\"yiyanbot\",\n\t\t\"youbot\",\n\t\t\"zanistabot\"\n\t]\n}\n","{\n\t\"sources\": [\n\t\t{\n\t\t\t\"bot\": \"GPTBot\",\n\t\t\t\"url\": \"https://openai.com/gptbot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"4.227.36.0/25\",\n\t\t\t\t\"20.125.66.80/28\",\n\t\t\t\t\"20.171.206.0/24\",\n\t\t\t\t\"20.171.207.0/24\",\n\t\t\t\t\"52.230.152.0/24\",\n\t\t\t\t\"74.7.175.128/25\",\n\t\t\t\t\"74.7.227.0/25\",\n\t\t\t\t\"74.7.227.128/25\",\n\t\t\t\t\"74.7.228.0/25\",\n\t\t\t\t\"74.7.230.0/25\",\n\t\t\t\t\"74.7.241.0/25\",\n\t\t\t\t\"74.7.241.128/25\",\n\t\t\t\t\"74.7.242.0/25\",\n\t\t\t\t\"74.7.243.128/25\",\n\t\t\t\t\"74.7.244.0/25\",\n\t\t\t\t\"132.196.86.0/24\",\n\t\t\t\t\"172.182.202.0/25\",\n\t\t\t\t\"172.182.207.0/25\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"OAI-SearchBot\",\n\t\t\t\"url\": \"https://openai.com/searchbot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"4.227.36.0/25\",\n\t\t\t\t\"13.66.216.176/28\",\n\t\t\t\t\"20.14.99.96/28\",\n\t\t\t\t\"20.25.151.224/28\",\n\t\t\t\t\"20.42.10.176/28\",\n\t\t\t\t\"20.168.18.32/28\",\n\t\t\t\t\"20.169.6.224/28\",\n\t\t\t\t\"20.169.7.48/28\",\n\t\t\t\t\"20.169.77.0/25\",\n\t\t\t\t\"20.171.53.224/28\",\n\t\t\t\t\"20.171.123.64/28\",\n\t\t\t\t\"23.102.145.48/28\",\n\t\t\t\t\"40.67.175.0/25\",\n\t\t\t\t\"40.90.214.16/28\",\n\t\t\t\t\"51.8.102.0/24\",\n\t\t\t\t\"74.7.175.128/25\",\n\t\t\t\t\"74.7.228.0/25\",\n\t\t\t\t\"74.7.228.128/25\",\n\t\t\t\t\"74.7.229.0/25\",\n\t\t\t\t\"74.7.229.128/25\",\n\t\t\t\t\"74.7.230.0/25\",\n\t\t\t\t\"74.7.241.128/25\",\n\t\t\t\t\"74.7.242.128/25\",\n\t\t\t\t\"74.7.243.0/25\",\n\t\t\t\t\"74.7.244.0/25\",\n\t\t\t\t\"104.210.140.128/28\",\n\t\t\t\t\"135.234.64.0/24\",\n\t\t\t\t\"172.182.193.80/28\",\n\t\t\t\t\"172.182.193.224/28\",\n\t\t\t\t\"172.182.194.32/28\",\n\t\t\t\t\"172.182.194.144/28\",\n\t\t\t\t\"172.182.195.48/28\",\n\t\t\t\t\"172.182.209.208/28\",\n\t\t\t\t\"172.182.211.192/28\",\n\t\t\t\t\"172.182.213.192/28\",\n\t\t\t\t\"172.182.224.0/28\",\n\t\t\t\t\"172.203.190.80/28\",\n\t\t\t\t\"172.203.190.112/28\",\n\t\t\t\t\"172.203.190.128/28\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"ChatGPT-User\",\n\t\t\t\"url\": \"https://openai.com/chatgpt-user.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"4.151.71.176/28\",\n\t\t\t\t\"4.151.119.48/28\",\n\t\t\t\t\"4.189.118.208/28\",\n\t\t\t\t\"4.189.119.48/28\",\n\t\t\t\t\"4.197.22.112/28\",\n\t\t\t\t\"4.197.64.0/28\",\n\t\t\t\t\"4.197.64.16/28\",\n\t\t\t\t\"4.197.64.48/28\",\n\t\t\t\t\"4.197.64.64/28\",\n\t\t\t\t\"4.197.115.112/28\",\n\t\t\t\t\"4.201.232.64/28\",\n\t\t\t\t\"4.205.128.176/28\",\n\t\t\t\t\"4.218.24.64/28\",\n\t\t\t\t\"4.226.200.16/28\",\n\t\t\t\t\"4.226.226.32/28\",\n\t\t\t\t\"9.129.0.0/16\",\n\t\t\t\t\"9.160.34.144/28\",\n\t\t\t\t\"9.160.36.16/28\",\n\t\t\t\t\"9.160.96.16/28\",\n\t\t\t\t\"9.160.128.16/28\",\n\t\t\t\t\"9.160.128.64/28\",\n\t\t\t\t\"9.160.163.128/28\",\n\t\t\t\t\"9.163.101.48/28\",\n\t\t\t\t\"9.205.8.48/28\",\n\t\t\t\t\"9.205.8.64/28\",\n\t\t\t\t\"9.205.25.128/28\",\n\t\t\t\t\"9.205.30.128/28\",\n\t\t\t\t\"9.205.30.144/28\",\n\t\t\t\t\"9.205.30.176/28\",\n\t\t\t\t\"9.205.128.32/28\",\n\t\t\t\t\"9.205.128.48/28\",\n\t\t\t\t\"9.223.181.208/28\",\n\t\t\t\t\"9.234.96.192/28\",\n\t\t\t\t\"9.234.97.96/28\",\n\t\t\t\t\"13.65.138.112/28\",\n\t\t\t\t\"13.67.72.16/28\",\n\t\t\t\t\"13.71.2.208/28\",\n\t\t\t\t\"13.76.32.208/28\",\n\t\t\t\t\"13.76.116.80/28\",\n\t\t\t\t\"13.83.167.128/28\",\n\t\t\t\t\"13.83.237.176/28\",\n\t\t\t\t\"20.45.178.144/28\",\n\t\t\t\t\"20.55.229.144/28\",\n\t\t\t\t\"20.57.199.192/28\",\n\t\t\t\t\"20.63.180.96/28\",\n\t\t\t\t\"20.63.221.64/28\",\n\t\t\t\t\"20.78.130.48/28\",\n\t\t\t\t\"20.83.243.176/28\",\n\t\t\t\t\"20.98.18.80/28\",\n\t\t\t\t\"20.102.212.144/28\",\n\t\t\t\t\"20.113.211.112/28\",\n\t\t\t\t\"20.113.225.112/28\",\n\t\t\t\t\"20.125.112.224/28\",\n\t\t\t\t\"20.125.144.144/28\",\n\t\t\t\t\"20.161.75.208/28\",\n\t\t\t\t\"20.168.7.192/28\",\n\t\t\t\t\"20.169.73.32/28\",\n\t\t\t\t\"20.169.73.64/28\",\n\t\t\t\t\"20.169.78.48/28\",\n\t\t\t\t\"20.169.78.64/28\",\n\t\t\t\t\"20.169.78.128/28\",\n\t\t\t\t\"20.169.78.160/28\",\n\t\t\t\t\"20.169.78.192/28\",\n\t\t\t\t\"20.169.86.224/28\",\n\t\t\t\t\"20.169.86.240/28\",\n\t\t\t\t\"20.170.184.16/28\",\n\t\t\t\t\"20.170.184.32/28\",\n\t\t\t\t\"20.170.184.48/28\",\n\t\t\t\t\"20.170.184.64/28\",\n\t\t\t\t\"20.170.184.80/28\",\n\t\t\t\t\"20.172.29.32/28\",\n\t\t\t\t\"20.199.211.160/28\",\n\t\t\t\t\"20.199.242.0/28\",\n\t\t\t\t\"20.200.212.240/28\",\n\t\t\t\t\"20.206.101.192/28\",\n\t\t\t\t\"20.210.211.192/28\",\n\t\t\t\t\"20.212.62.208/28\",\n\t\t\t\t\"20.215.187.208/28\",\n\t\t\t\t\"20.215.219.128/28\",\n\t\t\t\t\"20.215.219.160/28\",\n\t\t\t\t\"20.215.219.208/28\",\n\t\t\t\t\"20.218.30.240/28\",\n\t\t\t\t\"20.219.71.192/28\",\n\t\t\t\t\"20.219.161.192/28\",\n\t\t\t\t\"20.219.184.96/28\",\n\t\t\t\t\"20.222.36.192/28\",\n\t\t\t\t\"20.227.140.32/28\",\n\t\t\t\t\"20.228.106.176/28\",\n\t\t\t\t\"20.235.87.224/28\",\n\t\t\t\t\"20.249.63.208/28\",\n\t\t\t\t\"20.250.6.128/28\",\n\t\t\t\t\"20.250.136.64/28\",\n\t\t\t\t\"20.254.201.208/28\",\n\t\t\t\t\"23.98.142.176/28\",\n\t\t\t\t\"23.98.186.64/28\",\n\t\t\t\t\"23.98.186.96/28\",\n\t\t\t\t\"23.98.186.176/28\",\n\t\t\t\t\"23.98.186.192/28\",\n\t\t\t\t\"23.101.217.176/28\",\n\t\t\t\t\"23.102.140.144/28\",\n\t\t\t\t\"23.102.141.32/28\",\n\t\t\t\t\"40.74.200.208/28\",\n\t\t\t\t\"40.81.67.96/28\",\n\t\t\t\t\"40.81.234.144/28\",\n\t\t\t\t\"40.84.221.208/28\",\n\t\t\t\t\"40.84.221.224/28\",\n\t\t\t\t\"40.116.73.208/28\",\n\t\t\t\t\"40.119.36.240/28\",\n\t\t\t\t\"40.124.161.0/28\",\n\t\t\t\t\"48.221.40.176/28\",\n\t\t\t\t\"48.221.184.80/28\",\n\t\t\t\t\"48.221.184.96/28\",\n\t\t\t\t\"51.8.155.48/28\",\n\t\t\t\t\"51.8.155.112/28\",\n\t\t\t\t\"51.57.0.96/28\",\n\t\t\t\t\"51.59.24.64/28\",\n\t\t\t\t\"51.59.24.80/28\",\n\t\t\t\t\"51.59.48.80/28\",\n\t\t\t\t\"51.116.2.80/28\",\n\t\t\t\t\"51.116.221.96/28\",\n\t\t\t\t\"52.148.129.32/28\",\n\t\t\t\t\"52.156.77.144/28\",\n\t\t\t\t\"52.159.227.32/28\",\n\t\t\t\t\"52.159.249.96/28\",\n\t\t\t\t\"52.161.49.96/28\",\n\t\t\t\t\"52.165.212.48/28\",\n\t\t\t\t\"52.172.129.160/28\",\n\t\t\t\t\"52.173.219.96/28\",\n\t\t\t\t\"52.173.219.112/28\",\n\t\t\t\t\"52.173.221.16/28\",\n\t\t\t\t\"52.173.221.176/28\",\n\t\t\t\t\"52.173.221.208/28\",\n\t\t\t\t\"52.173.234.16/28\",\n\t\t\t\t\"52.173.234.80/28\",\n\t\t\t\t\"52.183.217.240/28\",\n\t\t\t\t\"52.190.137.16/28\",\n\t\t\t\t\"52.190.137.144/28\",\n\t\t\t\t\"52.190.139.48/28\",\n\t\t\t\t\"52.190.142.64/28\",\n\t\t\t\t\"52.190.190.16/28\",\n\t\t\t\t\"52.190.251.112/28\",\n\t\t\t\t\"52.225.75.208/28\",\n\t\t\t\t\"52.231.30.48/28\",\n\t\t\t\t\"52.231.34.176/28\",\n\t\t\t\t\"52.231.39.144/28\",\n\t\t\t\t\"52.231.50.64/28\",\n\t\t\t\t\"52.236.94.144/28\",\n\t\t\t\t\"52.241.146.208/28\",\n\t\t\t\t\"52.242.132.224/28\",\n\t\t\t\t\"52.242.132.240/28\",\n\t\t\t\t\"52.255.109.80/28\",\n\t\t\t\t\"52.255.109.96/28\",\n\t\t\t\t\"52.255.109.144/28\",\n\t\t\t\t\"52.255.111.0/28\",\n\t\t\t\t\"52.255.111.80/28\",\n\t\t\t\t\"57.154.174.112/28\",\n\t\t\t\t\"57.154.175.0/28\",\n\t\t\t\t\"57.154.187.32/28\",\n\t\t\t\t\"68.154.28.96/28\",\n\t\t\t\t\"68.220.57.64/28\",\n\t\t\t\t\"70.153.32.16/28\",\n\t\t\t\t\"70.153.32.32/28\",\n\t\t\t\t\"70.153.87.224/28\",\n\t\t\t\t\"70.153.189.192/28\",\n\t\t\t\t\"70.156.152.96/28\",\n\t\t\t\t\"74.7.35.48/28\",\n\t\t\t\t\"74.7.35.112/28\",\n\t\t\t\t\"74.7.36.64/28\",\n\t\t\t\t\"74.7.36.80/28\",\n\t\t\t\t\"74.7.36.96/28\",\n\t\t\t\t\"74.161.200.96/28\",\n\t\t\t\t\"74.224.217.64/28\",\n\t\t\t\t\"74.226.253.160/28\",\n\t\t\t\t\"74.248.37.160/28\",\n\t\t\t\t\"85.211.128.16/28\",\n\t\t\t\t\"85.211.128.32/28\",\n\t\t\t\t\"104.208.184.192/28\",\n\t\t\t\t\"104.210.139.192/28\",\n\t\t\t\t\"104.210.139.224/28\",\n\t\t\t\t\"128.85.198.32/28\",\n\t\t\t\t\"132.196.82.48/28\",\n\t\t\t\t\"134.33.102.192/28\",\n\t\t\t\t\"134.138.52.16/28\",\n\t\t\t\t\"134.138.52.64/28\",\n\t\t\t\t\"134.138.52.96/28\",\n\t\t\t\t\"134.138.52.128/28\",\n\t\t\t\t\"134.138.57.64/28\",\n\t\t\t\t\"134.138.57.80/28\",\n\t\t\t\t\"134.138.57.176/28\",\n\t\t\t\t\"134.138.58.32/28\",\n\t\t\t\t\"134.149.233.80/28\",\n\t\t\t\t\"135.13.64.240/28\",\n\t\t\t\t\"135.116.136.160/28\",\n\t\t\t\t\"135.220.73.208/28\",\n\t\t\t\t\"135.220.73.240/28\",\n\t\t\t\t\"135.222.99.32/28\",\n\t\t\t\t\"135.237.131.208/28\",\n\t\t\t\t\"135.237.133.48/28\",\n\t\t\t\t\"137.135.191.176/28\",\n\t\t\t\t\"138.91.46.96/28\",\n\t\t\t\t\"145.132.136.96/28\",\n\t\t\t\t\"158.158.5.32/28\",\n\t\t\t\t\"168.63.252.240/28\",\n\t\t\t\t\"172.162.248.64/28\",\n\t\t\t\t\"172.170.1.80/28\",\n\t\t\t\t\"172.170.225.0/28\",\n\t\t\t\t\"172.170.241.80/28\",\n\t\t\t\t\"172.172.206.48/28\",\n\t\t\t\t\"172.175.152.224/28\",\n\t\t\t\t\"172.178.140.144/28\",\n\t\t\t\t\"172.178.141.112/28\",\n\t\t\t\t\"172.178.141.128/28\",\n\t\t\t\t\"172.183.143.224/28\",\n\t\t\t\t\"172.183.222.128/28\",\n\t\t\t\t\"172.192.112.208/28\",\n\t\t\t\t\"172.197.160.192/28\",\n\t\t\t\t\"172.197.203.16/28\",\n\t\t\t\t\"172.198.58.176/28\",\n\t\t\t\t\"172.198.78.192/28\",\n\t\t\t\t\"172.198.79.112/28\",\n\t\t\t\t\"172.198.161.128/28\",\n\t\t\t\t\"172.199.137.80/28\",\n\t\t\t\t\"172.204.28.224/28\",\n\t\t\t\t\"172.204.96.80/28\",\n\t\t\t\t\"172.205.189.192/28\",\n\t\t\t\t\"172.206.38.240/28\",\n\t\t\t\t\"172.207.1.32/28\",\n\t\t\t\t\"172.212.172.160/28\",\n\t\t\t\t\"172.215.215.32/28\",\n\t\t\t\t\"191.232.238.96/28\",\n\t\t\t\t\"191.233.197.0/28\",\n\t\t\t\t\"191.233.199.160/28\",\n\t\t\t\t\"191.234.167.144/28\",\n\t\t\t\t\"191.235.99.160/28\",\n\t\t\t\t\"191.237.249.64/28\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"ClaudeBot\",\n\t\t\t\"url\": \"https://claude.com/crawling/bots.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"34.152.124.140/32\",\n\t\t\t\t\"34.153.21.190/32\",\n\t\t\t\t\"34.162.27.34/32\",\n\t\t\t\t\"34.162.86.102/32\",\n\t\t\t\t\"34.162.90.102/32\",\n\t\t\t\t\"34.162.111.78/32\",\n\t\t\t\t\"34.162.153.71/32\",\n\t\t\t\t\"34.162.191.81/32\",\n\t\t\t\t\"34.162.230.222/32\",\n\t\t\t\t\"34.162.244.71/32\",\n\t\t\t\t\"34.186.252.4/32\",\n\t\t\t\t\"136.79.143.251/32\",\n\t\t\t\t\"136.83.20.215/32\",\n\t\t\t\t\"216.73.216.0/22\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"PerplexityBot\",\n\t\t\t\"url\": \"https://www.perplexity.com/perplexitybot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"3.222.232.239/32\",\n\t\t\t\t\"3.224.62.45/32\",\n\t\t\t\t\"18.97.1.228/32\",\n\t\t\t\t\"18.97.9.96/32\",\n\t\t\t\t\"18.97.9.97/32\",\n\t\t\t\t\"18.97.9.98/32\",\n\t\t\t\t\"18.97.9.99/32\",\n\t\t\t\t\"18.97.9.100/32\",\n\t\t\t\t\"18.97.9.101/32\",\n\t\t\t\t\"18.97.9.102/32\",\n\t\t\t\t\"18.97.9.103/32\",\n\t\t\t\t\"18.210.92.235/32\",\n\t\t\t\t\"107.20.236.150/32\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"Perplexity-User\",\n\t\t\t\"url\": \"https://www.perplexity.com/perplexity-user.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"18.97.43.80/32\",\n\t\t\t\t\"18.97.43.81/32\",\n\t\t\t\t\"18.97.43.82/32\",\n\t\t\t\t\"18.97.43.83/32\",\n\t\t\t\t\"18.97.43.84/32\",\n\t\t\t\t\"18.97.43.85/32\",\n\t\t\t\t\"18.97.43.86/32\",\n\t\t\t\t\"18.97.43.87/32\",\n\t\t\t\t\"34.193.163.52/32\",\n\t\t\t\t\"44.208.221.197/32\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"MistralAI-User\",\n\t\t\t\"url\": \"https://mistral.ai/mistralai-user-ips.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"20.240.160.1/32\",\n\t\t\t\t\"20.240.160.161/32\",\n\t\t\t\t\"20.240.194.83/32\",\n\t\t\t\t\"51.12.243.114/32\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"CCBot\",\n\t\t\t\"url\": \"https://index.commoncrawl.org/ccbot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"3.41.188.32/29\",\n\t\t\t\t\"18.97.9.168/29\",\n\t\t\t\t\"18.97.14.80/29\",\n\t\t\t\t\"18.97.14.88/30\"\n\t\t\t],\n\t\t\t\"ipv6\": [\n\t\t\t\t\"2600:1f28:365:8000::/56\"\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"DuckAssistBot\",\n\t\t\t\"url\": \"https://duckduckgo.com/duckassistbot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"4.144.148.23/32\",\n\t\t\t\t\"4.144.182.50/32\",\n\t\t\t\t\"4.144.227.142/32\",\n\t\t\t\t\"4.144.251.39/32\",\n\t\t\t\t\"4.149.142.7/32\",\n\t\t\t\t\"4.149.159.71/32\",\n\t\t\t\t\"4.149.159.102/32\",\n\t\t\t\t\"4.149.216.220/32\",\n\t\t\t\t\"4.150.142.218/32\",\n\t\t\t\t\"4.154.193.167/32\",\n\t\t\t\t\"4.156.30.123/32\",\n\t\t\t\t\"4.156.136.79/32\",\n\t\t\t\t\"4.156.154.107/32\",\n\t\t\t\t\"4.157.77.76/32\",\n\t\t\t\t\"4.172.49.103/32\",\n\t\t\t\t\"4.177.16.196/32\",\n\t\t\t\t\"4.182.10.198/32\",\n\t\t\t\t\"4.182.131.108/32\",\n\t\t\t\t\"4.195.133.120/32\",\n\t\t\t\t\"4.201.125.59/32\",\n\t\t\t\t\"4.201.141.71/32\",\n\t\t\t\t\"4.201.197.203/32\",\n\t\t\t\t\"4.201.206.133/32\",\n\t\t\t\t\"4.201.220.8/32\",\n\t\t\t\t\"4.207.220.92/32\",\n\t\t\t\t\"4.209.224.56/32\",\n\t\t\t\t\"4.213.46.14/32\",\n\t\t\t\t\"4.226.40.135/32\",\n\t\t\t\t\"4.228.76.163/32\",\n\t\t\t\t\"4.237.155.54/32\",\n\t\t\t\t\"4.237.244.80/32\",\n\t\t\t\t\"4.242.119.13/32\",\n\t\t\t\t\"4.246.10.252/32\",\n\t\t\t\t\"4.248.234.26/32\",\n\t\t\t\t\"4.249.216.104/32\",\n\t\t\t\t\"4.249.223.51/32\",\n\t\t\t\t\"4.249.238.11/32\",\n\t\t\t\t\"4.254.123.56/32\",\n\t\t\t\t\"4.255.35.121/32\",\n\t\t\t\t\"4.255.39.80/32\",\n\t\t\t\t\"13.86.35.212/32\",\n\t\t\t\t\"13.89.106.77/32\",\n\t\t\t\t\"13.90.178.139/32\",\n\t\t\t\t\"20.3.1.178/32\",\n\t\t\t\t\"20.3.60.212/32\",\n\t\t\t\t\"20.8.252.26/32\",\n\t\t\t\t\"20.9.114.242/32\",\n\t\t\t\t\"20.12.141.99/32\",\n\t\t\t\t\"20.12.249.123/32\",\n\t\t\t\t\"20.12.252.21/32\",\n\t\t\t\t\"20.13.44.19/32\",\n\t\t\t\t\"20.15.142.178/32\",\n\t\t\t\t\"20.23.88.231/32\",\n\t\t\t\t\"20.29.149.44/32\",\n\t\t\t\t\"20.29.154.56/32\",\n\t\t\t\t\"20.40.133.240/32\",\n\t\t\t\t\"20.40.147.172/32\",\n\t\t\t\t\"20.43.150.85/32\",\n\t\t\t\t\"20.43.150.93/32\",\n\t\t\t\t\"20.43.152.28/32\",\n\t\t\t\t\"20.43.172.120/32\",\n\t\t\t\t\"20.44.222.1/32\",\n\t\t\t\t\"20.49.129.236/32\",\n\t\t\t\t\"20.49.136.28/32\",\n\t\t\t\t\"20.50.48.159/32\",\n\t\t\t\t\"20.50.48.192/32\",\n\t\t\t\t\"20.50.49.0/32\",\n\t\t\t\t\"20.50.49.25/32\",\n\t\t\t\t\"20.50.49.40/32\",\n\t\t\t\t\"20.50.49.55/32\",\n\t\t\t\t\"20.50.49.237/32\",\n\t\t\t\t\"20.50.50.46/32\",\n\t\t\t\t\"20.50.50.118/32\",\n\t\t\t\t\"20.50.50.121/32\",\n\t\t\t\t\"20.50.50.123/32\",\n\t\t\t\t\"20.50.50.130/32\",\n\t\t\t\t\"20.50.50.134/32\",\n\t\t\t\t\"20.50.50.145/32\",\n\t\t\t\t\"20.50.50.146/32\",\n\t\t\t\t\"20.50.50.163/32\",\n\t\t\t\t\"20.50.168.91/32\",\n\t\t\t\t\"20.53.78.106/32\",\n\t\t\t\t\"20.53.78.123/32\",\n\t\t\t\t\"20.53.78.138/32\",\n\t\t\t\t\"20.53.78.144/32\",\n\t\t\t\t\"20.53.78.236/32\",\n\t\t\t\t\"20.53.91.2/32\",\n\t\t\t\t\"20.53.92.211/32\",\n\t\t\t\t\"20.53.134.160/32\",\n\t\t\t\t\"20.54.224.39/32\",\n\t\t\t\t\"20.56.197.58/32\",\n\t\t\t\t\"20.56.197.63/32\",\n\t\t\t\t\"20.59.0.65/32\",\n\t\t\t\t\"20.61.34.40/32\",\n\t\t\t\t\"20.61.142.192/32\",\n\t\t\t\t\"20.62.224.44/32\",\n\t\t\t\t\"20.64.185.45/32\",\n\t\t\t\t\"20.69.74.250/32\",\n\t\t\t\t\"20.69.129.205/32\",\n\t\t\t\t\"20.69.131.45/32\",\n\t\t\t\t\"20.69.138.149/32\",\n\t\t\t\t\"20.69.138.189/32\",\n\t\t\t\t\"20.69.144.137/32\",\n\t\t\t\t\"20.69.146.171/32\",\n\t\t\t\t\"20.69.147.21/32\",\n\t\t\t\t\"20.69.171.204/32\",\n\t\t\t\t\"20.71.12.143/32\",\n\t\t\t\t\"20.71.69.210/32\",\n\t\t\t\t\"20.72.197.110/32\",\n\t\t\t\t\"20.72.203.250/32\",\n\t\t\t\t\"20.72.218.29/32\",\n\t\t\t\t\"20.72.221.33/32\",\n\t\t\t\t\"20.72.221.45/32\",\n\t\t\t\t\"20.72.225.13/32\",\n\t\t\t\t\"20.72.242.93/32\",\n\t\t\t\t\"20.72.242.209/32\",\n\t\t\t\t\"20.73.132.240/32\",\n\t\t\t\t\"20.73.202.147/32\",\n\t\t\t\t\"20.75.144.152/32\",\n\t\t\t\t\"20.75.254.61/32\",\n\t\t\t\t\"20.77.146.108/32\",\n\t\t\t\t\"20.79.226.26/32\",\n\t\t\t\t\"20.79.238.198/32\",\n\t\t\t\t\"20.79.239.66/32\",\n\t\t\t\t\"20.80.114.203/32\",\n\t\t\t\t\"20.80.120.41/32\",\n\t\t\t\t\"20.80.129.80/32\",\n\t\t\t\t\"20.80.135.7/32\",\n\t\t\t\t\"20.80.156.88/32\",\n\t\t\t\t\"20.81.115.13/32\",\n\t\t\t\t\"20.82.24.207/32\",\n\t\t\t\t\"20.84.138.61/32\",\n\t\t\t\t\"20.84.219.45/32\",\n\t\t\t\t\"20.84.251.223/32\",\n\t\t\t\t\"20.85.138.75/32\",\n\t\t\t\t\"20.85.167.56/32\",\n\t\t\t\t\"20.85.187.146/32\",\n\t\t\t\t\"20.88.168.65/32\",\n\t\t\t\t\"20.93.28.24/32\",\n\t\t\t\t\"20.93.49.96/32\",\n\t\t\t\t\"20.99.154.113/32\",\n\t\t\t\t\"20.99.255.235/32\",\n\t\t\t\t\"20.100.136.36/32\",\n\t\t\t\t\"20.100.140.155/32\",\n\t\t\t\t\"20.101.17.173/32\",\n\t\t\t\t\"20.102.34.205/32\",\n\t\t\t\t\"20.109.129.162/32\",\n\t\t\t\t\"20.109.130.190/32\",\n\t\t\t\t\"20.109.241.18/32\",\n\t\t\t\t\"20.112.58.44/32\",\n\t\t\t\t\"20.113.3.121/32\",\n\t\t\t\t\"20.113.14.159/32\",\n\t\t\t\t\"20.115.181.187/32\",\n\t\t\t\t\"20.118.11.251/32\",\n\t\t\t\t\"20.119.127.146/32\",\n\t\t\t\t\"20.121.75.32/32\",\n\t\t\t\t\"20.166.171.150/32\",\n\t\t\t\t\"20.170.75.54/32\",\n\t\t\t\t\"20.175.232.228/32\",\n\t\t\t\t\"20.185.79.15/32\",\n\t\t\t\t\"20.185.79.47/32\",\n\t\t\t\t\"20.190.21.172/32\",\n\t\t\t\t\"20.191.44.16/32\",\n\t\t\t\t\"20.191.44.22/32\",\n\t\t\t\t\"20.191.44.119/32\",\n\t\t\t\t\"20.191.44.234/32\",\n\t\t\t\t\"20.191.45.212/32\",\n\t\t\t\t\"20.193.12.126/32\",\n\t\t\t\t\"20.193.24.10/32\",\n\t\t\t\t\"20.193.24.251/32\",\n\t\t\t\t\"20.193.25.197/32\",\n\t\t\t\t\"20.193.27.215/32\",\n\t\t\t\t\"20.193.45.113/32\",\n\t\t\t\t\"20.195.58.189/32\",\n\t\t\t\t\"20.195.108.47/32\",\n\t\t\t\t\"20.197.209.11/32\",\n\t\t\t\t\"20.197.209.27/32\",\n\t\t\t\t\"20.198.132.126/32\",\n\t\t\t\t\"20.198.152.208/32\",\n\t\t\t\t\"20.201.15.208/32\",\n\t\t\t\t\"20.204.228.200/32\",\n\t\t\t\t\"20.204.240.172/32\",\n\t\t\t\t\"20.204.241.148/32\",\n\t\t\t\t\"20.204.242.19/32\",\n\t\t\t\t\"20.204.242.101/32\",\n\t\t\t\t\"20.204.243.55/32\",\n\t\t\t\t\"20.204.246.81/32\",\n\t\t\t\t\"20.204.246.254/32\",\n\t\t\t\t\"20.207.72.11/32\",\n\t\t\t\t\"20.207.72.21/32\",\n\t\t\t\t\"20.207.72.110/32\",\n\t\t\t\t\"20.207.72.113/32\",\n\t\t\t\t\"20.207.97.190/32\",\n\t\t\t\t\"20.207.99.197/32\",\n\t\t\t\t\"20.207.107.181/32\",\n\t\t\t\t\"20.207.117.66/32\",\n\t\t\t\t\"20.212.90.107/32\",\n\t\t\t\t\"20.212.139.34/32\",\n\t\t\t\t\"20.216.200.223/32\",\n\t\t\t\t\"20.219.43.246/32\",\n\t\t\t\t\"20.219.45.67/32\",\n\t\t\t\t\"20.219.45.190/32\",\n\t\t\t\t\"20.221.10.115/32\",\n\t\t\t\t\"20.221.84.185/32\",\n\t\t\t\t\"20.226.133.105/32\",\n\t\t\t\t\"20.231.254.179/32\",\n\t\t\t\t\"20.232.51.46/32\",\n\t\t\t\t\"20.232.249.95/32\",\n\t\t\t\t\"20.236.40.93/32\",\n\t\t\t\t\"20.236.195.196/32\",\n\t\t\t\t\"20.236.198.192/32\",\n\t\t\t\t\"20.236.227.113/32\",\n\t\t\t\t\"20.236.233.23/32\",\n\t\t\t\t\"20.236.247.180/32\",\n\t\t\t\t\"20.237.1.51/32\",\n\t\t\t\t\"20.241.135.19/32\",\n\t\t\t\t\"20.241.147.151/32\",\n\t\t\t\t\"20.241.153.203/32\",\n\t\t\t\t\"20.241.164.5/32\",\n\t\t\t\t\"20.241.241.100/32\",\n\t\t\t\t\"20.242.133.53/32\",\n\t\t\t\t\"20.242.135.165/32\",\n\t\t\t\t\"20.242.211.139/32\",\n\t\t\t\t\"20.242.215.53/32\",\n\t\t\t\t\"20.242.237.240/32\",\n\t\t\t\t\"20.246.132.168/32\",\n\t\t\t\t\"20.246.225.34/32\",\n\t\t\t\t\"20.247.235.11/32\",\n\t\t\t\t\"20.250.51.113/32\",\n\t\t\t\t\"20.252.9.6/32\",\n\t\t\t\t\"20.252.16.193/32\",\n\t\t\t\t\"20.252.41.18/32\",\n\t\t\t\t\"20.252.63.162/32\",\n\t\t\t\t\"20.252.88.10/32\",\n\t\t\t\t\"20.253.59.76/32\",\n\t\t\t\t\"20.253.96.199/32\",\n\t\t\t\t\"20.253.114.51/32\",\n\t\t\t\t\"40.64.67.220/32\",\n\t\t\t\t\"40.64.78.45/32\",\n\t\t\t\t\"40.64.88.145/32\",\n\t\t\t\t\"40.64.105.247/32\",\n\t\t\t\t\"40.64.106.11/32\",\n\t\t\t\t\"40.64.106.152/32\",\n\t\t\t\t\"40.65.78.86/32\",\n\t\t\t\t\"40.76.162.17/32\",\n\t\t\t\t\"40.76.162.191/32\",\n\t\t\t\t\"40.76.162.208/32\",\n\t\t\t\t\"40.76.162.247/32\",\n\t\t\t\t\"40.76.163.7/32\",\n\t\t\t\t\"40.76.163.23/32\",\n\t\t\t\t\"40.76.173.151/32\",\n\t\t\t\t\"40.81.250.205/32\",\n\t\t\t\t\"40.82.218.203/32\",\n\t\t\t\t\"40.88.21.235/32\",\n\t\t\t\t\"40.88.195.62/32\",\n\t\t\t\t\"40.89.243.175/32\",\n\t\t\t\t\"40.114.182.45/32\",\n\t\t\t\t\"40.114.182.153/32\",\n\t\t\t\t\"40.114.182.172/32\",\n\t\t\t\t\"40.114.183.88/32\",\n\t\t\t\t\"40.114.183.196/32\",\n\t\t\t\t\"40.114.183.251/32\",\n\t\t\t\t\"40.119.232.50/32\",\n\t\t\t\t\"40.119.232.146/32\",\n\t\t\t\t\"40.119.232.215/32\",\n\t\t\t\t\"40.119.232.218/32\",\n\t\t\t\t\"40.119.232.251/32\",\n\t\t\t\t\"40.121.121.54/32\",\n\t\t\t\t\"40.127.154.196/32\",\n\t\t\t\t\"48.192.16.86/32\",\n\t\t\t\t\"48.192.74.178/32\",\n\t\t\t\t\"48.200.33.227/32\",\n\t\t\t\t\"48.202.155.200/32\",\n\t\t\t\t\"48.202.171.124/32\",\n\t\t\t\t\"48.204.32.87/32\",\n\t\t\t\t\"48.206.18.152/32\",\n\t\t\t\t\"48.217.23.236/32\",\n\t\t\t\t\"48.217.129.210/32\",\n\t\t\t\t\"48.217.212.89/32\",\n\t\t\t\t\"48.223.209.110/32\",\n\t\t\t\t\"51.8.31.28/32\",\n\t\t\t\t\"51.8.71.117/32\",\n\t\t\t\t\"51.8.253.152/32\",\n\t\t\t\t\"51.104.144.31/32\",\n\t\t\t\t\"51.104.146.225/32\",\n\t\t\t\t\"51.104.146.235/32\",\n\t\t\t\t\"51.104.160.167/32\",\n\t\t\t\t\"51.104.160.177/32\",\n\t\t\t\t\"51.104.161.32/32\",\n\t\t\t\t\"51.104.162.149/32\",\n\t\t\t\t\"51.104.163.250/32\",\n\t\t\t\t\"51.104.164.109/32\",\n\t\t\t\t\"51.104.164.147/32\",\n\t\t\t\t\"51.104.164.189/32\",\n\t\t\t\t\"51.104.164.215/32\",\n\t\t\t\t\"51.104.166.111/32\",\n\t\t\t\t\"51.104.167.19/32\",\n\t\t\t\t\"51.104.167.52/32\",\n\t\t\t\t\"51.104.167.54/32\",\n\t\t\t\t\"51.104.167.61/32\",\n\t\t\t\t\"51.104.167.71/32\",\n\t\t\t\t\"51.104.167.87/32\",\n\t\t\t\t\"51.104.167.88/32\",\n\t\t\t\t\"51.104.167.95/32\",\n\t\t\t\t\"51.104.167.96/32\",\n\t\t\t\t\"51.104.167.104/32\",\n\t\t\t\t\"51.104.167.110/32\",\n\t\t\t\t\"51.104.180.26/32\",\n\t\t\t\t\"51.104.180.47/32\",\n\t\t\t\t\"51.104.180.53/32\",\n\t\t\t\t\"51.107.40.209/32\",\n\t\t\t\t\"51.116.131.221/32\",\n\t\t\t\t\"51.120.48.122/32\",\n\t\t\t\t\"51.138.90.161/32\",\n\t\t\t\t\"51.138.90.206/32\",\n\t\t\t\t\"51.138.90.233/32\",\n\t\t\t\t\"51.143.114.45/32\",\n\t\t\t\t\"51.145.185.16/32\",\n\t\t\t\t\"52.142.24.149/32\",\n\t\t\t\t\"52.142.26.175/32\",\n\t\t\t\t\"52.143.95.162/32\",\n\t\t\t\t\"52.143.95.204/32\",\n\t\t\t\t\"52.143.241.111/32\",\n\t\t\t\t\"52.143.242.6/32\",\n\t\t\t\t\"52.143.243.117/32\",\n\t\t\t\t\"52.143.244.81/32\",\n\t\t\t\t\"52.143.247.235/32\",\n\t\t\t\t\"52.146.57.51/32\",\n\t\t\t\t\"52.146.58.236/32\",\n\t\t\t\t\"52.146.59.12/32\",\n\t\t\t\t\"52.146.59.154/32\",\n\t\t\t\t\"52.146.59.156/32\",\n\t\t\t\t\"52.146.63.80/32\",\n\t\t\t\t\"52.148.161.87/32\",\n\t\t\t\t\"52.148.165.38/32\",\n\t\t\t\t\"52.149.25.43/32\",\n\t\t\t\t\"52.149.28.18/32\",\n\t\t\t\t\"52.149.28.83/32\",\n\t\t\t\t\"52.149.30.45/32\",\n\t\t\t\t\"52.149.56.151/32\",\n\t\t\t\t\"52.149.58.27/32\",\n\t\t\t\t\"52.149.58.69/32\",\n\t\t\t\t\"52.149.58.139/32\",\n\t\t\t\t\"52.149.58.173/32\",\n\t\t\t\t\"52.149.60.38/32\",\n\t\t\t\t\"52.149.61.51/32\",\n\t\t\t\t\"52.154.60.82/32\",\n\t\t\t\t\"52.154.169.50/32\",\n\t\t\t\t\"52.154.169.200/32\",\n\t\t\t\t\"52.154.170.26/32\",\n\t\t\t\t\"52.154.170.28/32\",\n\t\t\t\t\"52.154.170.88/32\",\n\t\t\t\t\"52.154.170.96/32\",\n\t\t\t\t\"52.154.170.113/32\",\n\t\t\t\t\"52.154.170.117/32\",\n\t\t\t\t\"52.154.170.122/32\",\n\t\t\t\t\"52.154.170.209/32\",\n\t\t\t\t\"52.154.170.229/32\",\n\t\t\t\t\"52.154.170.243/32\",\n\t\t\t\t\"52.154.171.0/32\",\n\t\t\t\t\"52.154.171.44/32\",\n\t\t\t\t\"52.154.171.70/32\",\n\t\t\t\t\"52.154.171.87/32\",\n\t\t\t\t\"52.154.171.150/32\",\n\t\t\t\t\"52.154.171.196/32\",\n\t\t\t\t\"52.154.171.205/32\",\n\t\t\t\t\"52.154.171.235/32\",\n\t\t\t\t\"52.154.171.250/32\",\n\t\t\t\t\"52.154.172.2/32\",\n\t\t\t\t\"52.154.204.93/32\",\n\t\t\t\t\"52.185.93.208/32\",\n\t\t\t\t\"52.186.37.211/32\",\n\t\t\t\t\"52.188.89.106/32\",\n\t\t\t\t\"52.189.70.103/32\",\n\t\t\t\t\"52.190.37.160/32\",\n\t\t\t\t\"52.191.82.242/32\",\n\t\t\t\t\"52.191.222.158/32\",\n\t\t\t\t\"52.224.16.221/32\",\n\t\t\t\t\"52.224.16.229/32\",\n\t\t\t\t\"52.224.19.152/32\",\n\t\t\t\t\"52.224.20.174/32\",\n\t\t\t\t\"52.224.20.181/32\",\n\t\t\t\t\"52.224.20.186/32\",\n\t\t\t\t\"52.224.20.190/32\",\n\t\t\t\t\"52.224.20.193/32\",\n\t\t\t\t\"52.224.20.203/32\",\n\t\t\t\t\"52.224.20.204/32\",\n\t\t\t\t\"52.224.20.223/32\",\n\t\t\t\t\"52.224.20.227/32\",\n\t\t\t\t\"52.224.20.249/32\",\n\t\t\t\t\"52.224.21.4/32\",\n\t\t\t\t\"52.224.21.19/32\",\n\t\t\t\t\"52.224.21.20/32\",\n\t\t\t\t\"52.224.21.23/32\",\n\t\t\t\t\"52.224.21.27/32\",\n\t\t\t\t\"52.224.21.49/32\",\n\t\t\t\t\"52.224.21.51/32\",\n\t\t\t\t\"52.224.21.53/32\",\n\t\t\t\t\"52.224.21.55/32\",\n\t\t\t\t\"52.224.21.61/32\",\n\t\t\t\t\"52.224.147.21/32\",\n\t\t\t\t\"52.224.191.245/32\",\n\t\t\t\t\"52.242.224.168/32\",\n\t\t\t\t\"52.250.46.221/32\",\n\t\t\t\t\"57.151.44.140/32\",\n\t\t\t\t\"57.152.17.237/32\",\n\t\t\t\t\"57.152.62.107/32\",\n\t\t\t\t\"57.152.72.128/32\",\n\t\t\t\t\"57.155.18.44/32\",\n\t\t\t\t\"57.165.205.172/32\",\n\t\t\t\t\"57.167.88.38/32\",\n\t\t\t\t\"64.236.15.33/32\",\n\t\t\t\t\"64.236.93.100/32\",\n\t\t\t\t\"64.236.118.43/32\",\n\t\t\t\t\"68.219.152.220/32\",\n\t\t\t\t\"72.144.95.185/32\",\n\t\t\t\t\"74.163.169.106/32\",\n\t\t\t\t\"74.163.208.206/32\",\n\t\t\t\t\"74.179.200.126/32\",\n\t\t\t\t\"74.179.232.116/32\",\n\t\t\t\t\"74.242.194.48/32\",\n\t\t\t\t\"98.70.219.37/32\",\n\t\t\t\t\"104.43.54.127/32\",\n\t\t\t\t\"104.43.55.116/32\",\n\t\t\t\t\"104.43.55.117/32\",\n\t\t\t\t\"104.43.55.166/32\",\n\t\t\t\t\"104.43.55.167/32\",\n\t\t\t\t\"108.141.83.74/32\",\n\t\t\t\t\"128.203.82.154/32\",\n\t\t\t\t\"128.203.132.152/32\",\n\t\t\t\t\"128.203.181.79/32\",\n\t\t\t\t\"128.203.214.241/32\",\n\t\t\t\t\"128.203.236.110/32\",\n\t\t\t\t\"130.107.228.224/32\",\n\t\t\t\t\"131.145.39.118/32\",\n\t\t\t\t\"131.189.183.241/32\",\n\t\t\t\t\"132.164.209.198/32\",\n\t\t\t\t\"132.196.106.146/32\",\n\t\t\t\t\"132.220.1.71/32\",\n\t\t\t\t\"134.33.207.189/32\",\n\t\t\t\t\"135.171.251.169/32\",\n\t\t\t\t\"135.233.63.237/32\",\n\t\t\t\t\"135.234.221.112/32\",\n\t\t\t\t\"135.235.183.95/32\",\n\t\t\t\t\"172.168.12.93/32\",\n\t\t\t\t\"172.168.43.43/32\",\n\t\t\t\t\"172.168.51.216/32\",\n\t\t\t\t\"172.168.53.53/32\",\n\t\t\t\t\"172.168.81.217/32\",\n\t\t\t\t\"172.168.94.160/32\",\n\t\t\t\t\"172.168.115.250/32\",\n\t\t\t\t\"172.168.133.76/32\",\n\t\t\t\t\"172.168.137.102/32\",\n\t\t\t\t\"172.168.152.88/32\",\n\t\t\t\t\"172.168.173.230/32\",\n\t\t\t\t\"172.168.190.186/32\",\n\t\t\t\t\"172.168.227.120/32\",\n\t\t\t\t\"172.168.254.119/32\",\n\t\t\t\t\"172.169.7.33/32\",\n\t\t\t\t\"172.169.17.165/32\",\n\t\t\t\t\"172.169.28.184/32\",\n\t\t\t\t\"172.169.36.43/32\",\n\t\t\t\t\"172.169.53.208/32\",\n\t\t\t\t\"172.169.60.134/32\",\n\t\t\t\t\"172.169.93.148/32\",\n\t\t\t\t\"172.169.164.109/32\",\n\t\t\t\t\"172.169.177.131/32\",\n\t\t\t\t\"172.169.181.237/32\",\n\t\t\t\t\"172.171.37.221/32\",\n\t\t\t\t\"172.179.105.87/32\",\n\t\t\t\t\"172.179.128.118/32\",\n\t\t\t\t\"172.188.250.220/32\",\n\t\t\t\t\"172.189.40.18/32\",\n\t\t\t\t\"172.193.184.42/32\",\n\t\t\t\t\"172.193.197.146/32\",\n\t\t\t\t\"172.193.245.229/32\",\n\t\t\t\t\"172.194.141.49/32\",\n\t\t\t\t\"172.199.55.212/32\",\n\t\t\t\t\"172.210.111.68/32\",\n\t\t\t\t\"191.233.3.197/32\",\n\t\t\t\t\"191.233.3.202/32\",\n\t\t\t\t\"191.234.216.4/32\",\n\t\t\t\t\"191.234.216.178/32\",\n\t\t\t\t\"191.235.201.214/32\",\n\t\t\t\t\"191.235.202.38/32\",\n\t\t\t\t\"191.235.202.48/32\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"Applebot\",\n\t\t\t\"url\": \"https://search.developer.apple.com/applebot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"17.166.20.0/24\",\n\t\t\t\t\"17.166.21.0/24\",\n\t\t\t\t\"17.166.22.0/24\",\n\t\t\t\t\"17.166.23.0/24\",\n\t\t\t\t\"17.166.24.0/24\",\n\t\t\t\t\"17.166.25.0/24\",\n\t\t\t\t\"17.166.150.0/24\",\n\t\t\t\t\"17.166.151.0/24\",\n\t\t\t\t\"17.166.152.0/24\",\n\t\t\t\t\"17.166.153.0/24\",\n\t\t\t\t\"17.166.154.0/24\",\n\t\t\t\t\"17.166.155.0/24\",\n\t\t\t\t\"17.166.192.48/28\",\n\t\t\t\t\"17.166.232.0/24\",\n\t\t\t\t\"17.166.233.0/24\",\n\t\t\t\t\"17.166.234.0/24\",\n\t\t\t\t\"17.166.235.0/24\",\n\t\t\t\t\"17.166.236.0/24\",\n\t\t\t\t\"17.166.237.0/24\",\n\t\t\t\t\"17.166.240.48/28\",\n\t\t\t\t\"17.166.248.48/28\",\n\t\t\t\t\"17.241.193.160/27\",\n\t\t\t\t\"17.241.200.160/27\",\n\t\t\t\t\"17.241.208.160/27\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"Claude API\",\n\t\t\t\"url\": \"https://platform.claude.com/docs/en/api/ip-addresses.md\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"153.61.208.0/20\",\n\t\t\t\t\"160.79.104.0/21\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t}\n\t]\n}\n","const encoder = new TextEncoder();\nconst keys = new Map<string, Promise<CryptoKey>>();\n\nfunction importOrgKey(orgKey: string): Promise<CryptoKey> {\n\tlet key = keys.get(orgKey);\n\tif (!key) {\n\t\tkey = crypto.subtle.importKey(\n\t\t\t\"raw\",\n\t\t\tencoder.encode(orgKey),\n\t\t\t{ name: \"HMAC\", hash: \"SHA-256\" },\n\t\t\tfalse,\n\t\t\t[\"sign\"],\n\t\t);\n\t\tkey.catch(() => keys.delete(orgKey));\n\t\tkeys.set(orgKey, key);\n\t}\n\treturn key;\n}\n\nconst toHex = (bytes: ArrayBuffer): string =>\n\tArray.from(new Uint8Array(bytes), (byte) =>\n\t\tbyte.toString(16).padStart(2, \"0\"),\n\t).join(\"\");\n\nexport async function deriveMatchKey(\n\torgKey: string,\n\tip: string,\n): Promise<string> {\n\tconst signature = await crypto.subtle.sign(\n\t\t\"HMAC\",\n\t\tawait importOrgKey(orgKey),\n\t\tencoder.encode(ip),\n\t);\n\treturn toHex(signature);\n}\n","import { applyRules, type MatchRules } from \"./logic/decide\";\nimport { settings } from \"./settings\";\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n\ttypeof value === \"object\" && value !== null;\n\nconst isStringArray = (value: unknown): value is string[] =>\n\tArray.isArray(value) && value.every((item) => typeof item === \"string\");\n\n/** The match rules in a rules file from the app (the `data` of its answer), or null when it does not have the expected form. A malformed range makes `applyRules` throw instead. */\nexport function parseRemoteRules(body: unknown): MatchRules | null {\n\tif (!isObject(body) || body.version !== 1) {\n\t\treturn null;\n\t}\n\tconst { tokens, ipRanges } = body;\n\tif (!isStringArray(tokens) || !Array.isArray(ipRanges)) {\n\t\treturn null;\n\t}\n\tconst ranges: MatchRules[\"ipRanges\"] = [];\n\tfor (const range of ipRanges) {\n\t\tif (\n\t\t\t!isObject(range) ||\n\t\t\t!isStringArray(range.ipv4) ||\n\t\t\t!isStringArray(range.ipv6)\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\t\tranges.push({ ipv4: range.ipv4, ipv6: range.ipv6 });\n\t}\n\treturn {\n\t\ttokens: tokens.map((token) => token.toLowerCase()),\n\t\tipRanges: ranges,\n\t};\n}\n\nlet lastAttemptOk = false;\nlet lastAttemptAt = Number.NEGATIVE_INFINITY;\n\nasync function fetchRules(apiUrl: string): Promise<boolean> {\n\tconst response = await fetch(`${apiUrl}/api/v2/agent-traffic/rules`, {\n\t\tsignal: AbortSignal.timeout(settings.requestTimeoutMs),\n\t});\n\tif (!response.ok) {\n\t\treturn false;\n\t}\n\tconst body: unknown = await response.json();\n\tconst rules = parseRemoteRules(isObject(body) ? body.data : null);\n\tif (!rules) {\n\t\treturn false;\n\t}\n\tapplyRules(rules);\n\treturn true;\n}\n\n/**\n * Starts one fetch of the rules file when the last attempt succeeded more than the refresh interval ago,\n * or failed more than the retry interval ago. Returns that work, or null.\n */\nexport function refreshRules(apiUrl: string): Promise<void> | null {\n\tconst now = Date.now();\n\tconst wait = lastAttemptOk ? settings.rulesRefreshMs : settings.rulesRetryMs;\n\tif (now - lastAttemptAt < wait) {\n\t\treturn null;\n\t}\n\tlastAttemptAt = now;\n\tlastAttemptOk = false;\n\treturn fetchRules(apiUrl).then(\n\t\t(ok) => {\n\t\t\tlastAttemptOk = ok;\n\t\t},\n\t\t() => undefined,\n\t);\n}\n","type WaitUntil = (promise: Promise<unknown>) => void;\ntype RequestContextStore = {\n\tget?: () => { waitUntil?: WaitUntil } | undefined;\n};\n\nconst REQUEST_CONTEXT = Symbol.for(\"@vercel/request-context\");\n\nexport function vercelRequestContext(): { waitUntil?: WaitUntil } | undefined {\n\tconst store: RequestContextStore | undefined = Reflect.get(\n\t\tglobalThis,\n\t\tREQUEST_CONTEXT,\n\t);\n\treturn store?.get?.();\n}\n\n/** The `waitUntil` that the Vercel runtime puts in the request context, the same one `@vercel/functions` reads. */\nexport function vercelWaitUntil(): WaitUntil | undefined {\n\tconst context = vercelRequestContext();\n\treturn context?.waitUntil\n\t\t? (promise) => context.waitUntil?.(promise)\n\t\t: undefined;\n}\n","import { vercelRequestContext } from \"./wait-until\";\n\nexport type Platform = \"vercel\" | \"cloudflare\" | \"cloudfront\";\n\n/** The platform this request runs on, or undefined on a runtime the relay does not know. Never throws. */\nexport function detectPlatform(): Platform | undefined {\n\ttry {\n\t\tconst context = vercelRequestContext();\n\t\tif (typeof context === \"object\" && context !== null) {\n\t\t\treturn \"vercel\";\n\t\t}\n\t\tif (globalThis.navigator?.userAgent === \"Cloudflare-Workers\") {\n\t\t\treturn \"cloudflare\";\n\t\t}\n\t} catch {}\n\treturn undefined;\n}\n","import { clientIp, HEADER_CAP } from \"./headers\";\nimport { detectPlatform, type Platform } from \"./platform\";\n\n/** `event.Records[0].cf.request` of a Lambda@Edge viewer-request event. */\nexport type CloudFrontRequest = {\n\tclientIp: string;\n\tmethod: string;\n\turi: string;\n\tquerystring: string;\n\theaders: Record<string, Array<{ key?: string; value: string }>>;\n};\n\nexport type RelayRequest = {\n\turl: string | null;\n\tmethod: string;\n\theaders: Headers;\n\tip: string | null;\n\tplatform: Platform | undefined;\n};\n\nexport function isCloudFrontRequest(\n\tinput: Request | CloudFrontRequest,\n): input is CloudFrontRequest {\n\treturn (\n\t\ttypeof Reflect.get(input, \"clientIp\") === \"string\" &&\n\t\ttypeof Reflect.get(input, \"uri\") === \"string\"\n\t);\n}\n\n/** The origin for a `host` header, or null when the value holds more than a host and an optional port. */\nfunction originOf(host: string | undefined): string | null {\n\tif (!host) {\n\t\treturn null;\n\t}\n\ttry {\n\t\tconst url = new URL(`https://${host}`);\n\t\tconst bare =\n\t\t\turl.pathname === \"/\" &&\n\t\t\turl.search === \"\" &&\n\t\t\turl.hash === \"\" &&\n\t\t\turl.username === \"\" &&\n\t\t\turl.password === \"\";\n\t\treturn bare ? url.origin : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** The hostname in a `host` header or a bare hostname, lowercased and without the port, or null when the value holds more. */\nexport function hostnameOf(host: string | undefined): string | null {\n\tconst origin = originOf(host);\n\treturn origin === null ? null : new URL(origin).hostname;\n}\n\nexport function cloudFrontHeader(\n\tinput: CloudFrontRequest,\n\tname: string,\n): string | null {\n\tconst value = input.headers?.[name]?.[0]?.value;\n\treturn typeof value === \"string\" ? value : null;\n}\n\n/** Copies only the headers the relay reads, so a cookie or a value `Headers` refuses never makes the request fail. */\nfunction cloudFrontHeaders(input: CloudFrontRequest[\"headers\"]): Headers {\n\tconst headers = new Headers();\n\tfor (const name of HEADER_CAP) {\n\t\tfor (const entry of input?.[name] ?? []) {\n\t\t\tif (typeof entry?.value !== \"string\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\theaders.append(name, entry.value);\n\t\t\t} catch {}\n\t\t}\n\t}\n\treturn headers;\n}\n\n/** The client can set `X-Forwarded-For` at viewer-request, so a CloudFront request takes its IP from `clientIp` alone. `url` is null when the host header is missing or malformed. */\nexport function toRelayRequest(\n\tinput: Request | CloudFrontRequest,\n): RelayRequest {\n\tif (!isCloudFrontRequest(input)) {\n\t\treturn {\n\t\t\turl: input.url,\n\t\t\tmethod: input.method,\n\t\t\theaders: input.headers,\n\t\t\tip: clientIp(input.headers),\n\t\t\tplatform: detectPlatform(),\n\t\t};\n\t}\n\tconst origin = originOf(input.headers?.host?.[0]?.value);\n\tconst query = input.querystring ? `?${input.querystring}` : \"\";\n\tconst ip = input.clientIp.trim().toLowerCase();\n\treturn {\n\t\turl: origin === null ? null : `${origin}${input.uri}${query}`,\n\t\tmethod: input.method,\n\t\theaders: cloudFrontHeaders(input.headers),\n\t\tip: ip ? ip : null,\n\t\tplatform: \"cloudfront\",\n\t};\n}\n","import {\n\tfetchAgentPage,\n\tloadSiteRules,\n\tmatchesPath,\n\tnormalizePath,\n\ttype SiteRules,\n} from \"./agent-page\";\nimport { readCappedHeaders } from \"./headers\";\nimport { type PostPolicy, postEvent } from \"./ingest\";\nimport { readKey } from \"./key\";\nimport { decide } from \"./logic\";\nimport { deriveMatchKey } from \"./match-key\";\nimport type { Platform } from \"./platform\";\nimport { refreshRules } from \"./refresh\";\nimport {\n\ttype CloudFrontRequest,\n\tcloudFrontHeader,\n\thostnameOf,\n\tisCloudFrontRequest,\n\ttoRelayRequest,\n} from \"./request\";\nimport { settings } from \"./settings\";\nimport { RELAY_VERSION } from \"./version\";\nimport { vercelWaitUntil } from \"./wait-until\";\n\nexport type { CloudFrontRequest };\n\n/** The response a Lambda@Edge handler returns in place of the request. */\nexport type CloudFrontResultResponse = {\n\tstatus: string;\n\tstatusDescription?: string;\n\theaders: Record<string, Array<{ key: string; value: string }>>;\n\tbody?: string;\n};\n\nexport type Region = keyof typeof settings.apiUrls;\n\nexport type RelayOptions = {\n\t/** The region of your WaniWani organization. Defaults to `\"us\"`. */\n\tregion?: Region;\n\t/** Overrides the host that `region` picks. */\n\tapiUrl?: string;\n\t/** Overrides `process.env.WANIWANI_AGENT_TRAFFIC_KEY`. */\n\tkey?: string;\n\t/** Keeps the background post alive after the response. Defaults to the `waitUntil` of the Vercel request context. */\n\twaitUntil?: (promise: Promise<unknown>) => void;\n\t/** Header names, and the fields `matchKey`, `userAgent` or `referer`, to leave out of every event. */\n\tomit?: Array<string>;\n\t/** `\"remote\"` loads the newest AI bot list from WaniWani every 12 hours. `\"bundled\"` keeps the list shipped in this version. Defaults to `\"remote\"`. */\n\trules?: \"remote\" | \"bundled\";\n};\n\nexport type IsAgentOptions = Pick<\n\tRelayOptions,\n\t\"region\" | \"apiUrl\" | \"waitUntil\" | \"rules\"\n>;\n\nexport type ServeAgentPageOptions = Pick<\n\tRelayOptions,\n\t\"region\" | \"apiUrl\" | \"key\"\n>;\n\nexport type AgentVisitRecord = {\n\toccurredAt: string;\n\tmethod: string;\n\thost: string;\n\tpath: string;\n\tuserAgent?: string;\n\treferer?: string;\n\theaders: Record<string, string>;\n\tmatchKey?: string;\n\tsourceEventId: string;\n\trelayVersion: string;\n\tplatform?: Platform;\n};\n\n/**\n * Tells whether a request, or the `cf.request` of a Lambda@Edge event, comes from an AI agent. Reads headers only and never waits on the network.\n * Every 12 hours it starts one background fetch of the newest AI bot list, unless `rules` is `\"bundled\"`.\n */\nexport function isAgent(\n\trequest: Request | CloudFrontRequest,\n\toptions: IsAgentOptions = {},\n): boolean {\n\tstartRulesRefresh(options);\n\ttry {\n\t\tconst { headers, ip } = toRelayRequest(request);\n\t\treturn decide({\n\t\t\theaders: readCappedHeaders(headers),\n\t\t\tip,\n\t\t\tuseBundled: options.rules === \"bundled\",\n\t\t});\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction startRulesRefresh(options: IsAgentOptions): void {\n\ttry {\n\t\tif (options.rules === \"bundled\") {\n\t\t\treturn;\n\t\t}\n\t\tconst apiUrl = resolveApiUrl(options);\n\t\tconst work = apiUrl ? refreshRules(apiUrl) : null;\n\t\tif (work) {\n\t\t\tschedule(work, options.waitUntil);\n\t\t}\n\t} catch {}\n}\n\n/**\n * Posts one event for this request to WaniWani after the response.\n * Sends nothing when no key is set. Never throws, never rejects and never touches the request.\n * The promise settles when the post is done, so Lambda@Edge, which has no `waitUntil`, awaits it.\n */\nexport function recordAgentVisit(\n\trequest: Request | CloudFrontRequest,\n\toptions: RelayOptions = {},\n): Promise<void> {\n\ttry {\n\t\tconst key = readKey(options.key);\n\t\tif (!key) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\tconst apiUrl = resolveApiUrl(options);\n\t\tif (!apiUrl) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\tconst omitted = omitSet(options.omit);\n\t\tconst {\n\t\t\turl: requestUrl,\n\t\t\tmethod,\n\t\t\theaders: requestHeaders,\n\t\t\tip,\n\t\t\tplatform,\n\t\t} = toRelayRequest(request);\n\t\tif (requestUrl === null) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\tconst capped = readCappedHeaders(requestHeaders);\n\n\t\tconst headers: Record<string, string> = {};\n\t\tfor (const [name, value] of Object.entries(capped)) {\n\t\t\tif (!omitted.has(name)) {\n\t\t\t\theaders[name] = value;\n\t\t\t}\n\t\t}\n\t\tconst url = new URL(requestUrl);\n\t\tconst record: AgentVisitRecord = {\n\t\t\toccurredAt: new Date().toISOString(),\n\t\t\tmethod,\n\t\t\thost: url.hostname,\n\t\t\tpath: url.pathname + url.search,\n\t\t\theaders,\n\t\t\tsourceEventId: crypto.randomUUID(),\n\t\t\trelayVersion: RELAY_VERSION,\n\t\t};\n\t\tif (capped[\"user-agent\"] !== undefined && !omitted.has(\"user-agent\")) {\n\t\t\trecord.userAgent = capped[\"user-agent\"];\n\t\t}\n\t\tif (capped.referer !== undefined && !omitted.has(\"referer\")) {\n\t\t\trecord.referer = capped.referer;\n\t\t}\n\t\tif (platform !== undefined) {\n\t\t\trecord.platform = platform;\n\t\t}\n\n\t\tconst policy: PostPolicy =\n\t\t\tplatform === \"cloudfront\"\n\t\t\t\t? { timeoutMs: settings.cloudFrontTimeoutMs, retry: false }\n\t\t\t\t: { timeoutMs: settings.requestTimeoutMs, retry: true };\n\t\tconst ingestUrl = `${apiUrl}/api/v2/agent-traffic/ingest/middleware/${key.ingestToken}`;\n\t\tconst withMatchKey = async () => {\n\t\t\tif (ip !== null && !omitted.has(\"matchkey\")) {\n\t\t\t\trecord.matchKey = await deriveMatchKey(key.orgKey, ip).catch(\n\t\t\t\t\t() => undefined,\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait postEvent(\n\t\t\t\tingestUrl,\n\t\t\t\tJSON.stringify({ requests: [record] }),\n\t\t\t\tpolicy,\n\t\t\t);\n\t\t};\n\t\treturn schedule(withMatchKey(), options.waitUntil);\n\t} catch {\n\t\treturn Promise.resolve();\n\t}\n}\n\ntype PageRequest = {\n\tmethod: string;\n\thostname: string;\n\tpathname: string;\n\tuserAgent: string;\n\trendering: boolean;\n};\n\ntype PageAnswer = {\n\tstatus: 200 | 403;\n\theaders: Record<string, string>;\n\tbody: Uint8Array<ArrayBuffer> | string | null;\n};\n\ntype SiteMatch = {\n\trules: SiteRules;\n\tsite: SiteRules[\"sites\"][number];\n};\n\nconst BLOCKED: Omit<PageAnswer, \"body\"> = {\n\tstatus: 403,\n\theaders: {\n\t\t\"content-type\": \"text/plain; charset=utf-8\",\n\t\t\"x-waniwani-blocked\": \"1\",\n\t\t\"cache-control\": \"private, no-store\",\n\t\tvary: \"user-agent\",\n\t},\n};\n\n/**\n * The stored agent page for this request, when its site lists the path and the user agent acts for a user. A 403 when the site blocks the user agent. Otherwise null, and the caller serves the origin.\n * A `CloudFrontRequest` comes from the origin-request handler. Only a URI that `handleViewerRequest` marked gets the page, read for the host in the mark. The function restores `request.uri` to the original path before anything else, and it never returns a 403 on this path.\n * Never throws and never rejects.\n */\nexport function serveAgentPage(\n\trequest: Request,\n\toptions?: ServeAgentPageOptions,\n): Promise<Response | null>;\nexport function serveAgentPage(\n\trequest: CloudFrontRequest,\n\toptions?: ServeAgentPageOptions,\n): Promise<CloudFrontResultResponse | null>;\nexport async function serveAgentPage(\n\trequest: Request | CloudFrontRequest,\n\toptions: ServeAgentPageOptions = {},\n): Promise<Response | CloudFrontResultResponse | null> {\n\ttry {\n\t\treturn isCloudFrontRequest(request)\n\t\t\t? await serveCloudFront(request, options)\n\t\t\t: await serveFetch(request, options);\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nasync function serveFetch(\n\trequest: Request,\n\toptions: ServeAgentPageOptions,\n): Promise<Response | null> {\n\tconst url = new URL(request.url);\n\tconst answer = await answerAgentPage(\n\t\t{\n\t\t\tmethod: request.method.toUpperCase(),\n\t\t\thostname: url.hostname,\n\t\t\tpathname: url.pathname,\n\t\t\tuserAgent: request.headers.get(\"user-agent\") ?? \"\",\n\t\t\trendering: request.headers.has(\"x-waniwani-render\"),\n\t\t},\n\t\toptions,\n\t);\n\tif (!answer) {\n\t\treturn null;\n\t}\n\treturn new Response(answer.body, {\n\t\tstatus: answer.status,\n\t\theaders: answer.headers,\n\t});\n}\n\nasync function serveCloudFront(\n\trequest: CloudFrontRequest,\n\toptions: ServeAgentPageOptions,\n): Promise<CloudFrontResultResponse | null> {\n\tconst hostname = takeMarkFromUri(request);\n\tif (hostname === null) {\n\t\treturn null;\n\t}\n\tconst method = request.method.toUpperCase();\n\tif (!isPageMethod(method)) {\n\t\treturn null;\n\t}\n\tconst match = await findSite(hostname, options);\n\tif (!match) {\n\t\treturn null;\n\t}\n\tconst answer = await pageAnswer(\n\t\tmatch.rules,\n\t\thostname,\n\t\tnormalizePath(request.uri),\n\t\tmethod,\n\t);\n\tif (!answer) {\n\t\treturn null;\n\t}\n\tconst result = toCloudFrontResponse(answer);\n\tif (\n\t\tresult.body !== undefined &&\n\t\tnew TextEncoder().encode(result.body).byteLength >\n\t\t\tsettings.cloudFrontMaxBodyBytes\n\t) {\n\t\treturn null;\n\t}\n\treturn result;\n}\n\nfunction toCloudFrontResponse(answer: PageAnswer): CloudFrontResultResponse {\n\tconst headers: CloudFrontResultResponse[\"headers\"] = {};\n\tfor (const [name, value] of Object.entries(answer.headers)) {\n\t\theaders[name] = [{ key: headerKey(name), value }];\n\t}\n\tconst result: CloudFrontResultResponse = {\n\t\tstatus: String(answer.status),\n\t\tstatusDescription: answer.status === 200 ? \"OK\" : \"Forbidden\",\n\t\theaders,\n\t};\n\tif (answer.body !== null) {\n\t\tresult.body =\n\t\t\ttypeof answer.body === \"string\"\n\t\t\t\t? answer.body\n\t\t\t\t: new TextDecoder().decode(answer.body);\n\t}\n\treturn result;\n}\n\nfunction headerKey(name: string): string {\n\treturn name\n\t\t.split(\"-\")\n\t\t.map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n\t\t.join(\"-\");\n}\n\nconst isPageMethod = (method: string): boolean =>\n\tmethod === \"GET\" || method === \"HEAD\";\n\nasync function findSite(\n\thostname: string,\n\toptions: ServeAgentPageOptions,\n): Promise<SiteMatch | null> {\n\tconst key = readKey(options.key);\n\tif (!key) {\n\t\treturn null;\n\t}\n\tconst apiUrl = resolveApiUrl(options);\n\tif (!apiUrl) {\n\t\treturn null;\n\t}\n\tconst rules = await loadSiteRules(apiUrl, key.ingestToken, (work) =>\n\t\tschedule(work, undefined),\n\t);\n\tif (!rules) {\n\t\treturn null;\n\t}\n\tconst site = rules.sites.find((entry) => entry.hostname === hostname);\n\treturn site ? { rules, site } : null;\n}\n\nfunction blockedAnswer(\n\tsite: SiteMatch[\"site\"],\n\tuserAgent: string,\n\tmethod: string,\n): PageAnswer | null {\n\treturn site.blockedUserAgentTokens.some((token) => userAgent.includes(token))\n\t\t? { ...BLOCKED, body: method === \"HEAD\" ? null : \"Forbidden\" }\n\t\t: null;\n}\n\nasync function answerAgentPage(\n\trequest: PageRequest,\n\toptions: ServeAgentPageOptions,\n): Promise<PageAnswer | null> {\n\tconst { method, hostname } = request;\n\tif (!isPageMethod(method)) {\n\t\treturn null;\n\t}\n\tconst match = await findSite(hostname, options);\n\tif (!match) {\n\t\treturn null;\n\t}\n\tconst { rules, site } = match;\n\tconst userAgent = request.userAgent.toLowerCase();\n\tconst blocked = blockedAnswer(site, userAgent, method);\n\tif (blocked) {\n\t\treturn blocked;\n\t}\n\tif (request.rendering || !site.agentPages.enabled) {\n\t\treturn null;\n\t}\n\tconst path = normalizePath(request.pathname);\n\tif (!matchesPath(path, site.agentPages.paths)) {\n\t\treturn null;\n\t}\n\tif (!rules.userAgentTokens.some((token) => userAgent.includes(token))) {\n\t\treturn null;\n\t}\n\treturn pageAnswer(rules, hostname, path, method);\n}\n\nasync function pageAnswer(\n\trules: SiteRules,\n\thostname: string,\n\tpath: string,\n\tmethod: string,\n): Promise<PageAnswer | null> {\n\tconst page = await fetchAgentPage(rules, hostname, path);\n\tif (!page) {\n\t\treturn null;\n\t}\n\treturn {\n\t\tstatus: 200,\n\t\theaders: {\n\t\t\t\"content-type\": page.contentType,\n\t\t\t\"x-waniwani-agent-page\": \"hit\",\n\t\t\t\"cache-control\": \"private, no-store\",\n\t\t\tvary: \"user-agent, accept\",\n\t\t},\n\t\tbody: method === \"HEAD\" ? null : page.body,\n\t};\n}\n\nconst AGENT_URI_PREFIX = \"/__waniwani-agent/\";\n\nfunction splitAgentUri(\n\turi: string,\n): { encodedHost: string; originalUri: string } | null {\n\tif (!uri.startsWith(AGENT_URI_PREFIX)) {\n\t\treturn null;\n\t}\n\tconst rest = uri.slice(AGENT_URI_PREFIX.length);\n\tconst slash = rest.indexOf(\"/\");\n\treturn slash === -1\n\t\t? { encodedHost: rest, originalUri: \"/\" }\n\t\t: { encodedHost: rest.slice(0, slash), originalUri: rest.slice(slash) };\n}\n\nfunction stripForgedMark(request: CloudFrontRequest): void {\n\tlet forged = splitAgentUri(request.uri);\n\twhile (forged) {\n\t\trequest.uri = forged.originalUri;\n\t\tforged = splitAgentUri(request.uri);\n\t}\n}\n\nfunction takeMarkFromUri(request: CloudFrontRequest): string | null {\n\tconst parts = splitAgentUri(request.uri);\n\tif (!parts) {\n\t\treturn null;\n\t}\n\trequest.uri = parts.originalUri;\n\ttry {\n\t\treturn hostnameOf(decodeURIComponent(parts.encodedHost));\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * For the viewer-request handler on CloudFront. Strips a `/__waniwani-agent/` prefix the client sent, then, for a GET or HEAD from an AI agent, loads the rules of the site of the `host` header.\n * Returns the 403 response when the site blocks the user agent. CloudFront never caches a response made at viewer-request, so a block takes effect at once.\n * When the site serves an agent page for the path and the user agent acts for a user, rewrites `request.uri` to `/__waniwani-agent/<host><original uri>` so the origin-request handler serves the page under its own cache key. The request stays unmarked when the new URI would pass the CloudFront URI limit.\n * Call `recordAgentVisit` before it, so the event holds the original path. Never throws and never rejects.\n */\nexport async function handleViewerRequest(\n\trequest: CloudFrontRequest,\n\toptions: ServeAgentPageOptions = {},\n): Promise<CloudFrontResultResponse | null> {\n\ttry {\n\t\tstripForgedMark(request);\n\t\tconst method = request.method.toUpperCase();\n\t\tif (!isPageMethod(method) || !isAgent(request, options)) {\n\t\t\treturn null;\n\t\t}\n\t\tconst hostname = hostnameOf(cloudFrontHeader(request, \"host\") ?? undefined);\n\t\tif (hostname === null) {\n\t\t\treturn null;\n\t\t}\n\t\tconst match = await findSite(hostname, options);\n\t\tif (!match) {\n\t\t\treturn null;\n\t\t}\n\t\tconst { rules, site } = match;\n\t\tconst userAgent = (\n\t\t\tcloudFrontHeader(request, \"user-agent\") ?? \"\"\n\t\t).toLowerCase();\n\t\tconst blocked = blockedAnswer(site, userAgent, method);\n\t\tif (blocked) {\n\t\t\treturn toCloudFrontResponse(blocked);\n\t\t}\n\t\tif (\n\t\t\tcloudFrontHeader(request, \"x-waniwani-render\") !== null ||\n\t\t\t!site.agentPages.enabled ||\n\t\t\t!matchesPath(normalizePath(request.uri), site.agentPages.paths) ||\n\t\t\t!rules.userAgentTokens.some((token) => userAgent.includes(token))\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\t\tconst uri = `${AGENT_URI_PREFIX}${encodeURIComponent(hostname)}${request.uri}`;\n\t\tif (\n\t\t\tnew TextEncoder().encode(uri).byteLength <= settings.cloudFrontMaxUriBytes\n\t\t) {\n\t\t\trequest.uri = uri;\n\t\t}\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nlet warnedRegion = false;\n\nfunction resolveApiUrl(options: IsAgentOptions): string | null {\n\tif (options.apiUrl !== undefined) {\n\t\treturn options.apiUrl.replace(/\\/+$/, \"\");\n\t}\n\tconst region = options.region ?? \"us\";\n\tif (Object.hasOwn(settings.apiUrls, region)) {\n\t\treturn settings.apiUrls[region];\n\t}\n\tif (!warnedRegion) {\n\t\twarnedRegion = true;\n\t\tconsole.warn(\n\t\t\t`[@waniwani/relay] Unknown region ${JSON.stringify(region)}, so no agent visits are recorded. Use \"us\" or \"eu\".`,\n\t\t);\n\t}\n\treturn null;\n}\n\nfunction omitSet(omit: Array<string> | undefined): Set<string> {\n\tconst names = new Set((omit ?? []).map((name) => name.toLowerCase()));\n\tif (names.has(\"useragent\")) {\n\t\tnames.add(\"user-agent\");\n\t}\n\treturn names;\n}\n\nfunction schedule(\n\twork: Promise<unknown>,\n\twaitUntil: RelayOptions[\"waitUntil\"],\n): Promise<void> {\n\tconst settled = work.then(\n\t\t() => undefined,\n\t\t() => undefined,\n\t);\n\ttry {\n\t\t(waitUntil ?? vercelWaitUntil())?.(settled);\n\t} catch {}\n\treturn settled;\n}\n"],"mappings":"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IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;;;AC7KA;AAAA,EACC,SAAW;AAAA,IACV;AAAA,MACC,KAAO;AAAA,MACP,KAAO;AAAA,MACP,MAAQ;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,MACA,MAAQ,CAAC;AAAA,IACV;AAAA,IACA;AAAA,MACC,KAAO;AAAA,MACP,KAAO;AAAA,MACP,MAAQ;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,MACA,MAAQ,CAAC;AAAA,IACV;AAAA,IACA;AAAA,MACC,KAAO;AAAA,MACP,KAAO;AAAA,MACP,MAAQ;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AA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CA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QA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","names":["isObject","isStringArray"]}
|
|
1
|
+
{"version":3,"sources":["../src/settings.ts","../src/version.ts","../src/agent-page.ts","../src/headers.ts","../src/http.ts","../src/ingest.ts","../src/key.ts","../src/logic/decide.ts","../src/logic/agents.generated.json","../src/logic/ip-ranges.generated.json","../src/match-key.ts","../src/refresh.ts","../src/wait-until.ts","../src/platform.ts","../src/request.ts","../src/index.ts"],"sourcesContent":["export const settings = {\n\tapiUrls: {\n\t\tus: \"https://app.waniwani.ai\",\n\t\teu: \"https://eu.app.waniwani.ai\",\n\t},\n\trequestTimeoutMs: 2000,\n\tcloudFrontTimeoutMs: 1000,\n\tcloudFrontMaxUriBytes: 8192,\n\tcloudFrontMaxBodyBytes: 1_000_000 - 16_384,\n\tretryDelayMs: 500,\n\trulesRefreshMs: 12 * 60 * 60 * 1000,\n\trulesRetryMs: 5 * 60 * 1000,\n\tsiteRulesTimeoutMs: 300,\n\tsiteRulesRefreshMs: 60_000,\n\tsiteRulesRetryMs: 5_000,\n};\n","declare const __RELAY_PACKAGE_VERSION__: string | undefined;\n\nexport const RELAY_VERSION: string =\n\ttypeof __RELAY_PACKAGE_VERSION__ === \"string\"\n\t\t? __RELAY_PACKAGE_VERSION__\n\t\t: \"0.0.0-dev\";\n","import { settings } from \"./settings\";\nimport { RELAY_VERSION } from \"./version\";\n\nexport type SiteRules = {\n\tversion: 1;\n\tpagesUrl: string;\n\tuserAgentTokens: string[];\n\tdeadlines: { headersMs: number; bodyMs: number };\n\tmaxBodyBytes: number;\n\tsites: Array<{\n\t\thostname: string;\n\t\tblockedUserAgentTokens: string[];\n\t\tagentPages: { enabled: boolean; paths: string[] };\n\t}>;\n};\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n\ttypeof value === \"object\" && value !== null;\n\nconst isStringArray = (value: unknown): value is string[] =>\n\tArray.isArray(value) && value.every((item) => typeof item === \"string\");\n\nconst isPositive = (value: unknown): value is number =>\n\ttypeof value === \"number\" && Number.isFinite(value) && value > 0;\n\n/** Lowercase like the user agent they are matched against, with blank tokens dropped since they would match every request. */\nconst cleanTokens = (tokens: string[]): string[] =>\n\ttokens\n\t\t.map((token) => token.trim().toLowerCase())\n\t\t.filter((token) => token !== \"\");\n\n/** The site rules in a serve rules file from the app (the `data` of its answer), or null when it does not have the expected form. */\nexport function parseSiteRules(body: unknown): SiteRules | null {\n\tif (!isObject(body) || body.version !== 1) {\n\t\treturn null;\n\t}\n\tconst { pagesUrl, userAgentTokens, deadlines, maxBodyBytes, sites } = body;\n\tif (\n\t\ttypeof pagesUrl !== \"string\" ||\n\t\t!isStringArray(userAgentTokens) ||\n\t\t!isObject(deadlines) ||\n\t\t!isPositive(deadlines.headersMs) ||\n\t\t!isPositive(deadlines.bodyMs) ||\n\t\t!isPositive(maxBodyBytes) ||\n\t\t!Array.isArray(sites)\n\t) {\n\t\treturn null;\n\t}\n\tconst parsedSites: SiteRules[\"sites\"] = [];\n\tfor (const site of sites) {\n\t\tif (!isObject(site)) {\n\t\t\treturn null;\n\t\t}\n\t\tconst blocked = site.blockedUserAgentTokens ?? [];\n\t\tif (\n\t\t\ttypeof site.hostname !== \"string\" ||\n\t\t\t!isStringArray(blocked) ||\n\t\t\t!isObject(site.agentPages) ||\n\t\t\ttypeof site.agentPages.enabled !== \"boolean\" ||\n\t\t\t!isStringArray(site.agentPages.paths)\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\t\tparsedSites.push({\n\t\t\thostname: site.hostname.toLowerCase(),\n\t\t\tblockedUserAgentTokens: cleanTokens(blocked),\n\t\t\tagentPages: {\n\t\t\t\tenabled: site.agentPages.enabled,\n\t\t\t\tpaths: site.agentPages.paths,\n\t\t\t},\n\t\t});\n\t}\n\treturn {\n\t\tversion: 1,\n\t\tpagesUrl: pagesUrl.replace(/\\/+$/, \"\"),\n\t\tuserAgentTokens: cleanTokens(userAgentTokens),\n\t\tdeadlines: { headersMs: deadlines.headersMs, bodyMs: deadlines.bodyMs },\n\t\tmaxBodyBytes,\n\t\tsites: parsedSites,\n\t};\n}\n\ntype CacheEntry = {\n\trules: SiteRules | null;\n\tfetchedAt: number;\n\tlastAttemptAt: number;\n\tlastAttemptOk: boolean;\n\tinFlight: Promise<SiteRules | null> | null;\n};\n\nconst cache = new Map<string, CacheEntry>();\n\nasync function fetchSiteRules(\n\turl: string,\n\ttimeoutMs: number,\n): Promise<SiteRules | null> {\n\tconst response = await fetch(url, {\n\t\tsignal: AbortSignal.timeout(timeoutMs),\n\t});\n\tif (!response.ok) {\n\t\tawait response.body?.cancel().catch(() => {});\n\t\treturn null;\n\t}\n\tconst body: unknown = await response.json();\n\treturn parseSiteRules(isObject(body) ? body.data : null);\n}\n\nfunction startFetch(\n\tentry: CacheEntry,\n\turl: string,\n\ttimeoutMs: number,\n): Promise<SiteRules | null> {\n\tconst now = Date.now();\n\tentry.lastAttemptAt = now;\n\tentry.lastAttemptOk = false;\n\tconst work = fetchSiteRules(url, timeoutMs)\n\t\t.catch(() => null)\n\t\t.then((rules) => {\n\t\t\tif (rules) {\n\t\t\t\tentry.rules = rules;\n\t\t\t\tentry.fetchedAt = Date.now();\n\t\t\t\tentry.lastAttemptOk = true;\n\t\t\t}\n\t\t\tentry.inFlight = null;\n\t\t\treturn rules;\n\t\t});\n\tentry.inFlight = work;\n\treturn work;\n}\n\n/**\n * The rules for this org. Awaits the first fetch, then serves the cache and refreshes it in the background once it is stale.\n * At most one fetch per org is in flight.\n */\nexport async function loadSiteRules(\n\tapiUrl: string,\n\tingestToken: string,\n\tbackground: (work: Promise<unknown>) => void,\n): Promise<SiteRules | null> {\n\tconst url = `${apiUrl}/api/v2/agent-traffic/serve/${ingestToken}/rules`;\n\tlet entry = cache.get(url);\n\tif (!entry) {\n\t\tentry = {\n\t\t\trules: null,\n\t\t\tfetchedAt: 0,\n\t\t\tlastAttemptAt: Number.NEGATIVE_INFINITY,\n\t\t\tlastAttemptOk: false,\n\t\t\tinFlight: null,\n\t\t};\n\t\tcache.set(url, entry);\n\t}\n\tconst now = Date.now();\n\tif (entry.rules) {\n\t\tconst stale = now - entry.fetchedAt >= settings.siteRulesRefreshMs;\n\t\tconst retryDue =\n\t\t\tentry.lastAttemptOk ||\n\t\t\tnow - entry.lastAttemptAt >= settings.siteRulesRetryMs;\n\t\tif (stale && retryDue && !entry.inFlight) {\n\t\t\tbackground(startFetch(entry, url, settings.requestTimeoutMs));\n\t\t}\n\t\treturn entry.rules;\n\t}\n\tif (entry.inFlight) {\n\t\treturn entry.inFlight;\n\t}\n\tif (now - entry.lastAttemptAt < settings.siteRulesRetryMs) {\n\t\treturn null;\n\t}\n\treturn startFetch(entry, url, settings.siteRulesTimeoutMs);\n}\n\n/** The pathname without its trailing slashes, except for the root. */\nexport function normalizePath(pathname: string): string {\n\tconst trimmed = pathname.replace(/\\/+$/, \"\");\n\treturn trimmed === \"\" ? \"/\" : trimmed;\n}\n\n/** Whether a path matches an exact entry like `/pricing`, or a folder entry like `/products/*` that also covers `/products`. */\nexport function matchesPath(path: string, patterns: string[]): boolean {\n\treturn patterns.some((pattern) => {\n\t\tif (pattern === \"*\" || pattern === \"/*\") {\n\t\t\treturn true;\n\t\t}\n\t\tif (pattern.endsWith(\"/*\")) {\n\t\t\tconst folder = normalizePath(pattern.slice(0, -2));\n\t\t\treturn path === folder || path.startsWith(`${folder}/`);\n\t\t}\n\t\treturn path === normalizePath(pattern);\n\t});\n}\n\nconst TIMEOUT = Symbol(\"timeout\");\n\nfunction within<T>(\n\tpromise: Promise<T>,\n\tms: number,\n): Promise<T | typeof TIMEOUT> {\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tconst deadline = new Promise<typeof TIMEOUT>((resolve) => {\n\t\ttimer = setTimeout(() => resolve(TIMEOUT), ms);\n\t});\n\treturn Promise.race([promise, deadline]).finally(() => clearTimeout(timer));\n}\n\nasync function readCapped(\n\tbody: ReadableStream<Uint8Array>,\n\tdeadline: Promise<typeof TIMEOUT>,\n\tmaxBytes: number,\n): Promise<Uint8Array<ArrayBuffer> | null> {\n\tconst reader = body.getReader();\n\tconst chunks: Uint8Array[] = [];\n\tlet size = 0;\n\ttry {\n\t\twhile (true) {\n\t\t\tconst result = await Promise.race([reader.read(), deadline]);\n\t\t\tif (result === TIMEOUT) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tif (result.done) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tsize += result.value.byteLength;\n\t\t\tif (size > maxBytes) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\tchunks.push(result.value);\n\t\t}\n\t} finally {\n\t\treader.cancel().catch(() => {});\n\t}\n\tconst out = new Uint8Array(size);\n\tlet offset = 0;\n\tfor (const chunk of chunks) {\n\t\tout.set(chunk, offset);\n\t\toffset += chunk.byteLength;\n\t}\n\treturn out;\n}\n\nexport type PageFormat = \"markdown\" | \"html\";\n\n/** Markdown only when the agent lists `text/markdown` with a nonzero q, so an agent that asks for HTML or for anything gets HTML. */\nexport function pageFormat(accept: string): PageFormat {\n\treturn accept.split(\",\").some((range) => {\n\t\tconst [type = \"\", ...params] = range\n\t\t\t.split(\";\")\n\t\t\t.map((part) => part.trim().toLowerCase());\n\t\tif (type !== \"text/markdown\") {\n\t\t\treturn false;\n\t\t}\n\t\tconst q = params.find((param) => param.startsWith(\"q=\"));\n\t\treturn q === undefined || Number(q.slice(2)) > 0;\n\t})\n\t\t? \"markdown\"\n\t\t: \"html\";\n}\n\n/** Fetches the stored page in the agent's format within both deadlines and the size cap. Returns its body and content type, or null. The home page URL has no trailing slash, which the app would answer with a redirect. */\nexport async function fetchAgentPage(\n\trules: SiteRules,\n\thostname: string,\n\tpath: string,\n\tformat: PageFormat,\n): Promise<{ body: Uint8Array<ArrayBuffer>; contentType: string } | null> {\n\tconst controller = new AbortController();\n\ttry {\n\t\tconst response = await within(\n\t\t\tfetch(`${rules.pagesUrl}/${hostname}${path === \"/\" ? \"\" : path}`, {\n\t\t\t\theaders: {\n\t\t\t\t\taccept: format === \"markdown\" ? \"text/markdown\" : \"text/html\",\n\t\t\t\t\t\"x-waniwani-relay\": RELAY_VERSION,\n\t\t\t\t},\n\t\t\t\tsignal: controller.signal,\n\t\t\t}),\n\t\t\trules.deadlines.headersMs,\n\t\t);\n\t\tif (response === TIMEOUT) {\n\t\t\treturn null;\n\t\t}\n\t\tconst contentType = response.headers.get(\"content-type\") ?? \"\";\n\t\tif (\n\t\t\tresponse.status !== 200 ||\n\t\t\t!contentType.toLowerCase().startsWith(\"text/\") ||\n\t\t\t!response.body\n\t\t) {\n\t\t\tawait response.body?.cancel().catch(() => {});\n\t\t\treturn null;\n\t\t}\n\t\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\t\tconst deadline = new Promise<typeof TIMEOUT>((resolve) => {\n\t\t\ttimer = setTimeout(() => resolve(TIMEOUT), rules.deadlines.bodyMs);\n\t\t});\n\t\ttry {\n\t\t\tconst body = await readCapped(\n\t\t\t\tresponse.body,\n\t\t\t\tdeadline,\n\t\t\t\trules.maxBodyBytes,\n\t\t\t);\n\t\t\treturn body ? { body, contentType } : null;\n\t\t} finally {\n\t\t\tclearTimeout(timer);\n\t\t}\n\t} catch {\n\t\treturn null;\n\t} finally {\n\t\tcontroller.abort();\n\t}\n}\n","export const HEADER_CAP: readonly string[] = [\n\t\"user-agent\",\n\t\"referer\",\n\t\"accept\",\n\t\"accept-language\",\n\t\"from\",\n\t\"signature-agent\",\n\t\"signature-input\",\n\t\"signature\",\n\t\"dpop\",\n\t\"sec-ch-ua\",\n\t\"sec-ch-ua-platform\",\n\t\"sec-ch-ua-mobile\",\n\t\"authorization\",\n];\n\nconst KNOWN_SCHEMES = new Map(\n\t[\n\t\t\"Basic\",\n\t\t\"Bearer\",\n\t\t\"Digest\",\n\t\t\"DPoP\",\n\t\t\"HOBA\",\n\t\t\"Mutual\",\n\t\t\"Negotiate\",\n\t\t\"NTLM\",\n\t\t\"OAuth\",\n\t\t\"PrivateToken\",\n\t\t\"SCRAM-SHA-1\",\n\t\t\"SCRAM-SHA-256\",\n\t\t\"vapid\",\n\t\t\"AWS4-HMAC-SHA256\",\n\t].map((scheme) => [scheme.toLowerCase(), scheme]),\n);\n\n/** The canonical name of a known Authorization scheme, kept only when a credential follows it. */\nexport function authorizationScheme(value: string): string | null {\n\tconst parts = value.trim().split(/\\s+/);\n\tif (parts.length < 2) {\n\t\treturn null;\n\t}\n\treturn KNOWN_SCHEMES.get((parts[0] ?? \"\").toLowerCase()) ?? null;\n}\n\nexport function readCappedHeaders(headers: Headers): Record<string, string> {\n\tconst out: Record<string, string> = {};\n\tfor (const name of HEADER_CAP) {\n\t\tconst value = headers.get(name);\n\t\tif (value === null) {\n\t\t\tcontinue;\n\t\t}\n\t\tif (name === \"authorization\") {\n\t\t\tconst scheme = authorizationScheme(value);\n\t\t\tif (scheme !== null) {\n\t\t\t\tout[name] = scheme;\n\t\t\t}\n\t\t} else {\n\t\t\tout[name] = value;\n\t\t}\n\t}\n\treturn out;\n}\n\n/** Cloudflare appends the real IP to a client-sent `X-Forwarded-For`, so `CF-Connecting-IP` comes first. */\nexport function clientIp(headers: Headers): string | null {\n\tconst forwarded = headers.get(\"x-forwarded-for\");\n\tconst candidate =\n\t\theaders.get(\"cf-connecting-ip\") ??\n\t\t(forwarded !== null ? forwarded.split(\",\")[0] : headers.get(\"x-real-ip\"));\n\tconst ip = candidate?.trim().toLowerCase();\n\treturn ip ? ip : null;\n}\n","import { settings } from \"./settings\";\n\nexport type Outcome = { status: number } | { failure: \"timeout\" | \"network\" };\n\n/** One fetch bounded by the timeout. Never rejects. */\nexport async function fetchWithTimeout(\n\turl: string,\n\tinit: RequestInit,\n\ttimeoutMs: number = settings.requestTimeoutMs,\n): Promise<Outcome> {\n\tconst controller = new AbortController();\n\tconst timer = setTimeout(() => controller.abort(), timeoutMs);\n\ttry {\n\t\tconst response = await fetch(url, { ...init, signal: controller.signal });\n\t\tawait response.body?.cancel().catch(() => {});\n\t\treturn { status: response.status };\n\t} catch {\n\t\treturn { failure: controller.signal.aborted ? \"timeout\" : \"network\" };\n\t} finally {\n\t\tclearTimeout(timer);\n\t}\n}\n\nexport const delay = (ms: number) =>\n\tnew Promise<void>((resolve) => setTimeout(resolve, ms));\n","import { delay, fetchWithTimeout, type Outcome } from \"./http\";\nimport { settings } from \"./settings\";\n\nconst isSuccess = (outcome: Outcome) =>\n\t\"status\" in outcome && outcome.status >= 200 && outcome.status < 300;\n\nconst isRetryable = (outcome: Outcome) =>\n\t\"failure\" in outcome || outcome.status === 429 || outcome.status >= 500;\n\nconst describe = (outcome: Outcome) =>\n\t\"failure\" in outcome ? outcome.failure : `status ${outcome.status}`;\n\nexport type PostPolicy = { timeoutMs: number; retry: boolean };\n\nconst post = (url: string, body: string, timeoutMs: number) =>\n\tfetchWithTimeout(\n\t\turl,\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\tbody,\n\t\t},\n\t\ttimeoutMs,\n\t);\n\n/** Posts once, retries once on a timeout, network error, 429 or 5xx when the policy allows it, and never rejects. */\nexport async function postEvent(\n\turl: string,\n\tbody: string,\n\tpolicy: PostPolicy = { timeoutMs: settings.requestTimeoutMs, retry: true },\n): Promise<boolean> {\n\tlet outcome = await post(url, body, policy.timeoutMs);\n\tif (policy.retry && !isSuccess(outcome) && isRetryable(outcome)) {\n\t\tawait delay(settings.retryDelayMs);\n\t\toutcome = await post(url, body, policy.timeoutMs);\n\t}\n\tif (isSuccess(outcome)) {\n\t\treturn true;\n\t}\n\tconsole.warn(\n\t\t`[@waniwani/relay] Could not record an agent visit (${describe(outcome)}). The event was dropped.`,\n\t);\n\treturn false;\n}\n","export type RelayKey = { ingestToken: string; orgKey: string };\n\nconst KEY = /^wwt_([A-Za-z0-9_-]+)\\.([A-Za-z0-9_-]+={0,2})$/;\n\nlet warnedMalformed = false;\n\nexport function readKey(explicit: string | undefined): RelayKey | null {\n\tconst raw = explicit ?? readEnv(\"WANIWANI_AGENT_TRAFFIC_KEY\");\n\tif (raw === undefined || raw.trim() === \"\") {\n\t\treturn null;\n\t}\n\tconst match = KEY.exec(raw.trim());\n\tif (!match?.[1] || !match[2]) {\n\t\tif (!warnedMalformed) {\n\t\t\twarnedMalformed = true;\n\t\t\tconsole.warn(\n\t\t\t\t\"[@waniwani/relay] WANIWANI_AGENT_TRAFFIC_KEY is malformed, so no agent visits are recorded. Copy the key again from the WaniWani setup screen.\",\n\t\t\t);\n\t\t}\n\t\treturn null;\n\t}\n\treturn { ingestToken: match[1], orgKey: match[2] };\n}\n\nfunction readEnv(name: string): string | undefined {\n\ttry {\n\t\treturn typeof process === \"undefined\" ? undefined : process.env[name];\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n","import { BlockList } from \"node:net\";\nimport agents from \"./agents.generated.json\";\nimport ranges from \"./ip-ranges.generated.json\";\n\nexport type DecideInput = {\n\theaders: Record<string, string>;\n\tip: string | null;\n\tuseBundled?: boolean;\n};\n\nexport type MatchRules = {\n\ttokens: string[];\n\tipRanges: Array<{ ipv4: string[]; ipv6: string[] }>;\n};\n\nconst MAX_SCANNED_USER_AGENT = 1024;\nconst PREFIX = 3;\n\ntype CompiledRules = {\n\ttokensByPrefix: Map<string, string[]>;\n\tagentRanges: BlockList;\n};\n\nfunction compile(rules: MatchRules): CompiledRules {\n\tconst tokensByPrefix = new Map<string, string[]>();\n\tfor (const token of rules.tokens) {\n\t\tconst key = token.slice(0, PREFIX);\n\t\tconst list = tokensByPrefix.get(key);\n\t\tif (list) {\n\t\t\tlist.push(token);\n\t\t} else {\n\t\t\ttokensByPrefix.set(key, [token]);\n\t\t}\n\t}\n\n\tconst agentRanges = new BlockList();\n\tconst addRanges = (cidrs: string[], type: \"ipv4\" | \"ipv6\") => {\n\t\tfor (const cidr of cidrs) {\n\t\t\tconst [address = \"\", prefix, extra] = cidr.split(\"/\");\n\t\t\tif (!prefix || extra !== undefined) {\n\t\t\t\tthrow new Error(`malformed range ${cidr}`);\n\t\t\t}\n\t\t\tagentRanges.addSubnet(address, Number(prefix), type);\n\t\t}\n\t};\n\tfor (const source of rules.ipRanges) {\n\t\taddRanges(source.ipv4, \"ipv4\");\n\t\taddRanges(source.ipv6, \"ipv6\");\n\t}\n\n\treturn { tokensByPrefix, agentRanges };\n}\n\nconst bundled = compile({ tokens: agents.tokens, ipRanges: ranges.sources });\nlet remote: CompiledRules | null = null;\n\n/** Sets the remote match rules. Throws, and keeps the current ones, when a range is malformed. */\nexport function applyRules(rules: MatchRules): void {\n\tremote = compile(rules);\n}\n\nconst pick = (useBundled: boolean): CompiledRules =>\n\tuseBundled ? bundled : (remote ?? bundled);\n\nexport const isAgentIp = (ip: string, useBundled = false): boolean =>\n\tpick(useBundled).agentRanges.check(ip, ip.includes(\":\") ? \"ipv6\" : \"ipv4\");\n\nexport function hasAgentToken(userAgent: string, useBundled = false): boolean {\n\tconst { tokensByPrefix } = pick(useBundled);\n\tconst text = userAgent.slice(0, MAX_SCANNED_USER_AGENT).toLowerCase();\n\tfor (let i = 0; i + PREFIX <= text.length; i++) {\n\t\tconst candidates = tokensByPrefix.get(text.slice(i, i + PREFIX));\n\t\tif (candidates) {\n\t\t\tfor (const token of candidates) {\n\t\t\t\tif (text.startsWith(token, i)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn false;\n}\n\nexport function decide({\n\theaders,\n\tip,\n\tuseBundled = false,\n}: DecideInput): boolean {\n\tconst userAgent = headers[\"user-agent\"];\n\treturn (\n\t\t(userAgent !== undefined && hasAgentToken(userAgent, useBundled)) ||\n\t\theaders[\"signature-agent\"] !== undefined ||\n\t\theaders.dpop !== undefined ||\n\t\theaders.authorization?.toLowerCase() === \"dpop\" ||\n\t\t(ip !== null && isAgentIp(ip, useBundled))\n\t);\n}\n","{\n\t\"source\": \"Generated by scripts/build-rules.ts from the WaniWani rules file ee1b010ce61b7660, do not edit. Each token is matched case-insensitively inside the User-Agent header.\",\n\t\"tokens\": [\n\t\t\"addsearchbot\",\n\t\t\"agentdatabot\",\n\t\t\"agenttimes\",\n\t\t\"ai2bot\",\n\t\t\"ai2bot-deepresearcheval\",\n\t\t\"ai2bot-dolma\",\n\t\t\"aihitbot\",\n\t\t\"aiwebindex\",\n\t\t\"allenai.org\",\n\t\t\"amazon-kendra\",\n\t\t\"amazon-qbusiness\",\n\t\t\"amazonbot\",\n\t\t\"amazonbuyforme\",\n\t\t\"amzn-searchbot\",\n\t\t\"amzn-user\",\n\t\t\"andibot\",\n\t\t\"anthropic-ai\",\n\t\t\"apifybot\",\n\t\t\"apifywebsitecontentcrawler\",\n\t\t\"applebot\",\n\t\t\"aranet-searchbot\",\n\t\t\"atlassian-bot\",\n\t\t\"awario\",\n\t\t\"azureai-searchbot\",\n\t\t\"bedrockbot\",\n\t\t\"bigsur.ai\",\n\t\t\"bixelbot\",\n\t\t\"bravebot\",\n\t\t\"brightbot\",\n\t\t\"buddybot\",\n\t\t\"bytespider\",\n\t\t\"ccbot\",\n\t\t\"channel3bot\",\n\t\t\"chatglm-spider\",\n\t\t\"chatgpt agent\",\n\t\t\"chatgpt-user\",\n\t\t\"claude-code\",\n\t\t\"claude-searchbot\",\n\t\t\"claude-user\",\n\t\t\"claude-web\",\n\t\t\"claudebot\",\n\t\t\"cloudflare-autorag\",\n\t\t\"cloudflarebrowserrenderingcrawler\",\n\t\t\"cloudvertexbot\",\n\t\t\"cohere-ai\",\n\t\t\"cohere-training-data-crawler\",\n\t\t\"cotoyogi\",\n\t\t\"coveobot\",\n\t\t\"cragcrawler\",\n\t\t\"crawl4ai\",\n\t\t\"crawlspace\",\n\t\t\"cursor/1.0.0\",\n\t\t\"datenbank crawler\",\n\t\t\"deepseekbot\",\n\t\t\"diffbot\",\n\t\t\"diffbot-user\",\n\t\t\"doubaobot\",\n\t\t\"duckassistbot\",\n\t\t\"echobot bot\",\n\t\t\"echoboxbot\",\n\t\t\"erniebot\",\n\t\t\"exabot\",\n\t\t\"exasearchbot\",\n\t\t\"exauser\",\n\t\t\"facebookbot\",\n\t\t\"factset_spyderbot\",\n\t\t\"firecrawlagent\",\n\t\t\"friendlycrawler\",\n\t\t\"geisthaus-pagefetcher\",\n\t\t\"gemini-deep-research\",\n\t\t\"google-ads-creatives-assistant\",\n\t\t\"google-agent\",\n\t\t\"google-cloudvertexbot\",\n\t\t\"google-firebase\",\n\t\t\"google-gemini-cli\",\n\t\t\"google-gemininotebook\",\n\t\t\"google-lens\",\n\t\t\"google-notebooklm\",\n\t\t\"googleagent-mariner\",\n\t\t\"googleagent-urlcontext\",\n\t\t\"googleother\",\n\t\t\"googleother-image\",\n\t\t\"googleother-video\",\n\t\t\"gptbot\",\n\t\t\"grove/1.0 rss feed service\",\n\t\t\"henkbot\",\n\t\t\"iaskbot\",\n\t\t\"iaskspider\",\n\t\t\"icc-crawler\",\n\t\t\"imagesiftbot\",\n\t\t\"imagespider\",\n\t\t\"img2dataset\",\n\t\t\"isscyberriskcrawler\",\n\t\t\"jackjillcareersbot\",\n\t\t\"kagi-fetcher\",\n\t\t\"kangaroo bot\",\n\t\t\"keenablebot\",\n\t\t\"kernel searchbot\",\n\t\t\"kimi-agent\",\n\t\t\"kimi-searchbot\",\n\t\t\"kimi-user\",\n\t\t\"kimibot\",\n\t\t\"klaviyoaibot\",\n\t\t\"kunatocrawler\",\n\t\t\"laion-huggingface-processor\",\n\t\t\"laiondownloader\",\n\t\t\"lightpanda\",\n\t\t\"linerbot\",\n\t\t\"linkupbot\",\n\t\t\"manus-user\",\n\t\t\"mediavinebot/\",\n\t\t\"meta-externalagent\",\n\t\t\"meta-externalfetcher\",\n\t\t\"meta-webindexer\",\n\t\t\"mistralai-index\",\n\t\t\"mistralai-training\",\n\t\t\"mistralai-user\",\n\t\t\"momentic\",\n\t\t\"mozilla-tabstack\",\n\t\t\"mycentralaiscraperbot\",\n\t\t\"nagetbot\",\n\t\t\"netestate imprint crawler\",\n\t\t\"notebooklm\",\n\t\t\"novaact\",\n\t\t\"oai-adsbot\",\n\t\t\"oai-searchbot\",\n\t\t\"omgili\",\n\t\t\"omgilibot\",\n\t\t\"pangubot\",\n\t\t\"panscient\",\n\t\t\"perplexity-user\",\n\t\t\"perplexitybot\",\n\t\t\"petalbot\",\n\t\t\"phindbot\",\n\t\t\"poggio-citations\",\n\t\t\"poseidon research crawler\",\n\t\t\"publogic crawler\",\n\t\t\"qodercli\",\n\t\t\"qualifiedbot\",\n\t\t\"querit-searchbot\",\n\t\t\"queritbot\",\n\t\t\"quillbot\",\n\t\t\"qwenbot\",\n\t\t\"reflectionbot\",\n\t\t\"ryebot\",\n\t\t\"sbintuitionsbot\",\n\t\t\"seltzsitemapfetcher\",\n\t\t\"semanticscholarbot\",\n\t\t\"semrushbot-ocob\",\n\t\t\"semrushbot-swa\",\n\t\t\"shap-user\",\n\t\t\"shapbot\",\n\t\t\"sidetrade indexer bot\",\n\t\t\"tavilybot\",\n\t\t\"terra cotta\",\n\t\t\"terracotta\",\n\t\t\"thinkbot\",\n\t\t\"tiktokspider\",\n\t\t\"timpibot\",\n\t\t\"tongyibot\",\n\t\t\"twilioknowledge\",\n\t\t\"twinagent\",\n\t\t\"v0bot\",\n\t\t\"velenpublicwebcrawler\",\n\t\t\"wardbot\",\n\t\t\"wrtnbot\",\n\t\t\"yandexadditional\",\n\t\t\"yandexadditionalbot\",\n\t\t\"yiyanbot\",\n\t\t\"youbot\",\n\t\t\"zanistabot\"\n\t]\n}\n","{\n\t\"sources\": [\n\t\t{\n\t\t\t\"bot\": \"GPTBot\",\n\t\t\t\"url\": \"https://openai.com/gptbot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"4.227.36.0/25\",\n\t\t\t\t\"20.125.66.80/28\",\n\t\t\t\t\"20.171.206.0/24\",\n\t\t\t\t\"20.171.207.0/24\",\n\t\t\t\t\"52.230.152.0/24\",\n\t\t\t\t\"74.7.175.128/25\",\n\t\t\t\t\"74.7.227.0/25\",\n\t\t\t\t\"74.7.227.128/25\",\n\t\t\t\t\"74.7.228.0/25\",\n\t\t\t\t\"74.7.230.0/25\",\n\t\t\t\t\"74.7.241.0/25\",\n\t\t\t\t\"74.7.241.128/25\",\n\t\t\t\t\"74.7.242.0/25\",\n\t\t\t\t\"74.7.243.128/25\",\n\t\t\t\t\"74.7.244.0/25\",\n\t\t\t\t\"132.196.86.0/24\",\n\t\t\t\t\"172.182.202.0/25\",\n\t\t\t\t\"172.182.207.0/25\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"OAI-SearchBot\",\n\t\t\t\"url\": \"https://openai.com/searchbot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"4.227.36.0/25\",\n\t\t\t\t\"13.66.216.176/28\",\n\t\t\t\t\"20.14.99.96/28\",\n\t\t\t\t\"20.25.151.224/28\",\n\t\t\t\t\"20.42.10.176/28\",\n\t\t\t\t\"20.168.18.32/28\",\n\t\t\t\t\"20.169.6.224/28\",\n\t\t\t\t\"20.169.7.48/28\",\n\t\t\t\t\"20.169.77.0/25\",\n\t\t\t\t\"20.171.53.224/28\",\n\t\t\t\t\"20.171.123.64/28\",\n\t\t\t\t\"23.102.145.48/28\",\n\t\t\t\t\"40.67.175.0/25\",\n\t\t\t\t\"40.90.214.16/28\",\n\t\t\t\t\"51.8.102.0/24\",\n\t\t\t\t\"74.7.175.128/25\",\n\t\t\t\t\"74.7.228.0/25\",\n\t\t\t\t\"74.7.228.128/25\",\n\t\t\t\t\"74.7.229.0/25\",\n\t\t\t\t\"74.7.229.128/25\",\n\t\t\t\t\"74.7.230.0/25\",\n\t\t\t\t\"74.7.241.128/25\",\n\t\t\t\t\"74.7.242.128/25\",\n\t\t\t\t\"74.7.243.0/25\",\n\t\t\t\t\"74.7.244.0/25\",\n\t\t\t\t\"104.210.140.128/28\",\n\t\t\t\t\"135.234.64.0/24\",\n\t\t\t\t\"172.182.193.80/28\",\n\t\t\t\t\"172.182.193.224/28\",\n\t\t\t\t\"172.182.194.32/28\",\n\t\t\t\t\"172.182.194.144/28\",\n\t\t\t\t\"172.182.195.48/28\",\n\t\t\t\t\"172.182.209.208/28\",\n\t\t\t\t\"172.182.211.192/28\",\n\t\t\t\t\"172.182.213.192/28\",\n\t\t\t\t\"172.182.224.0/28\",\n\t\t\t\t\"172.203.190.80/28\",\n\t\t\t\t\"172.203.190.112/28\",\n\t\t\t\t\"172.203.190.128/28\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"ChatGPT-User\",\n\t\t\t\"url\": \"https://openai.com/chatgpt-user.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"4.151.71.176/28\",\n\t\t\t\t\"4.151.119.48/28\",\n\t\t\t\t\"4.189.118.208/28\",\n\t\t\t\t\"4.189.119.48/28\",\n\t\t\t\t\"4.197.22.112/28\",\n\t\t\t\t\"4.197.64.0/28\",\n\t\t\t\t\"4.197.64.16/28\",\n\t\t\t\t\"4.197.64.48/28\",\n\t\t\t\t\"4.197.64.64/28\",\n\t\t\t\t\"4.197.115.112/28\",\n\t\t\t\t\"4.201.232.64/28\",\n\t\t\t\t\"4.205.128.176/28\",\n\t\t\t\t\"4.218.24.64/28\",\n\t\t\t\t\"4.226.200.16/28\",\n\t\t\t\t\"4.226.226.32/28\",\n\t\t\t\t\"9.129.0.0/16\",\n\t\t\t\t\"9.160.34.144/28\",\n\t\t\t\t\"9.160.36.16/28\",\n\t\t\t\t\"9.160.96.16/28\",\n\t\t\t\t\"9.160.128.16/28\",\n\t\t\t\t\"9.160.128.64/28\",\n\t\t\t\t\"9.160.163.128/28\",\n\t\t\t\t\"9.163.101.48/28\",\n\t\t\t\t\"9.205.8.48/28\",\n\t\t\t\t\"9.205.8.64/28\",\n\t\t\t\t\"9.205.25.128/28\",\n\t\t\t\t\"9.205.30.128/28\",\n\t\t\t\t\"9.205.30.144/28\",\n\t\t\t\t\"9.205.30.176/28\",\n\t\t\t\t\"9.205.128.32/28\",\n\t\t\t\t\"9.205.128.48/28\",\n\t\t\t\t\"9.223.181.208/28\",\n\t\t\t\t\"9.234.96.192/28\",\n\t\t\t\t\"9.234.97.96/28\",\n\t\t\t\t\"13.65.138.112/28\",\n\t\t\t\t\"13.67.72.16/28\",\n\t\t\t\t\"13.71.2.208/28\",\n\t\t\t\t\"13.76.32.208/28\",\n\t\t\t\t\"13.76.116.80/28\",\n\t\t\t\t\"13.83.167.128/28\",\n\t\t\t\t\"13.83.237.176/28\",\n\t\t\t\t\"20.45.178.144/28\",\n\t\t\t\t\"20.55.229.144/28\",\n\t\t\t\t\"20.57.199.192/28\",\n\t\t\t\t\"20.63.180.96/28\",\n\t\t\t\t\"20.63.221.64/28\",\n\t\t\t\t\"20.78.130.48/28\",\n\t\t\t\t\"20.83.243.176/28\",\n\t\t\t\t\"20.98.18.80/28\",\n\t\t\t\t\"20.102.212.144/28\",\n\t\t\t\t\"20.113.211.112/28\",\n\t\t\t\t\"20.113.225.112/28\",\n\t\t\t\t\"20.125.112.224/28\",\n\t\t\t\t\"20.125.144.144/28\",\n\t\t\t\t\"20.161.75.208/28\",\n\t\t\t\t\"20.168.7.192/28\",\n\t\t\t\t\"20.169.73.32/28\",\n\t\t\t\t\"20.169.73.64/28\",\n\t\t\t\t\"20.169.78.48/28\",\n\t\t\t\t\"20.169.78.64/28\",\n\t\t\t\t\"20.169.78.128/28\",\n\t\t\t\t\"20.169.78.160/28\",\n\t\t\t\t\"20.169.78.192/28\",\n\t\t\t\t\"20.169.86.224/28\",\n\t\t\t\t\"20.169.86.240/28\",\n\t\t\t\t\"20.170.184.16/28\",\n\t\t\t\t\"20.170.184.32/28\",\n\t\t\t\t\"20.170.184.48/28\",\n\t\t\t\t\"20.170.184.64/28\",\n\t\t\t\t\"20.170.184.80/28\",\n\t\t\t\t\"20.172.29.32/28\",\n\t\t\t\t\"20.199.211.160/28\",\n\t\t\t\t\"20.199.242.0/28\",\n\t\t\t\t\"20.200.212.240/28\",\n\t\t\t\t\"20.206.101.192/28\",\n\t\t\t\t\"20.210.211.192/28\",\n\t\t\t\t\"20.212.62.208/28\",\n\t\t\t\t\"20.215.187.208/28\",\n\t\t\t\t\"20.215.219.128/28\",\n\t\t\t\t\"20.215.219.160/28\",\n\t\t\t\t\"20.215.219.208/28\",\n\t\t\t\t\"20.218.30.240/28\",\n\t\t\t\t\"20.219.71.192/28\",\n\t\t\t\t\"20.219.161.192/28\",\n\t\t\t\t\"20.219.184.96/28\",\n\t\t\t\t\"20.222.36.192/28\",\n\t\t\t\t\"20.227.140.32/28\",\n\t\t\t\t\"20.228.106.176/28\",\n\t\t\t\t\"20.235.87.224/28\",\n\t\t\t\t\"20.249.63.208/28\",\n\t\t\t\t\"20.250.6.128/28\",\n\t\t\t\t\"20.250.136.64/28\",\n\t\t\t\t\"20.254.201.208/28\",\n\t\t\t\t\"23.98.142.176/28\",\n\t\t\t\t\"23.98.186.64/28\",\n\t\t\t\t\"23.98.186.96/28\",\n\t\t\t\t\"23.98.186.176/28\",\n\t\t\t\t\"23.98.186.192/28\",\n\t\t\t\t\"23.101.217.176/28\",\n\t\t\t\t\"23.102.140.144/28\",\n\t\t\t\t\"23.102.141.32/28\",\n\t\t\t\t\"40.74.200.208/28\",\n\t\t\t\t\"40.81.67.96/28\",\n\t\t\t\t\"40.81.234.144/28\",\n\t\t\t\t\"40.84.221.208/28\",\n\t\t\t\t\"40.84.221.224/28\",\n\t\t\t\t\"40.116.73.208/28\",\n\t\t\t\t\"40.119.36.240/28\",\n\t\t\t\t\"40.124.161.0/28\",\n\t\t\t\t\"48.221.40.176/28\",\n\t\t\t\t\"48.221.184.80/28\",\n\t\t\t\t\"48.221.184.96/28\",\n\t\t\t\t\"51.8.155.48/28\",\n\t\t\t\t\"51.8.155.112/28\",\n\t\t\t\t\"51.57.0.96/28\",\n\t\t\t\t\"51.59.24.64/28\",\n\t\t\t\t\"51.59.24.80/28\",\n\t\t\t\t\"51.59.48.80/28\",\n\t\t\t\t\"51.116.2.80/28\",\n\t\t\t\t\"51.116.221.96/28\",\n\t\t\t\t\"52.148.129.32/28\",\n\t\t\t\t\"52.156.77.144/28\",\n\t\t\t\t\"52.159.227.32/28\",\n\t\t\t\t\"52.159.249.96/28\",\n\t\t\t\t\"52.161.49.96/28\",\n\t\t\t\t\"52.165.212.48/28\",\n\t\t\t\t\"52.172.129.160/28\",\n\t\t\t\t\"52.173.219.96/28\",\n\t\t\t\t\"52.173.219.112/28\",\n\t\t\t\t\"52.173.221.16/28\",\n\t\t\t\t\"52.173.221.176/28\",\n\t\t\t\t\"52.173.221.208/28\",\n\t\t\t\t\"52.173.234.16/28\",\n\t\t\t\t\"52.173.234.80/28\",\n\t\t\t\t\"52.183.217.240/28\",\n\t\t\t\t\"52.190.137.16/28\",\n\t\t\t\t\"52.190.137.144/28\",\n\t\t\t\t\"52.190.139.48/28\",\n\t\t\t\t\"52.190.142.64/28\",\n\t\t\t\t\"52.190.190.16/28\",\n\t\t\t\t\"52.190.251.112/28\",\n\t\t\t\t\"52.225.75.208/28\",\n\t\t\t\t\"52.231.30.48/28\",\n\t\t\t\t\"52.231.34.176/28\",\n\t\t\t\t\"52.231.39.144/28\",\n\t\t\t\t\"52.231.50.64/28\",\n\t\t\t\t\"52.236.94.144/28\",\n\t\t\t\t\"52.241.146.208/28\",\n\t\t\t\t\"52.242.132.224/28\",\n\t\t\t\t\"52.242.132.240/28\",\n\t\t\t\t\"52.255.109.80/28\",\n\t\t\t\t\"52.255.109.96/28\",\n\t\t\t\t\"52.255.109.144/28\",\n\t\t\t\t\"52.255.111.0/28\",\n\t\t\t\t\"52.255.111.80/28\",\n\t\t\t\t\"57.154.174.112/28\",\n\t\t\t\t\"57.154.175.0/28\",\n\t\t\t\t\"57.154.187.32/28\",\n\t\t\t\t\"68.154.28.96/28\",\n\t\t\t\t\"68.220.57.64/28\",\n\t\t\t\t\"70.153.32.16/28\",\n\t\t\t\t\"70.153.32.32/28\",\n\t\t\t\t\"70.153.87.224/28\",\n\t\t\t\t\"70.153.189.192/28\",\n\t\t\t\t\"70.156.152.96/28\",\n\t\t\t\t\"74.7.35.48/28\",\n\t\t\t\t\"74.7.35.112/28\",\n\t\t\t\t\"74.7.36.64/28\",\n\t\t\t\t\"74.7.36.80/28\",\n\t\t\t\t\"74.7.36.96/28\",\n\t\t\t\t\"74.161.200.96/28\",\n\t\t\t\t\"74.224.217.64/28\",\n\t\t\t\t\"74.226.253.160/28\",\n\t\t\t\t\"74.248.37.160/28\",\n\t\t\t\t\"85.211.128.16/28\",\n\t\t\t\t\"85.211.128.32/28\",\n\t\t\t\t\"104.208.184.192/28\",\n\t\t\t\t\"104.210.139.192/28\",\n\t\t\t\t\"104.210.139.224/28\",\n\t\t\t\t\"128.85.198.32/28\",\n\t\t\t\t\"132.196.82.48/28\",\n\t\t\t\t\"134.33.102.192/28\",\n\t\t\t\t\"134.138.52.16/28\",\n\t\t\t\t\"134.138.52.64/28\",\n\t\t\t\t\"134.138.52.96/28\",\n\t\t\t\t\"134.138.52.128/28\",\n\t\t\t\t\"134.138.57.64/28\",\n\t\t\t\t\"134.138.57.80/28\",\n\t\t\t\t\"134.138.57.176/28\",\n\t\t\t\t\"134.138.58.32/28\",\n\t\t\t\t\"134.149.233.80/28\",\n\t\t\t\t\"135.13.64.240/28\",\n\t\t\t\t\"135.116.136.160/28\",\n\t\t\t\t\"135.220.73.208/28\",\n\t\t\t\t\"135.220.73.240/28\",\n\t\t\t\t\"135.222.99.32/28\",\n\t\t\t\t\"135.237.131.208/28\",\n\t\t\t\t\"135.237.133.48/28\",\n\t\t\t\t\"137.135.191.176/28\",\n\t\t\t\t\"138.91.46.96/28\",\n\t\t\t\t\"145.132.136.96/28\",\n\t\t\t\t\"158.158.5.32/28\",\n\t\t\t\t\"168.63.252.240/28\",\n\t\t\t\t\"172.162.248.64/28\",\n\t\t\t\t\"172.170.1.80/28\",\n\t\t\t\t\"172.170.225.0/28\",\n\t\t\t\t\"172.170.241.80/28\",\n\t\t\t\t\"172.172.206.48/28\",\n\t\t\t\t\"172.175.152.224/28\",\n\t\t\t\t\"172.178.140.144/28\",\n\t\t\t\t\"172.178.141.112/28\",\n\t\t\t\t\"172.178.141.128/28\",\n\t\t\t\t\"172.183.143.224/28\",\n\t\t\t\t\"172.183.222.128/28\",\n\t\t\t\t\"172.192.112.208/28\",\n\t\t\t\t\"172.197.160.192/28\",\n\t\t\t\t\"172.197.203.16/28\",\n\t\t\t\t\"172.198.58.176/28\",\n\t\t\t\t\"172.198.78.192/28\",\n\t\t\t\t\"172.198.79.112/28\",\n\t\t\t\t\"172.198.161.128/28\",\n\t\t\t\t\"172.199.137.80/28\",\n\t\t\t\t\"172.204.28.224/28\",\n\t\t\t\t\"172.204.96.80/28\",\n\t\t\t\t\"172.205.189.192/28\",\n\t\t\t\t\"172.206.38.240/28\",\n\t\t\t\t\"172.207.1.32/28\",\n\t\t\t\t\"172.212.172.160/28\",\n\t\t\t\t\"172.215.215.32/28\",\n\t\t\t\t\"191.232.238.96/28\",\n\t\t\t\t\"191.233.197.0/28\",\n\t\t\t\t\"191.233.199.160/28\",\n\t\t\t\t\"191.234.167.144/28\",\n\t\t\t\t\"191.235.99.160/28\",\n\t\t\t\t\"191.237.249.64/28\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"ClaudeBot\",\n\t\t\t\"url\": \"https://claude.com/crawling/bots.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"34.152.124.140/32\",\n\t\t\t\t\"34.153.21.190/32\",\n\t\t\t\t\"34.162.27.34/32\",\n\t\t\t\t\"34.162.86.102/32\",\n\t\t\t\t\"34.162.90.102/32\",\n\t\t\t\t\"34.162.111.78/32\",\n\t\t\t\t\"34.162.153.71/32\",\n\t\t\t\t\"34.162.191.81/32\",\n\t\t\t\t\"34.162.230.222/32\",\n\t\t\t\t\"34.162.244.71/32\",\n\t\t\t\t\"34.186.252.4/32\",\n\t\t\t\t\"136.79.143.251/32\",\n\t\t\t\t\"136.83.20.215/32\",\n\t\t\t\t\"216.73.216.0/22\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"PerplexityBot\",\n\t\t\t\"url\": \"https://www.perplexity.com/perplexitybot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"3.222.232.239/32\",\n\t\t\t\t\"3.224.62.45/32\",\n\t\t\t\t\"18.97.1.228/32\",\n\t\t\t\t\"18.97.9.96/32\",\n\t\t\t\t\"18.97.9.97/32\",\n\t\t\t\t\"18.97.9.98/32\",\n\t\t\t\t\"18.97.9.99/32\",\n\t\t\t\t\"18.97.9.100/32\",\n\t\t\t\t\"18.97.9.101/32\",\n\t\t\t\t\"18.97.9.102/32\",\n\t\t\t\t\"18.97.9.103/32\",\n\t\t\t\t\"18.210.92.235/32\",\n\t\t\t\t\"107.20.236.150/32\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"Perplexity-User\",\n\t\t\t\"url\": \"https://www.perplexity.com/perplexity-user.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"18.97.43.80/32\",\n\t\t\t\t\"18.97.43.81/32\",\n\t\t\t\t\"18.97.43.82/32\",\n\t\t\t\t\"18.97.43.83/32\",\n\t\t\t\t\"18.97.43.84/32\",\n\t\t\t\t\"18.97.43.85/32\",\n\t\t\t\t\"18.97.43.86/32\",\n\t\t\t\t\"18.97.43.87/32\",\n\t\t\t\t\"34.193.163.52/32\",\n\t\t\t\t\"44.208.221.197/32\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"MistralAI-User\",\n\t\t\t\"url\": \"https://mistral.ai/mistralai-user-ips.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"20.240.160.1/32\",\n\t\t\t\t\"20.240.160.161/32\",\n\t\t\t\t\"20.240.194.83/32\",\n\t\t\t\t\"51.12.243.114/32\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"CCBot\",\n\t\t\t\"url\": \"https://index.commoncrawl.org/ccbot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"3.41.188.32/29\",\n\t\t\t\t\"18.97.9.168/29\",\n\t\t\t\t\"18.97.14.80/29\",\n\t\t\t\t\"18.97.14.88/30\"\n\t\t\t],\n\t\t\t\"ipv6\": [\n\t\t\t\t\"2600:1f28:365:8000::/56\"\n\t\t\t]\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"DuckAssistBot\",\n\t\t\t\"url\": \"https://duckduckgo.com/duckassistbot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"4.144.148.23/32\",\n\t\t\t\t\"4.144.182.50/32\",\n\t\t\t\t\"4.144.227.142/32\",\n\t\t\t\t\"4.144.251.39/32\",\n\t\t\t\t\"4.149.142.7/32\",\n\t\t\t\t\"4.149.159.71/32\",\n\t\t\t\t\"4.149.159.102/32\",\n\t\t\t\t\"4.149.216.220/32\",\n\t\t\t\t\"4.150.142.218/32\",\n\t\t\t\t\"4.154.193.167/32\",\n\t\t\t\t\"4.156.30.123/32\",\n\t\t\t\t\"4.156.136.79/32\",\n\t\t\t\t\"4.156.154.107/32\",\n\t\t\t\t\"4.157.77.76/32\",\n\t\t\t\t\"4.172.49.103/32\",\n\t\t\t\t\"4.177.16.196/32\",\n\t\t\t\t\"4.182.10.198/32\",\n\t\t\t\t\"4.182.131.108/32\",\n\t\t\t\t\"4.195.133.120/32\",\n\t\t\t\t\"4.201.125.59/32\",\n\t\t\t\t\"4.201.141.71/32\",\n\t\t\t\t\"4.201.197.203/32\",\n\t\t\t\t\"4.201.206.133/32\",\n\t\t\t\t\"4.201.220.8/32\",\n\t\t\t\t\"4.207.220.92/32\",\n\t\t\t\t\"4.209.224.56/32\",\n\t\t\t\t\"4.213.46.14/32\",\n\t\t\t\t\"4.226.40.135/32\",\n\t\t\t\t\"4.228.76.163/32\",\n\t\t\t\t\"4.237.155.54/32\",\n\t\t\t\t\"4.237.244.80/32\",\n\t\t\t\t\"4.242.119.13/32\",\n\t\t\t\t\"4.246.10.252/32\",\n\t\t\t\t\"4.248.234.26/32\",\n\t\t\t\t\"4.249.216.104/32\",\n\t\t\t\t\"4.249.223.51/32\",\n\t\t\t\t\"4.249.238.11/32\",\n\t\t\t\t\"4.254.123.56/32\",\n\t\t\t\t\"4.255.35.121/32\",\n\t\t\t\t\"4.255.39.80/32\",\n\t\t\t\t\"13.86.35.212/32\",\n\t\t\t\t\"13.89.106.77/32\",\n\t\t\t\t\"13.90.178.139/32\",\n\t\t\t\t\"20.3.1.178/32\",\n\t\t\t\t\"20.3.60.212/32\",\n\t\t\t\t\"20.8.252.26/32\",\n\t\t\t\t\"20.9.114.242/32\",\n\t\t\t\t\"20.12.141.99/32\",\n\t\t\t\t\"20.12.249.123/32\",\n\t\t\t\t\"20.12.252.21/32\",\n\t\t\t\t\"20.13.44.19/32\",\n\t\t\t\t\"20.15.142.178/32\",\n\t\t\t\t\"20.23.88.231/32\",\n\t\t\t\t\"20.29.149.44/32\",\n\t\t\t\t\"20.29.154.56/32\",\n\t\t\t\t\"20.40.133.240/32\",\n\t\t\t\t\"20.40.147.172/32\",\n\t\t\t\t\"20.43.150.85/32\",\n\t\t\t\t\"20.43.150.93/32\",\n\t\t\t\t\"20.43.152.28/32\",\n\t\t\t\t\"20.43.172.120/32\",\n\t\t\t\t\"20.44.222.1/32\",\n\t\t\t\t\"20.49.129.236/32\",\n\t\t\t\t\"20.49.136.28/32\",\n\t\t\t\t\"20.50.48.159/32\",\n\t\t\t\t\"20.50.48.192/32\",\n\t\t\t\t\"20.50.49.0/32\",\n\t\t\t\t\"20.50.49.25/32\",\n\t\t\t\t\"20.50.49.40/32\",\n\t\t\t\t\"20.50.49.55/32\",\n\t\t\t\t\"20.50.49.237/32\",\n\t\t\t\t\"20.50.50.46/32\",\n\t\t\t\t\"20.50.50.118/32\",\n\t\t\t\t\"20.50.50.121/32\",\n\t\t\t\t\"20.50.50.123/32\",\n\t\t\t\t\"20.50.50.130/32\",\n\t\t\t\t\"20.50.50.134/32\",\n\t\t\t\t\"20.50.50.145/32\",\n\t\t\t\t\"20.50.50.146/32\",\n\t\t\t\t\"20.50.50.163/32\",\n\t\t\t\t\"20.50.168.91/32\",\n\t\t\t\t\"20.53.78.106/32\",\n\t\t\t\t\"20.53.78.123/32\",\n\t\t\t\t\"20.53.78.138/32\",\n\t\t\t\t\"20.53.78.144/32\",\n\t\t\t\t\"20.53.78.236/32\",\n\t\t\t\t\"20.53.91.2/32\",\n\t\t\t\t\"20.53.92.211/32\",\n\t\t\t\t\"20.53.134.160/32\",\n\t\t\t\t\"20.54.224.39/32\",\n\t\t\t\t\"20.56.197.58/32\",\n\t\t\t\t\"20.56.197.63/32\",\n\t\t\t\t\"20.59.0.65/32\",\n\t\t\t\t\"20.61.34.40/32\",\n\t\t\t\t\"20.61.142.192/32\",\n\t\t\t\t\"20.62.224.44/32\",\n\t\t\t\t\"20.64.185.45/32\",\n\t\t\t\t\"20.69.74.250/32\",\n\t\t\t\t\"20.69.129.205/32\",\n\t\t\t\t\"20.69.131.45/32\",\n\t\t\t\t\"20.69.138.149/32\",\n\t\t\t\t\"20.69.138.189/32\",\n\t\t\t\t\"20.69.144.137/32\",\n\t\t\t\t\"20.69.146.171/32\",\n\t\t\t\t\"20.69.147.21/32\",\n\t\t\t\t\"20.69.171.204/32\",\n\t\t\t\t\"20.71.12.143/32\",\n\t\t\t\t\"20.71.69.210/32\",\n\t\t\t\t\"20.72.197.110/32\",\n\t\t\t\t\"20.72.203.250/32\",\n\t\t\t\t\"20.72.218.29/32\",\n\t\t\t\t\"20.72.221.33/32\",\n\t\t\t\t\"20.72.221.45/32\",\n\t\t\t\t\"20.72.225.13/32\",\n\t\t\t\t\"20.72.242.93/32\",\n\t\t\t\t\"20.72.242.209/32\",\n\t\t\t\t\"20.73.132.240/32\",\n\t\t\t\t\"20.73.202.147/32\",\n\t\t\t\t\"20.75.144.152/32\",\n\t\t\t\t\"20.75.254.61/32\",\n\t\t\t\t\"20.77.146.108/32\",\n\t\t\t\t\"20.79.226.26/32\",\n\t\t\t\t\"20.79.238.198/32\",\n\t\t\t\t\"20.79.239.66/32\",\n\t\t\t\t\"20.80.114.203/32\",\n\t\t\t\t\"20.80.120.41/32\",\n\t\t\t\t\"20.80.129.80/32\",\n\t\t\t\t\"20.80.135.7/32\",\n\t\t\t\t\"20.80.156.88/32\",\n\t\t\t\t\"20.81.115.13/32\",\n\t\t\t\t\"20.82.24.207/32\",\n\t\t\t\t\"20.84.138.61/32\",\n\t\t\t\t\"20.84.219.45/32\",\n\t\t\t\t\"20.84.251.223/32\",\n\t\t\t\t\"20.85.138.75/32\",\n\t\t\t\t\"20.85.167.56/32\",\n\t\t\t\t\"20.85.187.146/32\",\n\t\t\t\t\"20.88.168.65/32\",\n\t\t\t\t\"20.93.28.24/32\",\n\t\t\t\t\"20.93.49.96/32\",\n\t\t\t\t\"20.99.154.113/32\",\n\t\t\t\t\"20.99.255.235/32\",\n\t\t\t\t\"20.100.136.36/32\",\n\t\t\t\t\"20.100.140.155/32\",\n\t\t\t\t\"20.101.17.173/32\",\n\t\t\t\t\"20.102.34.205/32\",\n\t\t\t\t\"20.109.129.162/32\",\n\t\t\t\t\"20.109.130.190/32\",\n\t\t\t\t\"20.109.241.18/32\",\n\t\t\t\t\"20.112.58.44/32\",\n\t\t\t\t\"20.113.3.121/32\",\n\t\t\t\t\"20.113.14.159/32\",\n\t\t\t\t\"20.115.181.187/32\",\n\t\t\t\t\"20.118.11.251/32\",\n\t\t\t\t\"20.119.127.146/32\",\n\t\t\t\t\"20.121.75.32/32\",\n\t\t\t\t\"20.166.171.150/32\",\n\t\t\t\t\"20.170.75.54/32\",\n\t\t\t\t\"20.175.232.228/32\",\n\t\t\t\t\"20.185.79.15/32\",\n\t\t\t\t\"20.185.79.47/32\",\n\t\t\t\t\"20.190.21.172/32\",\n\t\t\t\t\"20.191.44.16/32\",\n\t\t\t\t\"20.191.44.22/32\",\n\t\t\t\t\"20.191.44.119/32\",\n\t\t\t\t\"20.191.44.234/32\",\n\t\t\t\t\"20.191.45.212/32\",\n\t\t\t\t\"20.193.12.126/32\",\n\t\t\t\t\"20.193.24.10/32\",\n\t\t\t\t\"20.193.24.251/32\",\n\t\t\t\t\"20.193.25.197/32\",\n\t\t\t\t\"20.193.27.215/32\",\n\t\t\t\t\"20.193.45.113/32\",\n\t\t\t\t\"20.195.58.189/32\",\n\t\t\t\t\"20.195.108.47/32\",\n\t\t\t\t\"20.197.209.11/32\",\n\t\t\t\t\"20.197.209.27/32\",\n\t\t\t\t\"20.198.132.126/32\",\n\t\t\t\t\"20.198.152.208/32\",\n\t\t\t\t\"20.201.15.208/32\",\n\t\t\t\t\"20.204.228.200/32\",\n\t\t\t\t\"20.204.240.172/32\",\n\t\t\t\t\"20.204.241.148/32\",\n\t\t\t\t\"20.204.242.19/32\",\n\t\t\t\t\"20.204.242.101/32\",\n\t\t\t\t\"20.204.243.55/32\",\n\t\t\t\t\"20.204.246.81/32\",\n\t\t\t\t\"20.204.246.254/32\",\n\t\t\t\t\"20.207.72.11/32\",\n\t\t\t\t\"20.207.72.21/32\",\n\t\t\t\t\"20.207.72.110/32\",\n\t\t\t\t\"20.207.72.113/32\",\n\t\t\t\t\"20.207.97.190/32\",\n\t\t\t\t\"20.207.99.197/32\",\n\t\t\t\t\"20.207.107.181/32\",\n\t\t\t\t\"20.207.117.66/32\",\n\t\t\t\t\"20.212.90.107/32\",\n\t\t\t\t\"20.212.139.34/32\",\n\t\t\t\t\"20.216.200.223/32\",\n\t\t\t\t\"20.219.43.246/32\",\n\t\t\t\t\"20.219.45.67/32\",\n\t\t\t\t\"20.219.45.190/32\",\n\t\t\t\t\"20.221.10.115/32\",\n\t\t\t\t\"20.221.84.185/32\",\n\t\t\t\t\"20.226.133.105/32\",\n\t\t\t\t\"20.231.254.179/32\",\n\t\t\t\t\"20.232.51.46/32\",\n\t\t\t\t\"20.232.249.95/32\",\n\t\t\t\t\"20.236.40.93/32\",\n\t\t\t\t\"20.236.195.196/32\",\n\t\t\t\t\"20.236.198.192/32\",\n\t\t\t\t\"20.236.227.113/32\",\n\t\t\t\t\"20.236.233.23/32\",\n\t\t\t\t\"20.236.247.180/32\",\n\t\t\t\t\"20.237.1.51/32\",\n\t\t\t\t\"20.241.135.19/32\",\n\t\t\t\t\"20.241.147.151/32\",\n\t\t\t\t\"20.241.153.203/32\",\n\t\t\t\t\"20.241.164.5/32\",\n\t\t\t\t\"20.241.241.100/32\",\n\t\t\t\t\"20.242.133.53/32\",\n\t\t\t\t\"20.242.135.165/32\",\n\t\t\t\t\"20.242.211.139/32\",\n\t\t\t\t\"20.242.215.53/32\",\n\t\t\t\t\"20.242.237.240/32\",\n\t\t\t\t\"20.246.132.168/32\",\n\t\t\t\t\"20.246.225.34/32\",\n\t\t\t\t\"20.247.235.11/32\",\n\t\t\t\t\"20.250.51.113/32\",\n\t\t\t\t\"20.252.9.6/32\",\n\t\t\t\t\"20.252.16.193/32\",\n\t\t\t\t\"20.252.41.18/32\",\n\t\t\t\t\"20.252.63.162/32\",\n\t\t\t\t\"20.252.88.10/32\",\n\t\t\t\t\"20.253.59.76/32\",\n\t\t\t\t\"20.253.96.199/32\",\n\t\t\t\t\"20.253.114.51/32\",\n\t\t\t\t\"40.64.67.220/32\",\n\t\t\t\t\"40.64.78.45/32\",\n\t\t\t\t\"40.64.88.145/32\",\n\t\t\t\t\"40.64.105.247/32\",\n\t\t\t\t\"40.64.106.11/32\",\n\t\t\t\t\"40.64.106.152/32\",\n\t\t\t\t\"40.65.78.86/32\",\n\t\t\t\t\"40.76.162.17/32\",\n\t\t\t\t\"40.76.162.191/32\",\n\t\t\t\t\"40.76.162.208/32\",\n\t\t\t\t\"40.76.162.247/32\",\n\t\t\t\t\"40.76.163.7/32\",\n\t\t\t\t\"40.76.163.23/32\",\n\t\t\t\t\"40.76.173.151/32\",\n\t\t\t\t\"40.81.250.205/32\",\n\t\t\t\t\"40.82.218.203/32\",\n\t\t\t\t\"40.88.21.235/32\",\n\t\t\t\t\"40.88.195.62/32\",\n\t\t\t\t\"40.89.243.175/32\",\n\t\t\t\t\"40.114.182.45/32\",\n\t\t\t\t\"40.114.182.153/32\",\n\t\t\t\t\"40.114.182.172/32\",\n\t\t\t\t\"40.114.183.88/32\",\n\t\t\t\t\"40.114.183.196/32\",\n\t\t\t\t\"40.114.183.251/32\",\n\t\t\t\t\"40.119.232.50/32\",\n\t\t\t\t\"40.119.232.146/32\",\n\t\t\t\t\"40.119.232.215/32\",\n\t\t\t\t\"40.119.232.218/32\",\n\t\t\t\t\"40.119.232.251/32\",\n\t\t\t\t\"40.121.121.54/32\",\n\t\t\t\t\"40.127.154.196/32\",\n\t\t\t\t\"48.192.16.86/32\",\n\t\t\t\t\"48.192.74.178/32\",\n\t\t\t\t\"48.200.33.227/32\",\n\t\t\t\t\"48.202.155.200/32\",\n\t\t\t\t\"48.202.171.124/32\",\n\t\t\t\t\"48.204.32.87/32\",\n\t\t\t\t\"48.206.18.152/32\",\n\t\t\t\t\"48.217.23.236/32\",\n\t\t\t\t\"48.217.129.210/32\",\n\t\t\t\t\"48.217.212.89/32\",\n\t\t\t\t\"48.223.209.110/32\",\n\t\t\t\t\"51.8.31.28/32\",\n\t\t\t\t\"51.8.71.117/32\",\n\t\t\t\t\"51.8.253.152/32\",\n\t\t\t\t\"51.104.144.31/32\",\n\t\t\t\t\"51.104.146.225/32\",\n\t\t\t\t\"51.104.146.235/32\",\n\t\t\t\t\"51.104.160.167/32\",\n\t\t\t\t\"51.104.160.177/32\",\n\t\t\t\t\"51.104.161.32/32\",\n\t\t\t\t\"51.104.162.149/32\",\n\t\t\t\t\"51.104.163.250/32\",\n\t\t\t\t\"51.104.164.109/32\",\n\t\t\t\t\"51.104.164.147/32\",\n\t\t\t\t\"51.104.164.189/32\",\n\t\t\t\t\"51.104.164.215/32\",\n\t\t\t\t\"51.104.166.111/32\",\n\t\t\t\t\"51.104.167.19/32\",\n\t\t\t\t\"51.104.167.52/32\",\n\t\t\t\t\"51.104.167.54/32\",\n\t\t\t\t\"51.104.167.61/32\",\n\t\t\t\t\"51.104.167.71/32\",\n\t\t\t\t\"51.104.167.87/32\",\n\t\t\t\t\"51.104.167.88/32\",\n\t\t\t\t\"51.104.167.95/32\",\n\t\t\t\t\"51.104.167.96/32\",\n\t\t\t\t\"51.104.167.104/32\",\n\t\t\t\t\"51.104.167.110/32\",\n\t\t\t\t\"51.104.180.26/32\",\n\t\t\t\t\"51.104.180.47/32\",\n\t\t\t\t\"51.104.180.53/32\",\n\t\t\t\t\"51.107.40.209/32\",\n\t\t\t\t\"51.116.131.221/32\",\n\t\t\t\t\"51.120.48.122/32\",\n\t\t\t\t\"51.138.90.161/32\",\n\t\t\t\t\"51.138.90.206/32\",\n\t\t\t\t\"51.138.90.233/32\",\n\t\t\t\t\"51.143.114.45/32\",\n\t\t\t\t\"51.145.185.16/32\",\n\t\t\t\t\"52.142.24.149/32\",\n\t\t\t\t\"52.142.26.175/32\",\n\t\t\t\t\"52.143.95.162/32\",\n\t\t\t\t\"52.143.95.204/32\",\n\t\t\t\t\"52.143.241.111/32\",\n\t\t\t\t\"52.143.242.6/32\",\n\t\t\t\t\"52.143.243.117/32\",\n\t\t\t\t\"52.143.244.81/32\",\n\t\t\t\t\"52.143.247.235/32\",\n\t\t\t\t\"52.146.57.51/32\",\n\t\t\t\t\"52.146.58.236/32\",\n\t\t\t\t\"52.146.59.12/32\",\n\t\t\t\t\"52.146.59.154/32\",\n\t\t\t\t\"52.146.59.156/32\",\n\t\t\t\t\"52.146.63.80/32\",\n\t\t\t\t\"52.148.161.87/32\",\n\t\t\t\t\"52.148.165.38/32\",\n\t\t\t\t\"52.149.25.43/32\",\n\t\t\t\t\"52.149.28.18/32\",\n\t\t\t\t\"52.149.28.83/32\",\n\t\t\t\t\"52.149.30.45/32\",\n\t\t\t\t\"52.149.56.151/32\",\n\t\t\t\t\"52.149.58.27/32\",\n\t\t\t\t\"52.149.58.69/32\",\n\t\t\t\t\"52.149.58.139/32\",\n\t\t\t\t\"52.149.58.173/32\",\n\t\t\t\t\"52.149.60.38/32\",\n\t\t\t\t\"52.149.61.51/32\",\n\t\t\t\t\"52.154.60.82/32\",\n\t\t\t\t\"52.154.169.50/32\",\n\t\t\t\t\"52.154.169.200/32\",\n\t\t\t\t\"52.154.170.26/32\",\n\t\t\t\t\"52.154.170.28/32\",\n\t\t\t\t\"52.154.170.88/32\",\n\t\t\t\t\"52.154.170.96/32\",\n\t\t\t\t\"52.154.170.113/32\",\n\t\t\t\t\"52.154.170.117/32\",\n\t\t\t\t\"52.154.170.122/32\",\n\t\t\t\t\"52.154.170.209/32\",\n\t\t\t\t\"52.154.170.229/32\",\n\t\t\t\t\"52.154.170.243/32\",\n\t\t\t\t\"52.154.171.0/32\",\n\t\t\t\t\"52.154.171.44/32\",\n\t\t\t\t\"52.154.171.70/32\",\n\t\t\t\t\"52.154.171.87/32\",\n\t\t\t\t\"52.154.171.150/32\",\n\t\t\t\t\"52.154.171.196/32\",\n\t\t\t\t\"52.154.171.205/32\",\n\t\t\t\t\"52.154.171.235/32\",\n\t\t\t\t\"52.154.171.250/32\",\n\t\t\t\t\"52.154.172.2/32\",\n\t\t\t\t\"52.154.204.93/32\",\n\t\t\t\t\"52.185.93.208/32\",\n\t\t\t\t\"52.186.37.211/32\",\n\t\t\t\t\"52.188.89.106/32\",\n\t\t\t\t\"52.189.70.103/32\",\n\t\t\t\t\"52.190.37.160/32\",\n\t\t\t\t\"52.191.82.242/32\",\n\t\t\t\t\"52.191.222.158/32\",\n\t\t\t\t\"52.224.16.221/32\",\n\t\t\t\t\"52.224.16.229/32\",\n\t\t\t\t\"52.224.19.152/32\",\n\t\t\t\t\"52.224.20.174/32\",\n\t\t\t\t\"52.224.20.181/32\",\n\t\t\t\t\"52.224.20.186/32\",\n\t\t\t\t\"52.224.20.190/32\",\n\t\t\t\t\"52.224.20.193/32\",\n\t\t\t\t\"52.224.20.203/32\",\n\t\t\t\t\"52.224.20.204/32\",\n\t\t\t\t\"52.224.20.223/32\",\n\t\t\t\t\"52.224.20.227/32\",\n\t\t\t\t\"52.224.20.249/32\",\n\t\t\t\t\"52.224.21.4/32\",\n\t\t\t\t\"52.224.21.19/32\",\n\t\t\t\t\"52.224.21.20/32\",\n\t\t\t\t\"52.224.21.23/32\",\n\t\t\t\t\"52.224.21.27/32\",\n\t\t\t\t\"52.224.21.49/32\",\n\t\t\t\t\"52.224.21.51/32\",\n\t\t\t\t\"52.224.21.53/32\",\n\t\t\t\t\"52.224.21.55/32\",\n\t\t\t\t\"52.224.21.61/32\",\n\t\t\t\t\"52.224.147.21/32\",\n\t\t\t\t\"52.224.191.245/32\",\n\t\t\t\t\"52.242.224.168/32\",\n\t\t\t\t\"52.250.46.221/32\",\n\t\t\t\t\"57.151.44.140/32\",\n\t\t\t\t\"57.152.17.237/32\",\n\t\t\t\t\"57.152.62.107/32\",\n\t\t\t\t\"57.152.72.128/32\",\n\t\t\t\t\"57.155.18.44/32\",\n\t\t\t\t\"57.165.205.172/32\",\n\t\t\t\t\"57.167.88.38/32\",\n\t\t\t\t\"64.236.15.33/32\",\n\t\t\t\t\"64.236.93.100/32\",\n\t\t\t\t\"64.236.118.43/32\",\n\t\t\t\t\"68.219.152.220/32\",\n\t\t\t\t\"72.144.95.185/32\",\n\t\t\t\t\"74.163.169.106/32\",\n\t\t\t\t\"74.163.208.206/32\",\n\t\t\t\t\"74.179.200.126/32\",\n\t\t\t\t\"74.179.232.116/32\",\n\t\t\t\t\"74.242.194.48/32\",\n\t\t\t\t\"98.70.219.37/32\",\n\t\t\t\t\"104.43.54.127/32\",\n\t\t\t\t\"104.43.55.116/32\",\n\t\t\t\t\"104.43.55.117/32\",\n\t\t\t\t\"104.43.55.166/32\",\n\t\t\t\t\"104.43.55.167/32\",\n\t\t\t\t\"108.141.83.74/32\",\n\t\t\t\t\"128.203.82.154/32\",\n\t\t\t\t\"128.203.132.152/32\",\n\t\t\t\t\"128.203.181.79/32\",\n\t\t\t\t\"128.203.214.241/32\",\n\t\t\t\t\"128.203.236.110/32\",\n\t\t\t\t\"130.107.228.224/32\",\n\t\t\t\t\"131.145.39.118/32\",\n\t\t\t\t\"131.189.183.241/32\",\n\t\t\t\t\"132.164.209.198/32\",\n\t\t\t\t\"132.196.106.146/32\",\n\t\t\t\t\"132.220.1.71/32\",\n\t\t\t\t\"134.33.207.189/32\",\n\t\t\t\t\"135.171.251.169/32\",\n\t\t\t\t\"135.233.63.237/32\",\n\t\t\t\t\"135.234.221.112/32\",\n\t\t\t\t\"135.235.183.95/32\",\n\t\t\t\t\"172.168.12.93/32\",\n\t\t\t\t\"172.168.43.43/32\",\n\t\t\t\t\"172.168.51.216/32\",\n\t\t\t\t\"172.168.53.53/32\",\n\t\t\t\t\"172.168.81.217/32\",\n\t\t\t\t\"172.168.94.160/32\",\n\t\t\t\t\"172.168.115.250/32\",\n\t\t\t\t\"172.168.133.76/32\",\n\t\t\t\t\"172.168.137.102/32\",\n\t\t\t\t\"172.168.152.88/32\",\n\t\t\t\t\"172.168.173.230/32\",\n\t\t\t\t\"172.168.190.186/32\",\n\t\t\t\t\"172.168.227.120/32\",\n\t\t\t\t\"172.168.254.119/32\",\n\t\t\t\t\"172.169.7.33/32\",\n\t\t\t\t\"172.169.17.165/32\",\n\t\t\t\t\"172.169.28.184/32\",\n\t\t\t\t\"172.169.36.43/32\",\n\t\t\t\t\"172.169.53.208/32\",\n\t\t\t\t\"172.169.60.134/32\",\n\t\t\t\t\"172.169.93.148/32\",\n\t\t\t\t\"172.169.164.109/32\",\n\t\t\t\t\"172.169.177.131/32\",\n\t\t\t\t\"172.169.181.237/32\",\n\t\t\t\t\"172.171.37.221/32\",\n\t\t\t\t\"172.179.105.87/32\",\n\t\t\t\t\"172.179.128.118/32\",\n\t\t\t\t\"172.188.250.220/32\",\n\t\t\t\t\"172.189.40.18/32\",\n\t\t\t\t\"172.193.184.42/32\",\n\t\t\t\t\"172.193.197.146/32\",\n\t\t\t\t\"172.193.245.229/32\",\n\t\t\t\t\"172.194.141.49/32\",\n\t\t\t\t\"172.199.55.212/32\",\n\t\t\t\t\"172.210.111.68/32\",\n\t\t\t\t\"191.233.3.197/32\",\n\t\t\t\t\"191.233.3.202/32\",\n\t\t\t\t\"191.234.216.4/32\",\n\t\t\t\t\"191.234.216.178/32\",\n\t\t\t\t\"191.235.201.214/32\",\n\t\t\t\t\"191.235.202.38/32\",\n\t\t\t\t\"191.235.202.48/32\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"Applebot\",\n\t\t\t\"url\": \"https://search.developer.apple.com/applebot.json\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"17.166.20.0/24\",\n\t\t\t\t\"17.166.21.0/24\",\n\t\t\t\t\"17.166.22.0/24\",\n\t\t\t\t\"17.166.23.0/24\",\n\t\t\t\t\"17.166.24.0/24\",\n\t\t\t\t\"17.166.25.0/24\",\n\t\t\t\t\"17.166.150.0/24\",\n\t\t\t\t\"17.166.151.0/24\",\n\t\t\t\t\"17.166.152.0/24\",\n\t\t\t\t\"17.166.153.0/24\",\n\t\t\t\t\"17.166.154.0/24\",\n\t\t\t\t\"17.166.155.0/24\",\n\t\t\t\t\"17.166.192.48/28\",\n\t\t\t\t\"17.166.232.0/24\",\n\t\t\t\t\"17.166.233.0/24\",\n\t\t\t\t\"17.166.234.0/24\",\n\t\t\t\t\"17.166.235.0/24\",\n\t\t\t\t\"17.166.236.0/24\",\n\t\t\t\t\"17.166.237.0/24\",\n\t\t\t\t\"17.166.240.48/28\",\n\t\t\t\t\"17.166.248.48/28\",\n\t\t\t\t\"17.241.193.160/27\",\n\t\t\t\t\"17.241.200.160/27\",\n\t\t\t\t\"17.241.208.160/27\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t},\n\t\t{\n\t\t\t\"bot\": \"Claude API\",\n\t\t\t\"url\": \"https://platform.claude.com/docs/en/api/ip-addresses.md\",\n\t\t\t\"ipv4\": [\n\t\t\t\t\"153.61.208.0/20\",\n\t\t\t\t\"160.79.104.0/21\"\n\t\t\t],\n\t\t\t\"ipv6\": []\n\t\t}\n\t]\n}\n","const encoder = new TextEncoder();\nconst keys = new Map<string, Promise<CryptoKey>>();\n\nfunction importOrgKey(orgKey: string): Promise<CryptoKey> {\n\tlet key = keys.get(orgKey);\n\tif (!key) {\n\t\tkey = crypto.subtle.importKey(\n\t\t\t\"raw\",\n\t\t\tencoder.encode(orgKey),\n\t\t\t{ name: \"HMAC\", hash: \"SHA-256\" },\n\t\t\tfalse,\n\t\t\t[\"sign\"],\n\t\t);\n\t\tkey.catch(() => keys.delete(orgKey));\n\t\tkeys.set(orgKey, key);\n\t}\n\treturn key;\n}\n\nconst toHex = (bytes: ArrayBuffer): string =>\n\tArray.from(new Uint8Array(bytes), (byte) =>\n\t\tbyte.toString(16).padStart(2, \"0\"),\n\t).join(\"\");\n\nexport async function deriveMatchKey(\n\torgKey: string,\n\tip: string,\n): Promise<string> {\n\tconst signature = await crypto.subtle.sign(\n\t\t\"HMAC\",\n\t\tawait importOrgKey(orgKey),\n\t\tencoder.encode(ip),\n\t);\n\treturn toHex(signature);\n}\n","import { applyRules, type MatchRules } from \"./logic/decide\";\nimport { settings } from \"./settings\";\n\nconst isObject = (value: unknown): value is Record<string, unknown> =>\n\ttypeof value === \"object\" && value !== null;\n\nconst isStringArray = (value: unknown): value is string[] =>\n\tArray.isArray(value) && value.every((item) => typeof item === \"string\");\n\n/** The match rules in a rules file from the app (the `data` of its answer), or null when it does not have the expected form. A malformed range makes `applyRules` throw instead. */\nexport function parseRemoteRules(body: unknown): MatchRules | null {\n\tif (!isObject(body) || body.version !== 1) {\n\t\treturn null;\n\t}\n\tconst { tokens, ipRanges } = body;\n\tif (!isStringArray(tokens) || !Array.isArray(ipRanges)) {\n\t\treturn null;\n\t}\n\tconst ranges: MatchRules[\"ipRanges\"] = [];\n\tfor (const range of ipRanges) {\n\t\tif (\n\t\t\t!isObject(range) ||\n\t\t\t!isStringArray(range.ipv4) ||\n\t\t\t!isStringArray(range.ipv6)\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\t\tranges.push({ ipv4: range.ipv4, ipv6: range.ipv6 });\n\t}\n\treturn {\n\t\ttokens: tokens.map((token) => token.toLowerCase()),\n\t\tipRanges: ranges,\n\t};\n}\n\nlet lastAttemptOk = false;\nlet lastAttemptAt = Number.NEGATIVE_INFINITY;\n\nasync function fetchRules(apiUrl: string): Promise<boolean> {\n\tconst response = await fetch(`${apiUrl}/api/v2/agent-traffic/rules`, {\n\t\tsignal: AbortSignal.timeout(settings.requestTimeoutMs),\n\t});\n\tif (!response.ok) {\n\t\treturn false;\n\t}\n\tconst body: unknown = await response.json();\n\tconst rules = parseRemoteRules(isObject(body) ? body.data : null);\n\tif (!rules) {\n\t\treturn false;\n\t}\n\tapplyRules(rules);\n\treturn true;\n}\n\n/**\n * Starts one fetch of the rules file when the last attempt succeeded more than the refresh interval ago,\n * or failed more than the retry interval ago. Returns that work, or null.\n */\nexport function refreshRules(apiUrl: string): Promise<void> | null {\n\tconst now = Date.now();\n\tconst wait = lastAttemptOk ? settings.rulesRefreshMs : settings.rulesRetryMs;\n\tif (now - lastAttemptAt < wait) {\n\t\treturn null;\n\t}\n\tlastAttemptAt = now;\n\tlastAttemptOk = false;\n\treturn fetchRules(apiUrl).then(\n\t\t(ok) => {\n\t\t\tlastAttemptOk = ok;\n\t\t},\n\t\t() => undefined,\n\t);\n}\n","type WaitUntil = (promise: Promise<unknown>) => void;\ntype RequestContextStore = {\n\tget?: () => { waitUntil?: WaitUntil } | undefined;\n};\n\nconst REQUEST_CONTEXT = Symbol.for(\"@vercel/request-context\");\n\nexport function vercelRequestContext(): { waitUntil?: WaitUntil } | undefined {\n\tconst store: RequestContextStore | undefined = Reflect.get(\n\t\tglobalThis,\n\t\tREQUEST_CONTEXT,\n\t);\n\treturn store?.get?.();\n}\n\n/** The `waitUntil` that the Vercel runtime puts in the request context, the same one `@vercel/functions` reads. */\nexport function vercelWaitUntil(): WaitUntil | undefined {\n\tconst context = vercelRequestContext();\n\treturn context?.waitUntil\n\t\t? (promise) => context.waitUntil?.(promise)\n\t\t: undefined;\n}\n","import { vercelRequestContext } from \"./wait-until\";\n\nexport type Platform = \"vercel\" | \"cloudflare\" | \"cloudfront\";\n\n/** The platform this request runs on, or undefined on a runtime the relay does not know. Never throws. */\nexport function detectPlatform(): Platform | undefined {\n\ttry {\n\t\tconst context = vercelRequestContext();\n\t\tif (typeof context === \"object\" && context !== null) {\n\t\t\treturn \"vercel\";\n\t\t}\n\t\tif (globalThis.navigator?.userAgent === \"Cloudflare-Workers\") {\n\t\t\treturn \"cloudflare\";\n\t\t}\n\t} catch {}\n\treturn undefined;\n}\n","import { clientIp, HEADER_CAP } from \"./headers\";\nimport { detectPlatform, type Platform } from \"./platform\";\n\n/** `event.Records[0].cf.request` of a Lambda@Edge viewer-request event. */\nexport type CloudFrontRequest = {\n\tclientIp: string;\n\tmethod: string;\n\turi: string;\n\tquerystring: string;\n\theaders: Record<string, Array<{ key?: string; value: string }>>;\n};\n\nexport type RelayRequest = {\n\turl: string | null;\n\tmethod: string;\n\theaders: Headers;\n\tip: string | null;\n\tplatform: Platform | undefined;\n};\n\nexport function isCloudFrontRequest(\n\tinput: Request | CloudFrontRequest,\n): input is CloudFrontRequest {\n\treturn (\n\t\ttypeof Reflect.get(input, \"clientIp\") === \"string\" &&\n\t\ttypeof Reflect.get(input, \"uri\") === \"string\"\n\t);\n}\n\n/** The origin for a `host` header, or null when the value holds more than a host and an optional port. */\nfunction originOf(host: string | undefined): string | null {\n\tif (!host) {\n\t\treturn null;\n\t}\n\ttry {\n\t\tconst url = new URL(`https://${host}`);\n\t\tconst bare =\n\t\t\turl.pathname === \"/\" &&\n\t\t\turl.search === \"\" &&\n\t\t\turl.hash === \"\" &&\n\t\t\turl.username === \"\" &&\n\t\t\turl.password === \"\";\n\t\treturn bare ? url.origin : null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** The hostname in a `host` header or a bare hostname, lowercased and without the port, or null when the value holds more. */\nexport function hostnameOf(host: string | undefined): string | null {\n\tconst origin = originOf(host);\n\treturn origin === null ? null : new URL(origin).hostname;\n}\n\nexport function cloudFrontHeader(\n\tinput: CloudFrontRequest,\n\tname: string,\n): string | null {\n\tconst value = input.headers?.[name]?.[0]?.value;\n\treturn typeof value === \"string\" ? value : null;\n}\n\n/** Copies only the headers the relay reads, so a cookie or a value `Headers` refuses never makes the request fail. */\nfunction cloudFrontHeaders(input: CloudFrontRequest[\"headers\"]): Headers {\n\tconst headers = new Headers();\n\tfor (const name of HEADER_CAP) {\n\t\tfor (const entry of input?.[name] ?? []) {\n\t\t\tif (typeof entry?.value !== \"string\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\theaders.append(name, entry.value);\n\t\t\t} catch {}\n\t\t}\n\t}\n\treturn headers;\n}\n\n/** The client can set `X-Forwarded-For` at viewer-request, so a CloudFront request takes its IP from `clientIp` alone. `url` is null when the host header is missing or malformed. */\nexport function toRelayRequest(\n\tinput: Request | CloudFrontRequest,\n): RelayRequest {\n\tif (!isCloudFrontRequest(input)) {\n\t\treturn {\n\t\t\turl: input.url,\n\t\t\tmethod: input.method,\n\t\t\theaders: input.headers,\n\t\t\tip: clientIp(input.headers),\n\t\t\tplatform: detectPlatform(),\n\t\t};\n\t}\n\tconst origin = originOf(input.headers?.host?.[0]?.value);\n\tconst query = input.querystring ? `?${input.querystring}` : \"\";\n\tconst ip = input.clientIp.trim().toLowerCase();\n\treturn {\n\t\turl: origin === null ? null : `${origin}${input.uri}${query}`,\n\t\tmethod: input.method,\n\t\theaders: cloudFrontHeaders(input.headers),\n\t\tip: ip ? ip : null,\n\t\tplatform: \"cloudfront\",\n\t};\n}\n","import {\n\tfetchAgentPage,\n\tloadSiteRules,\n\tmatchesPath,\n\tnormalizePath,\n\ttype PageFormat,\n\tpageFormat,\n\ttype SiteRules,\n} from \"./agent-page\";\nimport { readCappedHeaders } from \"./headers\";\nimport { type PostPolicy, postEvent } from \"./ingest\";\nimport { readKey } from \"./key\";\nimport { decide } from \"./logic\";\nimport { deriveMatchKey } from \"./match-key\";\nimport type { Platform } from \"./platform\";\nimport { refreshRules } from \"./refresh\";\nimport {\n\ttype CloudFrontRequest,\n\tcloudFrontHeader,\n\thostnameOf,\n\tisCloudFrontRequest,\n\ttoRelayRequest,\n} from \"./request\";\nimport { settings } from \"./settings\";\nimport { RELAY_VERSION } from \"./version\";\nimport { vercelWaitUntil } from \"./wait-until\";\n\nexport type { CloudFrontRequest };\n\n/** The response a Lambda@Edge handler returns in place of the request. */\nexport type CloudFrontResultResponse = {\n\tstatus: string;\n\tstatusDescription?: string;\n\theaders: Record<string, Array<{ key: string; value: string }>>;\n\tbody?: string;\n};\n\nexport type Region = keyof typeof settings.apiUrls;\n\nexport type RelayOptions = {\n\t/** The region of your WaniWani organization. Defaults to `\"us\"`. */\n\tregion?: Region;\n\t/** Overrides the host that `region` picks. */\n\tapiUrl?: string;\n\t/** Overrides `process.env.WANIWANI_AGENT_TRAFFIC_KEY`. */\n\tkey?: string;\n\t/** Keeps the background post alive after the response. Defaults to the `waitUntil` of the Vercel request context. */\n\twaitUntil?: (promise: Promise<unknown>) => void;\n\t/** Header names, and the fields `matchKey`, `userAgent` or `referer`, to leave out of every event. */\n\tomit?: Array<string>;\n\t/** `\"remote\"` loads the newest AI bot list from WaniWani every 12 hours. `\"bundled\"` keeps the list shipped in this version. Defaults to `\"remote\"`. */\n\trules?: \"remote\" | \"bundled\";\n};\n\nexport type IsAgentOptions = Pick<\n\tRelayOptions,\n\t\"region\" | \"apiUrl\" | \"waitUntil\" | \"rules\"\n>;\n\nexport type ServeAgentPageOptions = Pick<\n\tRelayOptions,\n\t\"region\" | \"apiUrl\" | \"key\"\n>;\n\nexport type AgentVisitRecord = {\n\toccurredAt: string;\n\tmethod: string;\n\thost: string;\n\tpath: string;\n\tuserAgent?: string;\n\treferer?: string;\n\theaders: Record<string, string>;\n\tmatchKey?: string;\n\tsourceEventId: string;\n\trelayVersion: string;\n\tplatform?: Platform;\n\t/** The relay functions this process called, so WaniWani can show which setup steps the code has. */\n\tuses: RelayFunction[];\n};\n\nexport type RelayFunction =\n\t| \"recordAgentVisit\"\n\t| \"serveAgentPage\"\n\t| \"handleViewerRequest\";\n\nconst used = new Set<RelayFunction>();\n\n/**\n * Tells whether a request, or the `cf.request` of a Lambda@Edge event, comes from an AI agent. Reads headers only and never waits on the network.\n * Every 12 hours it starts one background fetch of the newest AI bot list, unless `rules` is `\"bundled\"`.\n */\nexport function isAgent(\n\trequest: Request | CloudFrontRequest,\n\toptions: IsAgentOptions = {},\n): boolean {\n\tstartRulesRefresh(options);\n\ttry {\n\t\tconst { headers, ip } = toRelayRequest(request);\n\t\treturn decide({\n\t\t\theaders: readCappedHeaders(headers),\n\t\t\tip,\n\t\t\tuseBundled: options.rules === \"bundled\",\n\t\t});\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction startRulesRefresh(options: IsAgentOptions): void {\n\ttry {\n\t\tif (options.rules === \"bundled\") {\n\t\t\treturn;\n\t\t}\n\t\tconst apiUrl = resolveApiUrl(options);\n\t\tconst work = apiUrl ? refreshRules(apiUrl) : null;\n\t\tif (work) {\n\t\t\tschedule(work, options.waitUntil);\n\t\t}\n\t} catch {}\n}\n\n/**\n * Posts one event for this request to WaniWani after the response.\n * Sends nothing when no key is set. Never throws, never rejects and never touches the request.\n * The promise settles when the post is done, so Lambda@Edge, which has no `waitUntil`, awaits it.\n */\nexport function recordAgentVisit(\n\trequest: Request | CloudFrontRequest,\n\toptions: RelayOptions = {},\n): Promise<void> {\n\tused.add(\"recordAgentVisit\");\n\ttry {\n\t\tconst key = readKey(options.key);\n\t\tif (!key) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\tconst apiUrl = resolveApiUrl(options);\n\t\tif (!apiUrl) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\tconst omitted = omitSet(options.omit);\n\t\tconst {\n\t\t\turl: requestUrl,\n\t\t\tmethod,\n\t\t\theaders: requestHeaders,\n\t\t\tip,\n\t\t\tplatform,\n\t\t} = toRelayRequest(request);\n\t\tif (requestUrl === null) {\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\tconst capped = readCappedHeaders(requestHeaders);\n\n\t\tconst headers: Record<string, string> = {};\n\t\tfor (const [name, value] of Object.entries(capped)) {\n\t\t\tif (!omitted.has(name)) {\n\t\t\t\theaders[name] = value;\n\t\t\t}\n\t\t}\n\t\tconst url = new URL(requestUrl);\n\t\tconst record: AgentVisitRecord = {\n\t\t\toccurredAt: new Date().toISOString(),\n\t\t\tmethod,\n\t\t\thost: url.hostname,\n\t\t\tpath: url.pathname + url.search,\n\t\t\theaders,\n\t\t\tsourceEventId: crypto.randomUUID(),\n\t\t\trelayVersion: RELAY_VERSION,\n\t\t\tuses: [],\n\t\t};\n\t\tif (capped[\"user-agent\"] !== undefined && !omitted.has(\"user-agent\")) {\n\t\t\trecord.userAgent = capped[\"user-agent\"];\n\t\t}\n\t\tif (capped.referer !== undefined && !omitted.has(\"referer\")) {\n\t\t\trecord.referer = capped.referer;\n\t\t}\n\t\tif (platform !== undefined) {\n\t\t\trecord.platform = platform;\n\t\t}\n\n\t\tconst policy: PostPolicy =\n\t\t\tplatform === \"cloudfront\"\n\t\t\t\t? { timeoutMs: settings.cloudFrontTimeoutMs, retry: false }\n\t\t\t\t: { timeoutMs: settings.requestTimeoutMs, retry: true };\n\t\tconst ingestUrl = `${apiUrl}/api/v2/agent-traffic/ingest/middleware/${key.ingestToken}`;\n\t\tconst withMatchKey = async () => {\n\t\t\t// Lets a serveAgentPage or handleViewerRequest call that follows on the same request join `used`.\n\t\t\tawait Promise.resolve();\n\t\t\trecord.uses = [...used];\n\t\t\tif (ip !== null && !omitted.has(\"matchkey\")) {\n\t\t\t\trecord.matchKey = await deriveMatchKey(key.orgKey, ip).catch(\n\t\t\t\t\t() => undefined,\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait postEvent(\n\t\t\t\tingestUrl,\n\t\t\t\tJSON.stringify({ requests: [record] }),\n\t\t\t\tpolicy,\n\t\t\t);\n\t\t};\n\t\treturn schedule(withMatchKey(), options.waitUntil);\n\t} catch {\n\t\treturn Promise.resolve();\n\t}\n}\n\ntype PageRequest = {\n\tmethod: string;\n\thostname: string;\n\tpathname: string;\n\tuserAgent: string;\n\taccept: string;\n\trendering: boolean;\n};\n\ntype PageAnswer = {\n\tstatus: 200 | 403;\n\theaders: Record<string, string>;\n\tbody: Uint8Array<ArrayBuffer> | string | null;\n};\n\ntype SiteMatch = {\n\trules: SiteRules;\n\tsite: SiteRules[\"sites\"][number];\n};\n\nconst BLOCKED: Omit<PageAnswer, \"body\"> = {\n\tstatus: 403,\n\theaders: {\n\t\t\"content-type\": \"text/plain; charset=utf-8\",\n\t\t\"x-waniwani-blocked\": \"1\",\n\t\t\"cache-control\": \"private, no-store\",\n\t\tvary: \"user-agent\",\n\t},\n};\n\n/**\n * The stored agent page for this request, when its site lists the path and the user agent acts for a user. A 403 when the site blocks the user agent. Otherwise null, and the caller serves the origin.\n * A `CloudFrontRequest` comes from the origin-request handler. Only a URI that `handleViewerRequest` marked gets the page, read for the host in the mark. The function restores `request.uri` to the original path before anything else, and it never returns a 403 on this path.\n * Never throws and never rejects.\n */\nexport function serveAgentPage(\n\trequest: Request,\n\toptions?: ServeAgentPageOptions,\n): Promise<Response | null>;\nexport function serveAgentPage(\n\trequest: CloudFrontRequest,\n\toptions?: ServeAgentPageOptions,\n): Promise<CloudFrontResultResponse | null>;\nexport async function serveAgentPage(\n\trequest: Request | CloudFrontRequest,\n\toptions: ServeAgentPageOptions = {},\n): Promise<Response | CloudFrontResultResponse | null> {\n\tused.add(\"serveAgentPage\");\n\ttry {\n\t\treturn isCloudFrontRequest(request)\n\t\t\t? await serveCloudFront(request, options)\n\t\t\t: await serveFetch(request, options);\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nasync function serveFetch(\n\trequest: Request,\n\toptions: ServeAgentPageOptions,\n): Promise<Response | null> {\n\tconst url = new URL(request.url);\n\tconst answer = await answerAgentPage(\n\t\t{\n\t\t\tmethod: request.method.toUpperCase(),\n\t\t\thostname: url.hostname,\n\t\t\tpathname: url.pathname,\n\t\t\tuserAgent: request.headers.get(\"user-agent\") ?? \"\",\n\t\t\taccept: request.headers.get(\"accept\") ?? \"\",\n\t\t\trendering: request.headers.has(\"x-waniwani-render\"),\n\t\t},\n\t\toptions,\n\t);\n\tif (!answer) {\n\t\treturn null;\n\t}\n\treturn new Response(answer.body, {\n\t\tstatus: answer.status,\n\t\theaders: answer.headers,\n\t});\n}\n\nasync function serveCloudFront(\n\trequest: CloudFrontRequest,\n\toptions: ServeAgentPageOptions,\n): Promise<CloudFrontResultResponse | null> {\n\tconst mark = takeMarkFromUri(request);\n\tif (mark === null) {\n\t\treturn null;\n\t}\n\tconst { hostname, format } = mark;\n\tconst method = request.method.toUpperCase();\n\tif (!isPageMethod(method)) {\n\t\treturn null;\n\t}\n\tconst match = await findSite(hostname, options);\n\tif (!match) {\n\t\treturn null;\n\t}\n\tconst answer = await pageAnswer(\n\t\tmatch.rules,\n\t\thostname,\n\t\tnormalizePath(request.uri),\n\t\tmethod,\n\t\tformat,\n\t);\n\tif (!answer) {\n\t\treturn null;\n\t}\n\tconst result = toCloudFrontResponse(answer);\n\tif (\n\t\tresult.body !== undefined &&\n\t\tnew TextEncoder().encode(result.body).byteLength >\n\t\t\tsettings.cloudFrontMaxBodyBytes\n\t) {\n\t\treturn null;\n\t}\n\treturn result;\n}\n\nfunction toCloudFrontResponse(answer: PageAnswer): CloudFrontResultResponse {\n\tconst headers: CloudFrontResultResponse[\"headers\"] = {};\n\tfor (const [name, value] of Object.entries(answer.headers)) {\n\t\theaders[name] = [{ key: headerKey(name), value }];\n\t}\n\tconst result: CloudFrontResultResponse = {\n\t\tstatus: String(answer.status),\n\t\tstatusDescription: answer.status === 200 ? \"OK\" : \"Forbidden\",\n\t\theaders,\n\t};\n\tif (answer.body !== null) {\n\t\tresult.body =\n\t\t\ttypeof answer.body === \"string\"\n\t\t\t\t? answer.body\n\t\t\t\t: new TextDecoder().decode(answer.body);\n\t}\n\treturn result;\n}\n\nfunction headerKey(name: string): string {\n\treturn name\n\t\t.split(\"-\")\n\t\t.map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n\t\t.join(\"-\");\n}\n\nconst isPageMethod = (method: string): boolean =>\n\tmethod === \"GET\" || method === \"HEAD\";\n\nasync function findSite(\n\thostname: string,\n\toptions: ServeAgentPageOptions,\n): Promise<SiteMatch | null> {\n\tconst key = readKey(options.key);\n\tif (!key) {\n\t\treturn null;\n\t}\n\tconst apiUrl = resolveApiUrl(options);\n\tif (!apiUrl) {\n\t\treturn null;\n\t}\n\tconst rules = await loadSiteRules(apiUrl, key.ingestToken, (work) =>\n\t\tschedule(work, undefined),\n\t);\n\tif (!rules) {\n\t\treturn null;\n\t}\n\tconst site = rules.sites.find((entry) => entry.hostname === hostname);\n\treturn site ? { rules, site } : null;\n}\n\nfunction blockedAnswer(\n\tsite: SiteMatch[\"site\"],\n\tuserAgent: string,\n\tmethod: string,\n): PageAnswer | null {\n\treturn site.blockedUserAgentTokens.some((token) => userAgent.includes(token))\n\t\t? { ...BLOCKED, body: method === \"HEAD\" ? null : \"Forbidden\" }\n\t\t: null;\n}\n\nasync function answerAgentPage(\n\trequest: PageRequest,\n\toptions: ServeAgentPageOptions,\n): Promise<PageAnswer | null> {\n\tconst { method, hostname } = request;\n\tif (!isPageMethod(method)) {\n\t\treturn null;\n\t}\n\tconst match = await findSite(hostname, options);\n\tif (!match) {\n\t\treturn null;\n\t}\n\tconst { rules, site } = match;\n\tconst userAgent = request.userAgent.toLowerCase();\n\tconst blocked = blockedAnswer(site, userAgent, method);\n\tif (blocked) {\n\t\treturn blocked;\n\t}\n\tif (request.rendering || !site.agentPages.enabled) {\n\t\treturn null;\n\t}\n\tconst path = normalizePath(request.pathname);\n\tif (!matchesPath(path, site.agentPages.paths)) {\n\t\treturn null;\n\t}\n\tif (!rules.userAgentTokens.some((token) => userAgent.includes(token))) {\n\t\treturn null;\n\t}\n\treturn pageAnswer(rules, hostname, path, method, pageFormat(request.accept));\n}\n\nasync function pageAnswer(\n\trules: SiteRules,\n\thostname: string,\n\tpath: string,\n\tmethod: string,\n\tformat: PageFormat,\n): Promise<PageAnswer | null> {\n\tconst page = await fetchAgentPage(rules, hostname, path, format);\n\tif (!page) {\n\t\treturn null;\n\t}\n\treturn {\n\t\tstatus: 200,\n\t\theaders: {\n\t\t\t\"content-type\": page.contentType,\n\t\t\t\"x-waniwani-agent-page\": \"hit\",\n\t\t\t\"cache-control\": \"private, no-store\",\n\t\t\tvary: \"user-agent, accept\",\n\t\t},\n\t\tbody: method === \"HEAD\" ? null : page.body,\n\t};\n}\n\nconst AGENT_URI_PREFIX = \"/__waniwani-agent/\";\n\nconst PAGE_FORMATS: readonly PageFormat[] = [\"markdown\", \"html\"];\n\n/** Splits `/__waniwani-agent/<format>/<host><original uri>`. The format is null when the mark names none the relay knows. */\nfunction splitAgentUri(uri: string): {\n\tformat: PageFormat | null;\n\tencodedHost: string;\n\toriginalUri: string;\n} | null {\n\tif (!uri.startsWith(AGENT_URI_PREFIX)) {\n\t\treturn null;\n\t}\n\tconst rest = uri.slice(AGENT_URI_PREFIX.length);\n\tconst formatEnd = rest.indexOf(\"/\");\n\tconst formatName = formatEnd === -1 ? rest : rest.slice(0, formatEnd);\n\tconst format = PAGE_FORMATS.find((known) => known === formatName) ?? null;\n\tconst afterFormat = formatEnd === -1 ? \"\" : rest.slice(formatEnd + 1);\n\tconst slash = afterFormat.indexOf(\"/\");\n\treturn slash === -1\n\t\t? { format, encodedHost: afterFormat, originalUri: \"/\" }\n\t\t: {\n\t\t\t\tformat,\n\t\t\t\tencodedHost: afterFormat.slice(0, slash),\n\t\t\t\toriginalUri: afterFormat.slice(slash),\n\t\t\t};\n}\n\nfunction stripForgedMark(request: CloudFrontRequest): void {\n\tlet forged = splitAgentUri(request.uri);\n\twhile (forged) {\n\t\trequest.uri = forged.originalUri;\n\t\tforged = splitAgentUri(request.uri);\n\t}\n}\n\nfunction takeMarkFromUri(\n\trequest: CloudFrontRequest,\n): { hostname: string; format: PageFormat } | null {\n\tconst parts = splitAgentUri(request.uri);\n\tif (!parts) {\n\t\treturn null;\n\t}\n\trequest.uri = parts.originalUri;\n\tif (parts.format === null) {\n\t\treturn null;\n\t}\n\ttry {\n\t\tconst hostname = hostnameOf(decodeURIComponent(parts.encodedHost));\n\t\treturn hostname === null ? null : { hostname, format: parts.format };\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * For the viewer-request handler on CloudFront. Strips a `/__waniwani-agent/` prefix the client sent, then, for a GET or HEAD from an AI agent, loads the rules of the site of the `host` header.\n * Returns the 403 response when the site blocks the user agent. CloudFront never caches a response made at viewer-request, so a block takes effect at once.\n * When the site serves an agent page for the path and the user agent acts for a user, rewrites `request.uri` to `/__waniwani-agent/<host><original uri>` so the origin-request handler serves the page under its own cache key. The request stays unmarked when the new URI would pass the CloudFront URI limit.\n * Call `recordAgentVisit` before it, so the event holds the original path. Never throws and never rejects.\n */\nexport async function handleViewerRequest(\n\trequest: CloudFrontRequest,\n\toptions: ServeAgentPageOptions = {},\n): Promise<CloudFrontResultResponse | null> {\n\tused.add(\"handleViewerRequest\");\n\ttry {\n\t\tstripForgedMark(request);\n\t\tconst method = request.method.toUpperCase();\n\t\tif (!isPageMethod(method) || !isAgent(request, options)) {\n\t\t\treturn null;\n\t\t}\n\t\tconst hostname = hostnameOf(cloudFrontHeader(request, \"host\") ?? undefined);\n\t\tif (hostname === null) {\n\t\t\treturn null;\n\t\t}\n\t\tconst match = await findSite(hostname, options);\n\t\tif (!match) {\n\t\t\treturn null;\n\t\t}\n\t\tconst { rules, site } = match;\n\t\tconst userAgent = (\n\t\t\tcloudFrontHeader(request, \"user-agent\") ?? \"\"\n\t\t).toLowerCase();\n\t\tconst blocked = blockedAnswer(site, userAgent, method);\n\t\tif (blocked) {\n\t\t\treturn toCloudFrontResponse(blocked);\n\t\t}\n\t\tif (\n\t\t\tcloudFrontHeader(request, \"x-waniwani-render\") !== null ||\n\t\t\t!site.agentPages.enabled ||\n\t\t\t!matchesPath(normalizePath(request.uri), site.agentPages.paths) ||\n\t\t\t!rules.userAgentTokens.some((token) => userAgent.includes(token))\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\t\tconst format = pageFormat(cloudFrontHeader(request, \"accept\") ?? \"\");\n\t\tconst uri = `${AGENT_URI_PREFIX}${format}/${encodeURIComponent(hostname)}${request.uri}`;\n\t\tif (\n\t\t\tnew TextEncoder().encode(uri).byteLength <= settings.cloudFrontMaxUriBytes\n\t\t) {\n\t\t\trequest.uri = uri;\n\t\t}\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nlet warnedRegion = false;\n\nfunction resolveApiUrl(options: IsAgentOptions): string | null {\n\tif (options.apiUrl !== undefined) {\n\t\treturn options.apiUrl.replace(/\\/+$/, \"\");\n\t}\n\tconst region = options.region ?? \"us\";\n\tif (Object.hasOwn(settings.apiUrls, region)) {\n\t\treturn settings.apiUrls[region];\n\t}\n\tif (!warnedRegion) {\n\t\twarnedRegion = true;\n\t\tconsole.warn(\n\t\t\t`[@waniwani/relay] Unknown region ${JSON.stringify(region)}, so no agent visits are recorded. Use \"us\" or \"eu\".`,\n\t\t);\n\t}\n\treturn null;\n}\n\nfunction omitSet(omit: Array<string> | undefined): Set<string> {\n\tconst names = new Set((omit ?? []).map((name) => name.toLowerCase()));\n\tif (names.has(\"useragent\")) {\n\t\tnames.add(\"user-agent\");\n\t}\n\treturn names;\n}\n\nfunction schedule(\n\twork: Promise<unknown>,\n\twaitUntil: RelayOptions[\"waitUntil\"],\n): Promise<void> {\n\tconst settled = work.then(\n\t\t() => undefined,\n\t\t() => undefined,\n\t);\n\ttry {\n\t\t(waitUntil ?? vercelWaitUntil())?.(settled);\n\t} catch {}\n\treturn settled;\n}\n"],"mappings":"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AA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;;;AC/KA;AAAA,EACC,SAAW;AAAA,IACV;AAAA,MACC,KAAO;AAAA,MACP,KAAO;AAAA,MACP,MAAQ;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,MACA,MAAQ,CAAC;AAAA,IACV;AAAA,IACA;AAAA,MACC,KAAO;AAAA,MACP,KAAO;AAAA,MACP,MAAQ;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,MACA,MAAQ,CAAC;AAAA,IACV;AAAA,IACA;AAAA,MACC,KAAO;AAAA,MACP,KAAO;AAAA,MACP,MAAQ;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,MACA,MAAQ,CAAC;AAAA,IACV;AAAA,IACA;AAAA,MACC,KAAO;AAAA,MACP,KAAO;AAAA,MACP,MAAQ;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,MACA,MAAQ,CAAC;AAAA,IACV;AAAA,IACA;AAAA,MACC,KAAO;AAAA,MACP,KAAO;AAAA,MACP,MAAQ;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,MACA,MAAQ,CAAC;AAAA,IACV;AAAA,IACA;AAAA,MACC,KAAO;AAAA,MACP,KAAO;AAAA,MACP,MAAQ;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,MACA,MAAQ,CAAC;AAAA,IACV;AAAA,IACA;AAAA,MACC,KAAO;AAAA,MACP,KAAO;AAAA,MACP,MAAQ;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,MACA,MAAQ,CAAC;AAAA,IACV;AAAA,IACA;AAAA,MACC,KAAO;AAAA,MACP,KAAO;AAAA,MACP,MAAQ;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,MACA,MAAQ;AAAA,QACP;AAAA,MACD;AAAA,IACD;AAAA,IACA;AAAA,MACC,KAAO;AAAA,MACP,KAAO;AAAA,MACP,MAAQ;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACD;AAAA,MACA,MAAQ,CAAC;AAAA,IACV;AAAA,IACA;AAAA,MACC,KAAO;AAAA,MACP,KAAO;AAAA,MACP,MAAQ;AAAA,QACP;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA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","names":["isObject","isStringArray"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@waniwani/relay",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Records AI agent visits to your website from your middleware and posts them to WaniWani. Open source, so your security team can read every line.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|