@profullstack/x402-gateway 0.10.0 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/index.d.ts +2 -1
- package/package.json +1 -1
- package/src/index.js +13 -3
package/README.md
CHANGED
|
@@ -95,7 +95,7 @@ export const GET = robotsRoute(gateway, { disallow: ['/login', '/api/'] });
|
|
|
95
95
|
| `aiIndex` | none | extra agents to charge and refuse in robots.txt. Since 0.10 the AI answer-engine indexers (`AI_INDEX_AGENTS`: meta-webindexer, YouBot, ExaSearchBot, AionBot) are welcome retrieval crawlers; pass `aiIndex: AI_INDEX_AGENTS` to charge them anyway |
|
|
96
96
|
| `verifiedRetrieval` | `VERIFIED_RETRIEVAL` | retrieval crawlers believed only from their operator's published addresses (OAI-SearchBot, ChatGPT-User, PerplexityBot, Perplexity-User); from elsewhere they are metered. Other retrieval and AI chat fetchers (Claude-User, Claude-SearchBot, Meta-ExternalFetcher, DuckAssistBot, MistralAI-User, the AI indexers) publish no addresses and are believed by name |
|
|
97
97
|
| `chargeImpostors` | `true` | a request wearing a `welcome` crawler's name (Googlebot, bingbot, Applebot, DuckDuckBot, Cloudflare-*) from an address outside that operator's published list is charged. Never judged without a readable address |
|
|
98
|
-
| `challenge` | off | `{ paths: ['/i/'], cookie?: 'pf_js' }`: every GET to these paths must run a one-line script (sets a per-address, per-day HMAC cookie and reloads) or gets a 402. Welcome crawlers (address-verified search crawlers, retrieval crawlers, AI chat fetchers) skip it; a client that did not ask for HTML gets the JSON offer instead of the script; sessions and passes never see it. For residential swarms that wear a current browser string and never run the page |
|
|
98
|
+
| `challenge` | off | `{ paths: ['/i/'], cookie?: 'pf_js', when?: (request, url) => boolean }`: every GET to these paths (that `when` accepts, if given) must run a one-line script (sets a per-address, per-day HMAC cookie and reloads) or gets a 402. Welcome crawlers (address-verified search crawlers, retrieval crawlers, AI chat fetchers) skip it; a client that did not ask for HTML gets the JSON offer instead of the script; sessions and passes never see it. For residential swarms that wear a current browser string and never run the page |
|
|
99
99
|
| `chargeStaleBrowsers` | `true` | charge a desktop Chrome/Edge/Firefox string about two years behind its auto-updates. Scraper rotation pools copy old strings; people's browsers update themselves. Phones, Firefox ESRs and the last version an old OS can run are never judged |
|
|
100
100
|
| `welcome` | `WELCOME_CRAWLERS` | Googlebot, bingbot and Applebot read unmetered when their address is in the operator's published range. A copied string from anywhere else is metered like everyone; `false` turns this off |
|
|
101
101
|
| `training`, `retrieval` | the lists in `./agents` | |
|
package/index.d.ts
CHANGED
|
@@ -77,7 +77,8 @@ export interface GatewayOptions {
|
|
|
77
77
|
/** Charge a request wearing a welcome search crawler's name from outside its published ranges. Default true. */
|
|
78
78
|
chargeImpostors?: boolean;
|
|
79
79
|
/** Require a one-line script (cookie + reload) on these paths before serving them. Off by default. */
|
|
80
|
-
|
|
80
|
+
/** Only GETs to `paths` that `when` accepts (default: all of them), e.g. `(req, url) => url.search.length > 1` for faceted URLs. */
|
|
81
|
+
challenge?: { paths: string[]; cookie?: string; when?: (request: Request, url: URL) => boolean } | false;
|
|
81
82
|
/** SEO resellers: charged, and refused in robots.txt. Default SCRAPER_AGENTS. */
|
|
82
83
|
scrapers?: string[];
|
|
83
84
|
/** Headless browsers that name themselves: charged. Default HEADLESS_AGENTS. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@profullstack/x402-gateway",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Sell crawl access to AI training crawlers by the day over x402, settled by CoinPay. One middleware: 402 with an offer, a sales page with CLI instructions, signed passes, Profullstack Power Keys honoured as a pass, and a robots.txt that keeps search crawlers welcome.",
|
|
6
6
|
"keywords": [
|
package/src/index.js
CHANGED
|
@@ -83,7 +83,7 @@ export { isPowerKey, keyFrom, PROFULLSTACK_KEYS, verifyKey } from '@profullstack
|
|
|
83
83
|
* @param {string[]} [options.aiIndex] extra agents to charge and refuse in robots.txt, e.g. AI_INDEX_AGENTS; default none (they are welcome retrieval crawlers since 0.10)
|
|
84
84
|
* @param {{token: string, cidrs: string[]}[]|false} [options.verifiedRetrieval] retrieval crawlers believed only from their published addresses; default VERIFIED_RETRIEVAL
|
|
85
85
|
* @param {boolean} [options.chargeImpostors=true] charge a request wearing a welcome search crawler's name from outside its published ranges
|
|
86
|
-
* @param {{paths: string[], cookie?: string}|false} [options.challenge]
|
|
86
|
+
* @param {{paths: string[], cookie?: string, when?: (request: Request, url: URL) => boolean}|false} [options.challenge] requests to these paths (and, with `when`, only those it accepts, e.g. faceted URLs) must run a one-line script (cookie + reload) first; off by default
|
|
87
87
|
* @param {{token: string, cidrs: string[]}[]|false} [options.welcome] search crawlers never metered when name and address agree; default WELCOME_CRAWLERS
|
|
88
88
|
* @param {(request: Request) => boolean} [options.exempt] requests never charged, e.g. ones carrying a signed-in cookie
|
|
89
89
|
* @param {string} [options.secret] pass signing secret; defaults to the CoinPay key
|
|
@@ -460,7 +460,13 @@ export function createGateway(options = {}) {
|
|
|
460
460
|
* that asked for HTML gets the one-line script; anything else gets the
|
|
461
461
|
* JSON offer, since it was never going to run a script.
|
|
462
462
|
*/
|
|
463
|
-
if (
|
|
463
|
+
if (
|
|
464
|
+
o.challenge &&
|
|
465
|
+
secret &&
|
|
466
|
+
meters(o.challenge, path) &&
|
|
467
|
+
(!o.challenge.when || o.challenge.when(request, new URL(request.url))) &&
|
|
468
|
+
(request.method === 'GET' || request.method === 'HEAD')
|
|
469
|
+
) {
|
|
464
470
|
const ip = clientIp(request);
|
|
465
471
|
if (ip && !(await passedChallenge(request, { secret, cookie: o.challenge.cookie, ip }))) {
|
|
466
472
|
if (!wantsHtml(request.headers.get('accept'))) {
|
|
@@ -622,7 +628,11 @@ function normalise(options) {
|
|
|
622
628
|
aiIndex,
|
|
623
629
|
challenge:
|
|
624
630
|
options.challenge && Array.isArray(options.challenge.paths) && options.challenge.paths.length
|
|
625
|
-
? {
|
|
631
|
+
? {
|
|
632
|
+
paths: options.challenge.paths,
|
|
633
|
+
cookie: options.challenge.cookie ?? 'pf_js',
|
|
634
|
+
when: typeof options.challenge.when === 'function' ? options.challenge.when : null,
|
|
635
|
+
}
|
|
626
636
|
: null,
|
|
627
637
|
welcome: options.welcome === false ? null : (options.welcome ?? WELCOME_CRAWLERS),
|
|
628
638
|
verifiedRetrieval: options.verifiedRetrieval === false ? null : (options.verifiedRetrieval ?? VERIFIED_RETRIEVAL),
|