@waniwani/relay 0.4.3 → 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 +7 -2
- package/dist/index.d.ts +4 -1
- package/dist/index.js +12 -3
- 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
|
|
|
@@ -173,10 +174,14 @@ Viewer-request handler:
|
|
|
173
174
|
|
|
174
175
|
```ts
|
|
175
176
|
const request = event.Records[0].cf.request;
|
|
176
|
-
|
|
177
|
-
|
|
177
|
+
const visit = isAgent(request) ? recordAgentVisit(request, { key }) : null;
|
|
178
|
+
const answer = await handleViewerRequest(request, { key });
|
|
179
|
+
await visit;
|
|
180
|
+
return answer ?? request;
|
|
178
181
|
```
|
|
179
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
|
+
|
|
180
185
|
Origin-request handler:
|
|
181
186
|
|
|
182
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;
|
|
@@ -370,7 +370,7 @@ import { BlockList } from "node:net";
|
|
|
370
370
|
|
|
371
371
|
// src/logic/agents.generated.json
|
|
372
372
|
var agents_generated_default = {
|
|
373
|
-
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.",
|
|
374
374
|
tokens: [
|
|
375
375
|
"addsearchbot",
|
|
376
376
|
"agentdatabot",
|
|
@@ -482,6 +482,7 @@ var agents_generated_default = {
|
|
|
482
482
|
"linerbot",
|
|
483
483
|
"linkupbot",
|
|
484
484
|
"manus-user",
|
|
485
|
+
"mediavinebot/",
|
|
485
486
|
"meta-externalagent",
|
|
486
487
|
"meta-externalfetcher",
|
|
487
488
|
"meta-webindexer",
|
|
@@ -507,6 +508,7 @@ var agents_generated_default = {
|
|
|
507
508
|
"phindbot",
|
|
508
509
|
"poggio-citations",
|
|
509
510
|
"poseidon research crawler",
|
|
511
|
+
"publogic crawler",
|
|
510
512
|
"qodercli",
|
|
511
513
|
"qualifiedbot",
|
|
512
514
|
"querit-searchbot",
|
|
@@ -1717,6 +1719,7 @@ function toRelayRequest(input) {
|
|
|
1717
1719
|
}
|
|
1718
1720
|
|
|
1719
1721
|
// src/index.ts
|
|
1722
|
+
var used = /* @__PURE__ */ new Set();
|
|
1720
1723
|
function isAgent(request, options = {}) {
|
|
1721
1724
|
startRulesRefresh(options);
|
|
1722
1725
|
try {
|
|
@@ -1744,6 +1747,7 @@ function startRulesRefresh(options) {
|
|
|
1744
1747
|
}
|
|
1745
1748
|
}
|
|
1746
1749
|
function recordAgentVisit(request, options = {}) {
|
|
1750
|
+
used.add("recordAgentVisit");
|
|
1747
1751
|
try {
|
|
1748
1752
|
const key = readKey(options.key);
|
|
1749
1753
|
if (!key) {
|
|
@@ -1779,7 +1783,8 @@ function recordAgentVisit(request, options = {}) {
|
|
|
1779
1783
|
path: url.pathname + url.search,
|
|
1780
1784
|
headers,
|
|
1781
1785
|
sourceEventId: crypto.randomUUID(),
|
|
1782
|
-
relayVersion: RELAY_VERSION
|
|
1786
|
+
relayVersion: RELAY_VERSION,
|
|
1787
|
+
uses: []
|
|
1783
1788
|
};
|
|
1784
1789
|
if (capped["user-agent"] !== void 0 && !omitted.has("user-agent")) {
|
|
1785
1790
|
record.userAgent = capped["user-agent"];
|
|
@@ -1793,6 +1798,8 @@ function recordAgentVisit(request, options = {}) {
|
|
|
1793
1798
|
const policy = platform === "cloudfront" ? { timeoutMs: settings.cloudFrontTimeoutMs, retry: false } : { timeoutMs: settings.requestTimeoutMs, retry: true };
|
|
1794
1799
|
const ingestUrl = `${apiUrl}/api/v2/agent-traffic/ingest/middleware/${key.ingestToken}`;
|
|
1795
1800
|
const withMatchKey = async () => {
|
|
1801
|
+
await Promise.resolve();
|
|
1802
|
+
record.uses = [...used];
|
|
1796
1803
|
if (ip !== null && !omitted.has("matchkey")) {
|
|
1797
1804
|
record.matchKey = await deriveMatchKey(key.orgKey, ip).catch(
|
|
1798
1805
|
() => void 0
|
|
@@ -1819,6 +1826,7 @@ var BLOCKED = {
|
|
|
1819
1826
|
}
|
|
1820
1827
|
};
|
|
1821
1828
|
async function serveAgentPage(request, options = {}) {
|
|
1829
|
+
used.add("serveAgentPage");
|
|
1822
1830
|
try {
|
|
1823
1831
|
return isCloudFrontRequest(request) ? await serveCloudFront(request, options) : await serveFetch(request, options);
|
|
1824
1832
|
} catch {
|
|
@@ -2003,6 +2011,7 @@ function takeMarkFromUri(request) {
|
|
|
2003
2011
|
}
|
|
2004
2012
|
}
|
|
2005
2013
|
async function handleViewerRequest(request, options = {}) {
|
|
2014
|
+
used.add("handleViewerRequest");
|
|
2006
2015
|
try {
|
|
2007
2016
|
stripForgedMark(request);
|
|
2008
2017
|
const method = request.method.toUpperCase();
|
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\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 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 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};\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\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\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\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,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;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,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;AAAA,QACA;AAAA,QACA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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",
|