x402-safe-fetch 0.11.0 → 0.11.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 +2 -2
- package/index.js +7 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -142,11 +142,11 @@ With `shareOutcomes: true`, safe-fetch tells x402 Doctor what happened after it
|
|
|
142
142
|
When safe-fetch does **not** pay (a `no_go`, or a `caution` that `onCaution` stops), it reports `refused` with the reason code that stopped it (for example `payto_changed`, `price_changed` or `payee_mismatch`). That is the seller's signal back: Doctor shows sellers how often agents refused to pay their endpoint and why (in the free web check and `GET https://x402-doctor.fizzl.eu/api/v1/refusals?url=…`), as counts only, never who.
|
|
143
143
|
|
|
144
144
|
With `tellSellers: true`, a refusal also goes straight to the seller, when its 402 names a refusal url on its own site (the draft refusal extension, [x402-foundation/x402#4087](https://github.com/x402-foundation/x402/issues/4087)). safe-fetch posts the ready report from Doctor's preflight:
|
|
145
|
-
- the reason code (for example `
|
|
145
|
+
- the reason code (for example `price_above_limit`, `network_unsupported` or `trust_low`);
|
|
146
146
|
- the sha256 of the refused offer;
|
|
147
147
|
- the endpoint without its query.
|
|
148
148
|
|
|
149
|
-
No keys, no amounts from your wallet, nothing about you. It goes only to the endpoint's own site, with redirects refused, at most once a day per endpoint and reason, and never delays the error you get. It works with or without `shareOutcomes`.
|
|
149
|
+
No keys, no amounts from your wallet, nothing about you, and no `by` naming safe-fetch, so buyers aren't grouped by the client they use. A `screening_hit` (sanctioned payout address) isn't sent: the draft (0.2) takes it only signed, and safe-fetch holds no key to sign with. It goes only to the endpoint's own site, with redirects refused, at most once a day per endpoint and reason, and never delays the error you get. It works with or without `shareOutcomes`.
|
|
150
150
|
|
|
151
151
|
## Signed verdicts
|
|
152
152
|
|
package/index.js
CHANGED
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
// or response content are sent.
|
|
23
23
|
// With tellSellers, a refusal also goes to the seller itself when its 402 names a refusal url on its own site (the
|
|
24
24
|
// draft refusal extension, x402-foundation/x402#4087): the ready report from Doctor's preflight (reason code and the
|
|
25
|
-
// hash of the refused offer; no query, no keys, nothing about the buyer), once a day per endpoint and
|
|
26
|
-
// default.
|
|
25
|
+
// hash of the refused offer; no query, no keys, nothing about the buyer or this client), once a day per endpoint and
|
|
26
|
+
// reason. A screening_hit isn't sent: the draft takes it only signed. Off by default.
|
|
27
27
|
// Every preflight must carry Doctor's signed receipt for exactly this request
|
|
28
28
|
// (endpoint, method, budget, network), checked against Doctor's pinned signer:
|
|
29
29
|
// a missing, changed or forged verdict is never a payment (receipt.js).
|
|
@@ -41,7 +41,7 @@ export { verifyReceipt, recoverSigner, canonicalJson, inputHash, certMessage, DO
|
|
|
41
41
|
export const DOCTOR_URL = "https://x402-doctor.fizzl.eu";
|
|
42
42
|
export const PREFLIGHT_CAP = "$0.002"; // the preflight costs $0.001; never more than twice that
|
|
43
43
|
export const DIAGNOSE_CAP = "$0.02"; // the diagnosis costs $0.01; never more than twice that
|
|
44
|
-
export const VERSION = "0.11.
|
|
44
|
+
export const VERSION = "0.11.1";
|
|
45
45
|
// Doctor sees which calls come from this package (in its usage counts), nothing more.
|
|
46
46
|
const DOCTOR_HEADERS = { accept: "application/json", "user-agent": `x402-safe-fetch/${VERSION}` };
|
|
47
47
|
export const BASE = "eip155:8453";
|
|
@@ -391,6 +391,8 @@ export function createSafeFetch({
|
|
|
391
391
|
if (target.protocol !== "https:" && !["localhost", "127.0.0.1"].includes(target.hostname)) return;
|
|
392
392
|
if (!sameSite(target.hostname, endpoint.hostname)) return;
|
|
393
393
|
if (refusal.report.resource !== `${endpoint.origin}${endpoint.pathname}`) return;
|
|
394
|
+
// The draft (0.2) takes a screening_hit only signed, and this client holds no key to sign with.
|
|
395
|
+
if (refusal.signature_required || (refusal.report.reason === "screening_hit" && !refusal.report.signature)) return;
|
|
394
396
|
const key = `${refusal.report.resource} ${refusal.report.reason}`;
|
|
395
397
|
if (now() - (toldSellers.get(key) ?? -Infinity) < 24 * 60 * 60 * 1000) return;
|
|
396
398
|
toldSellers.set(key, now());
|
|
@@ -399,7 +401,8 @@ export function createSafeFetch({
|
|
|
399
401
|
.then(() => baseFetch(target.href, {
|
|
400
402
|
method: "POST",
|
|
401
403
|
headers: { "content-type": "application/json" },
|
|
402
|
-
|
|
404
|
+
// No "by": buyers aren't grouped by the client they use.
|
|
405
|
+
body: JSON.stringify(refusal.report),
|
|
403
406
|
redirect: "error",
|
|
404
407
|
}))
|
|
405
408
|
.then((r) => r?.body?.cancel?.())
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "x402-safe-fetch",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "A fetch for AI agents that checks an unknown x402 endpoint ($0.001 x402 Doctor preflight) before paying it, and never pays above your budget.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|