twzrd-x402-gate 0.8.19 → 0.9.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/QUICKSTART.md +20 -11
- package/README.md +195 -42
- package/dist/cloudflare-base.d.ts +49 -0
- package/dist/cloudflare-base.d.ts.map +1 -0
- package/dist/cloudflare-base.js +113 -0
- package/dist/cloudflare-base.js.map +1 -0
- package/dist/cloudflare-x402.d.ts +29 -0
- package/dist/cloudflare-x402.d.ts.map +1 -0
- package/dist/cloudflare-x402.js +31 -0
- package/dist/cloudflare-x402.js.map +1 -0
- package/dist/index.d.ts +12 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -2
- package/dist/index.js.map +1 -1
- package/dist/mpp-hook.d.ts +39 -12
- package/dist/mpp-hook.d.ts.map +1 -1
- package/dist/mpp-hook.js +76 -13
- package/dist/mpp-hook.js.map +1 -1
- package/dist/paying-fetch.d.ts +15 -0
- package/dist/paying-fetch.d.ts.map +1 -0
- package/dist/paying-fetch.js +67 -0
- package/dist/paying-fetch.js.map +1 -0
- package/dist/policy-fetch.d.ts +18 -0
- package/dist/policy-fetch.d.ts.map +1 -0
- package/dist/policy-fetch.js +77 -0
- package/dist/policy-fetch.js.map +1 -0
- package/dist/policy-runtime.d.ts +2 -0
- package/dist/policy-runtime.d.ts.map +1 -1
- package/dist/policy-runtime.js +14 -0
- package/dist/policy-runtime.js.map +1 -1
- package/dist/resource-bind-tx.d.ts +27 -0
- package/dist/resource-bind-tx.d.ts.map +1 -0
- package/dist/resource-bind-tx.js +118 -0
- package/dist/resource-bind-tx.js.map +1 -0
- package/dist/resource-bind.d.ts +48 -0
- package/dist/resource-bind.d.ts.map +1 -0
- package/dist/resource-bind.js +203 -0
- package/dist/resource-bind.js.map +1 -0
- package/dist/spend-control.d.ts +39 -0
- package/dist/spend-control.d.ts.map +1 -0
- package/dist/spend-control.js +137 -0
- package/dist/spend-control.js.map +1 -0
- package/dist/spend-ledger-file.d.ts +3 -0
- package/dist/spend-ledger-file.d.ts.map +1 -0
- package/dist/spend-ledger-file.js +66 -0
- package/dist/spend-ledger-file.js.map +1 -0
- package/dist/wash-default.d.ts +106 -0
- package/dist/wash-default.d.ts.map +1 -0
- package/dist/wash-default.js +223 -0
- package/dist/wash-default.js.map +1 -0
- package/dist/x402-client-hook.d.ts +30 -14
- package/dist/x402-client-hook.d.ts.map +1 -1
- package/dist/x402-client-hook.js +80 -16
- package/dist/x402-client-hook.js.map +1 -1
- package/package.json +12 -3
package/QUICKSTART.md
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
# twzrd-x402-gate — Quickstart (15 minutes, 0 USDC)
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
preflight blocks the payment by default (`TWZRD_FAIL_OPEN=true` to allow).
|
|
3
|
+
**Product:** replace naked `createX402Client({ wallet })` with a client that
|
|
4
|
+
**refuses wash before sign**. Free `merchant_card`, no API key. Intel down →
|
|
5
|
+
**fail-open** (never invents wash). No Path A in the default path.
|
|
7
6
|
|
|
8
|
-
**Pin:** `twzrd-x402-gate@0.
|
|
9
|
-
`beforePayment` seat). `@x402/core` Path E remains supported; refuse script is fallback.
|
|
7
|
+
**Pin:** `twzrd-x402-gate@0.9.0` + `x402-solana@3.0.0`.
|
|
10
8
|
|
|
11
9
|
## 1. Stock PayAI client (default seat — copy-paste)
|
|
12
10
|
|
|
13
11
|
```bash
|
|
14
|
-
npm install twzrd-x402-gate@0.
|
|
12
|
+
npm install twzrd-x402-gate@0.9.0 x402-solana@3.0.0
|
|
15
13
|
```
|
|
16
14
|
|
|
17
15
|
> **ESM-only.** The package ships `import` conditions only — a CommonJS
|
|
@@ -19,6 +17,16 @@ npm install twzrd-x402-gate@0.8.19 x402-solana@2.1.0
|
|
|
19
17
|
> fails with `ERR_PACKAGE_PATH_NOT_EXPORTED`. Set `"type": "module"` in your
|
|
20
18
|
> package.json, use `.mjs`, or bundle ESM.
|
|
21
19
|
|
|
20
|
+
```typescript
|
|
21
|
+
import { createX402Client } from "x402-solana";
|
|
22
|
+
import { createTwzrdPayingClient } from "twzrd-x402-gate";
|
|
23
|
+
|
|
24
|
+
// was: createX402Client({ wallet })
|
|
25
|
+
const client = createX402Client(createTwzrdPayingClient({ wallet }));
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Or hook-only:
|
|
29
|
+
|
|
22
30
|
```typescript
|
|
23
31
|
import { createX402Client } from "x402-solana";
|
|
24
32
|
import { createTwzrdBeforePaymentHook } from "twzrd-x402-gate";
|
|
@@ -26,17 +34,18 @@ import { createTwzrdBeforePaymentHook } from "twzrd-x402-gate";
|
|
|
26
34
|
const client = createX402Client({
|
|
27
35
|
wallet,
|
|
28
36
|
network: "solana",
|
|
29
|
-
beforePayment: createTwzrdBeforePaymentHook(
|
|
37
|
+
beforePayment: createTwzrdBeforePaymentHook(),
|
|
30
38
|
});
|
|
31
39
|
```
|
|
32
40
|
|
|
33
|
-
|
|
41
|
+
Default engine = wash only. Full 0.8 preflight/Path A: `{ engine: "full" }`.
|
|
42
|
+
|
|
34
43
|
Then: `await client.fetch("https://merchant.example/paid");`
|
|
35
44
|
|
|
36
45
|
## 1b. Official `@x402/core` client (Path E)
|
|
37
46
|
|
|
38
47
|
```bash
|
|
39
|
-
npm install twzrd-x402-gate@0.
|
|
48
|
+
npm install twzrd-x402-gate@0.9.0 @x402/core @x402/fetch @x402/svm
|
|
40
49
|
```
|
|
41
50
|
|
|
42
51
|
```typescript
|
|
@@ -53,7 +62,7 @@ installTwzrdAutoGate(client, { refuseWashFlagged: true });
|
|
|
53
62
|
|
|
54
63
|
```bash
|
|
55
64
|
# Stock-client seat proof (beforePayment, signer_invocation_count=0)
|
|
56
|
-
# needs x402-solana@
|
|
65
|
+
# needs x402-solana@3.0.0 installed - the harness fallback runs but does not
|
|
57
66
|
# clear closure (its negative arm counts a would-be sign)
|
|
58
67
|
npm run x402-solana-before-payment-proof
|
|
59
68
|
# writes block-proof-<run_id>.json — hook: beforePayment
|
package/README.md
CHANGED
|
@@ -1,18 +1,72 @@
|
|
|
1
1
|
# twzrd-x402-gate
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Product: the paying client that already has the brake.**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
**
|
|
7
|
-
|
|
8
|
-
**Solana-deep** reputation only (Base/EVM = explicit `unknown`).
|
|
5
|
+
Agents pay other machines unsupervised. Most 402s are junk or wash. Something has
|
|
6
|
+
to say **no** before the signature. TWZRD is that no — inside `beforePayment` /
|
|
7
|
+
fetch+sign, not inside PayAI’s SDK defaults, not as a shop at intel.twzrd.xyz.
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
The other reason the no must live at the signature: **attaching a funded wallet
|
|
10
|
+
to an agent that reads untrusted web content is a drain vector** — indirect
|
|
11
|
+
prompt injection can spend anything the agent can sign. The gate closes it by
|
|
12
|
+
attaching the wallet to a policy runtime the injected content cannot
|
|
13
|
+
renegotiate: per-request cap, rolling daily ceiling, wash brake, endpoint
|
|
14
|
+
mandates, and a tamper-evident spend ledger that survives restarts. A fully
|
|
15
|
+
compromised agent spends at most what policy allows, to whom it allows, with
|
|
16
|
+
every decision signed.
|
|
17
|
+
|
|
18
|
+
### Replace naked `createX402Client({ wallet })` (5 lines)
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install twzrd-x402-gate@0.9.0 x402-solana@3.0.0
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { createX402Client } from "x402-solana";
|
|
26
|
+
import { createTwzrdPayingClient } from "twzrd-x402-gate";
|
|
27
|
+
|
|
28
|
+
// was: createX402Client({ wallet })
|
|
29
|
+
const client = createX402Client(createTwzrdPayingClient({ wallet }));
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Equivalent one-hook form:
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { createX402Client } from "x402-solana";
|
|
36
|
+
import { createTwzrdBeforePaymentHook } from "twzrd-x402-gate";
|
|
37
|
+
|
|
38
|
+
const client = createX402Client({
|
|
39
|
+
wallet,
|
|
40
|
+
network: "solana",
|
|
41
|
+
beforePayment: createTwzrdBeforePaymentHook(), // wash default
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Default path (engine `"wash"`):**
|
|
46
|
+
`GET /v1/intel/merchant_card/{payTo}` → abort **iff** `wash_flagged === true` →
|
|
47
|
+
**fail-open** on timeout / non-2xx / throw (never invent wash).
|
|
48
|
+
No Path A, no requireReceipt, no second 402, no payment-control tokens.
|
|
49
|
+
|
|
50
|
+
**Full 0.8.x engine (named opt-in):** preflight + optional Path A / escalate /
|
|
51
|
+
paymentControl:
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
createTwzrdBeforePaymentHook({ engine: "full", /* … */ })
|
|
55
|
+
// or createTwzrdFullBeforePaymentHook({ … })
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Customer = whoever ships `createX402Client({ wallet })` with no hook (Eliza, MCP
|
|
59
|
+
hosts, agent wallets). Crawlers will never `npm install`.
|
|
60
|
+
|
|
61
|
+
> **ESM-only:** CommonJS `require()` fails with `ERR_PACKAGE_PATH_NOT_EXPORTED`
|
|
62
|
+
> — set `"type": "module"` (or use `.mjs` / an ESM bundler).
|
|
63
|
+
|
|
64
|
+
### Default-on AutoGate (alternate seats)
|
|
11
65
|
|
|
12
66
|
```bash
|
|
13
|
-
npm install twzrd-x402-gate@0.
|
|
67
|
+
npm install twzrd-x402-gate@0.9.0 x402-solana@3.0.0
|
|
14
68
|
# official @x402/* path (alternate seat):
|
|
15
|
-
# npm install twzrd-x402-gate@0.
|
|
69
|
+
# npm install twzrd-x402-gate@0.9.0 @x402/core @x402/fetch @x402/svm
|
|
16
70
|
```
|
|
17
71
|
|
|
18
72
|
```typescript
|
|
@@ -20,7 +74,7 @@ import { x402Client } from "@x402/core/client";
|
|
|
20
74
|
import { installTwzrdAutoGate } from "twzrd-x402-gate";
|
|
21
75
|
|
|
22
76
|
const client = new x402Client();
|
|
23
|
-
//
|
|
77
|
+
// stock solana seat still defaults to wash via createTwzrdBeforePaymentHook()
|
|
24
78
|
installTwzrdAutoGate(client, { refuseWashFlagged: true });
|
|
25
79
|
// then register schemes + wrapFetchWithPayment as usual
|
|
26
80
|
```
|
|
@@ -35,16 +89,13 @@ cd packages/twzrd-x402-gate && npm run autogate-block-proof
|
|
|
35
89
|
# fixture resolves live: wash_flagged seller if available, else decision=block
|
|
36
90
|
```
|
|
37
91
|
|
|
38
|
-
|
|
39
|
-
> — set `"type": "module"` (or use `.mjs` / an ESM bundler).
|
|
92
|
+
`gateOnCanSpend` remains **opt-in** (`false` by default; set `true` or `TWZRD_GATE_ON_CAN_SPEND=1` only when you want hard cap enforcement). Full-engine preflight outage default stays fail-closed unless `failOpen: true` / `TWZRD_FAIL_OPEN=1`. The **product wash path always fail-opens** on intel outage.
|
|
40
93
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
**Optional (0.8.1):** a resource-server settle hook so merchants can apply **customer policy**
|
|
94
|
+
**Optional:** a resource-server settle hook so merchants can apply **customer policy**
|
|
44
95
|
before they settle and serve (abuse, sanctions, bots, “don’t serve this payer”). Not an equal
|
|
45
96
|
mirror of the buyer problem — settled USDC is final; wash resistance is mainly TWZRD scoring.
|
|
46
97
|
|
|
47
|
-
## Seller settle guard (`onBeforeSettle`) — optional
|
|
98
|
+
## Seller settle guard (`onBeforeSettle`) — optional
|
|
48
99
|
|
|
49
100
|
Resource servers can screen the **payer** before *they* settle an inbound payment and serve
|
|
50
101
|
the resource. Use for merchant policy (abuse / sanctions / bots / customer selection). TWZRD is
|
|
@@ -56,7 +107,7 @@ transfer on-chain. Wash/sybil edges are primarily discounted in TWZRD scoring, n
|
|
|
56
107
|
revenue refusal.
|
|
57
108
|
|
|
58
109
|
```bash
|
|
59
|
-
npm install twzrd-x402-gate@0.
|
|
110
|
+
npm install twzrd-x402-gate@0.9.0
|
|
60
111
|
```
|
|
61
112
|
|
|
62
113
|
```typescript
|
|
@@ -89,24 +140,22 @@ Fixture-backed SVM extract tests live in `test/seller-hook.test.ts` +
|
|
|
89
140
|
`test/fixtures/exact-svm-transfer-checked.ts`.
|
|
90
141
|
|
|
91
142
|
**PayAI x402-solana@2.1.0+** (stock client - primary Solana seat via `beforePayment`).
|
|
92
|
-
|
|
93
|
-
|
|
143
|
+
Both 2.1.0 and 3.0.0 are supported and produce an identical refuse transcript; 3.0.0
|
|
144
|
+
is the current release and what the install lines pin. The hook absorbs the one
|
|
145
|
+
breaking change between them (`declaredResource` is a string on 2.1.0, `{ url }` on
|
|
146
|
+
3.0.0) - see `src/x402-client-hook.ts` and `test/x402-solana-before-payment.test.ts`.
|
|
94
147
|
|
|
95
148
|
```typescript
|
|
96
149
|
import { createX402Client } from "x402-solana";
|
|
97
|
-
import {
|
|
150
|
+
import { createTwzrdPayingClient } from "twzrd-x402-gate";
|
|
98
151
|
|
|
99
|
-
const client = createX402Client({
|
|
100
|
-
wallet,
|
|
101
|
-
network: "solana",
|
|
102
|
-
beforePayment: createTwzrdBeforePaymentHook({ refuseWashFlagged: true }),
|
|
103
|
-
});
|
|
152
|
+
const client = createX402Client(createTwzrdPayingClient({ wallet }));
|
|
104
153
|
```
|
|
105
154
|
|
|
106
|
-
Also: `installTwzrdAutoGate("x402-solana", opts)` returns the same hook.
|
|
155
|
+
Also: `installTwzrdAutoGate("x402-solana", opts)` returns the same wash-default hook.
|
|
107
156
|
Prove refuse-before-sign: `npm run x402-solana-before-payment-proof`.
|
|
108
157
|
|
|
109
|
-
**PayAI agentic-payments** (alternate PayAI surface; prefer x402-solana@
|
|
158
|
+
**PayAI agentic-payments** (alternate PayAI surface; prefer x402-solana@3.0.0 when available):
|
|
110
159
|
`npx tsx examples/payai-agentic-onPaymentVerified.ts` — wire `onPaymentVerified`
|
|
111
160
|
→ `toPayaiVerifyResult` to screen payers before serving. Fail-open by default.
|
|
112
161
|
|
|
@@ -118,7 +167,7 @@ Prove refuse-before-sign: `npm run x402-solana-before-payment-proof`.
|
|
|
118
167
|
Install the published gate and run against wash fixtures:
|
|
119
168
|
|
|
120
169
|
```bash
|
|
121
|
-
npm install twzrd-x402-gate@0.
|
|
170
|
+
npm install twzrd-x402-gate@0.9.0
|
|
122
171
|
# from package root after install, or from a checkout:
|
|
123
172
|
npm run wash-dogfood
|
|
124
173
|
```
|
|
@@ -138,7 +187,7 @@ Gate dogfood: `approved=false` `reason=twzrd_decision_block`, **USDC spent = 0**
|
|
|
138
187
|
|
|
139
188
|
**Accurate paste line:**
|
|
140
189
|
|
|
141
|
-
> Preflight returned decision=block on wash seller 7G73PL… / HuSiSpc… (preflight_id 378468 / 378469, wash_flagged=true). Gate approved=false reason=twzrd_decision_block. No USDC spent. No tx broadcast. Repro: `npm i twzrd-x402-gate@0.
|
|
190
|
+
> Preflight returned decision=block on wash seller 7G73PL… / HuSiSpc… (preflight_id 378468 / 378469, wash_flagged=true). Gate approved=false reason=twzrd_decision_block. No USDC spent. No tx broadcast. Repro: `npm i twzrd-x402-gate@0.9.0 && npm run wash-dogfood` or gist above.
|
|
142
191
|
|
|
143
192
|
This is a **reproducible demonstration** that the free gate blocks known wash sellers with stamped `preflight_id`s and zero spend. It is **not** proof that external agents already default to this path at scale.
|
|
144
193
|
|
|
@@ -216,6 +265,56 @@ No AgentCash. No marketplace. No second probe. No TWZRD custody.
|
|
|
216
265
|
Wire `twzrdOnPaymentRequested` / prefer `onPaymentRequired` + `onBeforePayment` per
|
|
217
266
|
[lifecycle hooks](https://docs.x402.org/advanced-concepts/lifecycle-hooks). Same policy core.
|
|
218
267
|
|
|
268
|
+
### Cloudflare Agents x402 (Base)
|
|
269
|
+
|
|
270
|
+
Cloudflare's `withX402Client` takes an `onPaymentRequired` callback before its
|
|
271
|
+
automatic payment retry. Use the adapter below at that boundary:
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
import { createTwzrdCloudflareX402Approval } from "twzrd-x402-gate";
|
|
275
|
+
|
|
276
|
+
const approvePayment = createTwzrdCloudflareX402Approval({
|
|
277
|
+
// Base/EVM has no TWZRD behavioral reputation yet. Refuse it rather than
|
|
278
|
+
// presenting a policy allow as a trust verdict.
|
|
279
|
+
unsupportedNetworkMode: "strict",
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
await this.x402Client.callTool(approvePayment, {
|
|
283
|
+
name: "paid_tool",
|
|
284
|
+
arguments: {},
|
|
285
|
+
});
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
`observe` is available for an agent that intentionally permits unscored Base
|
|
289
|
+
payments, but it returns policy allow with `decision=unknown`; it is never a
|
|
290
|
+
TWZRD reputation approval. This adapter protects the Cloudflare x402 path only.
|
|
291
|
+
MPP is a separate protocol and belongs on its own `onChallenge` /
|
|
292
|
+
`onPaymentRequired` control path.
|
|
293
|
+
|
|
294
|
+
### Cloudflare Worker / Base x402 (edge-safe preflight)
|
|
295
|
+
|
|
296
|
+
For a Worker, import the dedicated subpath rather than the package root. It has
|
|
297
|
+
no Node-native imports and sends the exact Base `payTo` plus `chain_id: 8453` to
|
|
298
|
+
`/v1/intel/preflight` before a Viem account signs:
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
import { withTwzrdBasePreflight } from "twzrd-x402-gate/cloudflare-base";
|
|
302
|
+
|
|
303
|
+
const signature = await withTwzrdBasePreflight(
|
|
304
|
+
paymentRequirements, // accepts: [{ network: "eip155:8453", payTo: "0x..." }]
|
|
305
|
+
{ intelBase: "https://intel.twzrd.xyz" },
|
|
306
|
+
() => account.signTypedData(eip3009Authorization),
|
|
307
|
+
);
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
`block` throws `TwzrdBasePaymentBlockedError` and never invokes the signing
|
|
311
|
+
callback. `allow` and `warn` both proceed; enforce any additional amount or
|
|
312
|
+
mandate policy in the caller. Base is not presented as reputation-scored.
|
|
313
|
+
Checksummed and lowercase EVM addresses are accepted unchanged. The fixture in
|
|
314
|
+
[`test/cloudflare-base-edge.test.ts`](./test/cloudflare-base-edge.test.ts) proves
|
|
315
|
+
`block → zero signTypedData calls` and the compiled subpath is checked for Node
|
|
316
|
+
runtime leaks.
|
|
317
|
+
|
|
219
318
|
### Raw-fetch composition (injectible pay client only)
|
|
220
319
|
|
|
221
320
|
```typescript
|
|
@@ -263,7 +362,7 @@ Dogfood (one public live proof path):
|
|
|
263
362
|
## Install
|
|
264
363
|
|
|
265
364
|
```bash
|
|
266
|
-
npm install twzrd-x402-gate@0.
|
|
365
|
+
npm install twzrd-x402-gate@0.9.0 x402-solana@3.0.0
|
|
267
366
|
```
|
|
268
367
|
|
|
269
368
|
Install pin is the published version (`GATE_PACKAGE_PIN` in
|
|
@@ -363,15 +462,16 @@ installTwzrdX402ClientHook(client, {
|
|
|
363
462
|
- Hook binding test:
|
|
364
463
|
[`test/intent-binding.test.ts`](./test/intent-binding.test.ts).
|
|
365
464
|
|
|
366
|
-
### On MPP (Machine Payments Protocol) — Solana charge
|
|
465
|
+
### On MPP (Machine Payments Protocol) — Solana and narrow Base EVM charge
|
|
367
466
|
|
|
368
|
-
`createTwzrdMppOnChallenge` guards `Mppx.create({ onChallenge })`.
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
**`createCredential()` never runs and
|
|
373
|
-
creates the credential for the exact
|
|
374
|
-
|
|
467
|
+
`createTwzrdMppOnChallenge` guards `Mppx.create({ onChallenge })`. It supports
|
|
468
|
+
`solana/charge` and the published native MPP `evm/charge` shape only for
|
|
469
|
+
Base-mainnet native USDC (EIP-3009 authorization). `onChallenge` is the last
|
|
470
|
+
deterministic checkpoint before `createCredential()` makes the credential, so a
|
|
471
|
+
TWZRD block is an exception that means **`createCredential()` never runs and
|
|
472
|
+
nothing signs**. On allow, the guard creates the credential for the exact
|
|
473
|
+
challenge it evaluated; other methods/intents fail closed
|
|
474
|
+
(`allowUnevaluated: true` is an explicit ungated opt-out).
|
|
375
475
|
|
|
376
476
|
```typescript
|
|
377
477
|
import { Mppx } from "mppx/client";
|
|
@@ -387,6 +487,30 @@ const mppx = Mppx.create({
|
|
|
387
487
|
});
|
|
388
488
|
```
|
|
389
489
|
|
|
490
|
+
For Base, use mppx's EVM client with explicit local spend policy:
|
|
491
|
+
|
|
492
|
+
```typescript
|
|
493
|
+
import { Mppx } from "mppx/client";
|
|
494
|
+
import { evm } from "mppx/evm/client";
|
|
495
|
+
import { createTwzrdMppOnChallenge, createLocalDecisionSigner } from "twzrd-x402-gate";
|
|
496
|
+
|
|
497
|
+
const mppx = Mppx.create({
|
|
498
|
+
methods: [evm({ account: wallet })],
|
|
499
|
+
onChallenge: createTwzrdMppOnChallenge({
|
|
500
|
+
signer: createLocalDecisionSigner(),
|
|
501
|
+
policy: {
|
|
502
|
+
allowedNetworks: ["eip155:8453"],
|
|
503
|
+
allowedAssets: ["0x833589fCD6EDb6E08f4c7C32D4f71b54bdA02913"],
|
|
504
|
+
maxAmountUsd: "1.00",
|
|
505
|
+
},
|
|
506
|
+
}),
|
|
507
|
+
});
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
Base is **not reputation-scored**. The EVM path provides an actual pre-credential
|
|
511
|
+
local control point for Base USDC; it does not call a Solana-derived score or
|
|
512
|
+
present a local policy allow as a TWZRD trust verdict.
|
|
513
|
+
|
|
390
514
|
**Scope limit (honest):** the guard is authoritative only when no
|
|
391
515
|
`onChallengeReceived` event handler supplies a credential. mppx resolves
|
|
392
516
|
`eventCredential ?? onChallenge(...)`, so an event handler returning a credential
|
|
@@ -402,6 +526,8 @@ fails closed rather than approve a payment it cannot bind:
|
|
|
402
526
|
| Non-USD-pegged asset | `UNPRICED_ASSET` | Policy ceilings are USD; the wire amount is base-unit tokens. Pricing SOL would store `asset: solana:native` beside a **dollar** `amount` and lose the token quantity actually transferred. (A 1.5 SOL charge of `1500000000` at 9 decimals would otherwise evaluate as "$1.50" and sail under a $5 ceiling while moving ~$270.) USDC/USDT only until an intent version carries token amount + quote. |
|
|
403
527
|
| Unknown cluster | `UNKNOWN_CLUSTER` | mppx-solana's `resolveEndpoint` returns an unrecognized `cluster` **verbatim as the RPC endpoint URL**, and a network string containing "solana" is otherwise scored as mainnet - so `solana:https://seller-rpc.example` would inherit mainnet reputation for a chain never observed. Known cluster names only. |
|
|
404
528
|
| Misdeclared decimals | `MALFORMED_CHALLENGE` | A known stablecoin declaring the wrong decimals is a discount attempt, not a rounding error. |
|
|
529
|
+
| Non-Base EVM chain | `UNSUPPORTED_EVM_NETWORK` | EVM MPP is narrow: Base mainnet only. A testnet or another EVM network cannot inherit a Base USDC valuation. |
|
|
530
|
+
| EVM split recipients | `MULTI_LEG_CHARGE` | mppx EVM permits `splits`; each is an additional recipient. PaymentIntent v1 binds one amount to one payTo, so the guard refuses rather than approve transfers it cannot represent. |
|
|
405
531
|
|
|
406
532
|
The intent binds a **digest of the entire normalized challenge**, not just
|
|
407
533
|
`realm:id` - swapping `recipient` or `amount` under the same challenge id changes
|
|
@@ -790,17 +916,31 @@ const gatedFetch = wrapFetchWithTwzrdGate(fetch, resolveConfig());
|
|
|
790
916
|
|
|
791
917
|
A payment is **blocked** when:
|
|
792
918
|
1. `decision ∈ blockDecisions` (default: `["block"]`)
|
|
793
|
-
2. `trust_score < preflightMinScore` (default: `40`)
|
|
919
|
+
2. `trust_score < preflightMinScore` (default: `40`) — note a card with **no**
|
|
920
|
+
`trust_score` is scored `0` (`policy.ts`: `card.trust_score ?? 0`), so a
|
|
921
|
+
missing score blocks under the default floor
|
|
794
922
|
3. `can_spend === false` — **only** when `gateOnCanSpend: true` (default `false`, opt-in)
|
|
923
|
+
4. `merchant_card.wash_flagged === true` — **on by default**
|
|
924
|
+
(`refuseWashFlagged`, `TWZRD_REFUSE_WASH_FLAGGED`); forces `verdict: "block"`
|
|
925
|
+
with `reason: twzrd_wash_flagged`. Only tightens, and fails **open** when the
|
|
926
|
+
card is unreachable or `wash_flagged` is `null` (never evaluated ≠ clean).
|
|
927
|
+
Soft-cap instead of hard refuse with `washMaxUsdc` / `TWZRD_WASH_MAX_USDC`.
|
|
795
928
|
|
|
796
929
|
`warn` is allowed unless overridden. Preflight network failure **fails closed** by default (a preflight outage blocks the payment, so an intel hiccup never silently approves a spend); set `failOpen: true` / `TWZRD_FAIL_OPEN=true` to opt into legacy allow-on-outage.
|
|
797
930
|
|
|
798
931
|
A 402 whose payment requirements yield **no identifiable seller wallet** (missing/empty `payTo`, or an unparseable `accepts[]`) is a different case from "unknown seller" — it always **blocks** with `reason: twzrd_unidentifiable_payment_recipient`, without ever calling the preflight network. This is unconditional (not affected by `failOpen`): `failOpen` governs what happens when the TWZRD *service* is unreachable, not what happens when the caller can't say who they're paying.
|
|
799
932
|
|
|
800
933
|
> **`can_spend` note:** the free preflight returns `can_spend=false` for most sellers
|
|
801
|
-
> not yet in the TWZRD corpus, including legitimate ones.
|
|
802
|
-
>
|
|
803
|
-
>
|
|
934
|
+
> not yet in the TWZRD corpus, including legitimate ones. That **`can_spend` leg
|
|
935
|
+
> alone** is opt-in — it is ignored unless you set `gateOnCanSpend: true` — so an
|
|
936
|
+
> unknown seller on a platform like Agentic.Market is not blocked *on that basis*.
|
|
937
|
+
>
|
|
938
|
+
> This is **not** a blanket "unknown sellers are allowed by default". Conditions 1,
|
|
939
|
+
> 2 and 4 above are all active by default, and condition 2 is the one that catches
|
|
940
|
+
> unknown sellers: a card carrying no `trust_score` evaluates as `0` and blocks
|
|
941
|
+
> under the default `preflightMinScore: 40` with `twzrd_score_0_below_40`. To
|
|
942
|
+
> actually let unscored sellers through, lower or disable `preflightMinScore` —
|
|
943
|
+
> setting `gateOnCanSpend` does not govern it.
|
|
804
944
|
|
|
805
945
|
## Config
|
|
806
946
|
|
|
@@ -811,11 +951,24 @@ A 402 whose payment requirements yield **no identifiable seller wallet** (missin
|
|
|
811
951
|
| `blockDecisions` | `TWZRD_BLOCK_DECISIONS` | `block` | Decisions that throw |
|
|
812
952
|
| `failOpen` | `TWZRD_FAIL_OPEN` | `false` | `true` opts into legacy allow-on-outage; default blocks (fail-closed) |
|
|
813
953
|
| `gateOnCanSpend` | `TWZRD_GATE_ON_CAN_SPEND` | `false` | Also block when `can_spend=false` |
|
|
954
|
+
| `refuseWashFlagged` | `TWZRD_REFUSE_WASH_FLAGGED` | **`true`** | Refuse when `merchant_card.wash_flagged === true` (Policy 4). Set `false` / `0` to opt out |
|
|
955
|
+
| `washMaxUsdc` | `TWZRD_WASH_MAX_USDC` | — | Soft cap instead of hard refuse on wash: allow up to this USDC amount |
|
|
956
|
+
| `unsupportedNetworkMode` | `TWZRD_UNSUPPORTED_NETWORK_MODE` | `observe` | Behavior on a non-Solana / unrecognized network |
|
|
957
|
+
| `fetch` | — | global `fetch` | Injected fetch used for preflight + merchant card (testing / custom agents) |
|
|
958
|
+
| `onWarnUpsell` | — | — | Callback fired on a `warn` verdict (Path A upsell hook) |
|
|
959
|
+
| `attribution` | `TWZRD_ATTRIBUTION_INTEGRATION` + `TWZRD_ATTRIBUTION_RUN_ID` | — | Opt-in run attribution (see below) |
|
|
960
|
+
|
|
961
|
+
The four options below are **not** `TwzrdGateConfig` fields — they belong to
|
|
962
|
+
`TwzrdGuardOptions` / `InstallAutoGateOptions` (`withTwzrdGuard` /
|
|
963
|
+
`installTwzrdAutoGate`). Passing them to `createTwzrdGate({...})` is a type error
|
|
964
|
+
and is silently dropped at runtime:
|
|
965
|
+
|
|
966
|
+
| Option | Env | Default | Description |
|
|
967
|
+
|---|---|---|---|
|
|
814
968
|
| `autoReceipt` | — | `false` | Auto-buy $0.05 TWZRD receipt on warn/allow |
|
|
815
969
|
| `x402Fetch` | — | — | x402-capable fetch for `autoReceipt` |
|
|
816
970
|
| `onReceipt` | — | — | Callback after receipt is captured |
|
|
817
971
|
| `disabled` (`installTwzrdAutoGate` only) | `TWZRD_AUTO_GATE=0`/`false` | `false` | Bypass the guard entirely — `payWrap` gets the raw, unguarded fetch |
|
|
818
|
-
| `attribution` | `TWZRD_ATTRIBUTION_INTEGRATION` + `TWZRD_ATTRIBUTION_RUN_ID` | — | Opt-in run attribution (see below) |
|
|
819
972
|
|
|
820
973
|
## Gate adoption proof (no-spend harness)
|
|
821
974
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edge-safe Base x402 preflight gate.
|
|
3
|
+
*
|
|
4
|
+
* This module is intentionally standalone: it imports no package code and uses
|
|
5
|
+
* only Fetch/Web-standard APIs, so Workers can import `twzrd-x402-gate/cloudflare-base`
|
|
6
|
+
* without bringing Node modules or Solana adapters into their bundle.
|
|
7
|
+
*/
|
|
8
|
+
export declare const BASE_CHAIN_ID = 8453;
|
|
9
|
+
export declare const BASE_NETWORK = "eip155:8453";
|
|
10
|
+
export type CloudflareBaseRequirements = {
|
|
11
|
+
resource?: string;
|
|
12
|
+
accepts?: Array<Record<string, unknown>>;
|
|
13
|
+
};
|
|
14
|
+
export type BasePreflightVerdict = {
|
|
15
|
+
decision: "allow" | "warn" | "block";
|
|
16
|
+
/** TWZRD's current preflight calls this trust_score; normalized here for callers. */
|
|
17
|
+
riskScore: number | null;
|
|
18
|
+
reasons: string[];
|
|
19
|
+
};
|
|
20
|
+
export type CloudflareBaseGateOptions = {
|
|
21
|
+
intelBase?: string;
|
|
22
|
+
fetch?: typeof fetch;
|
|
23
|
+
/** Explicit price context only; x402 atomic amounts are never treated as USD. */
|
|
24
|
+
priceUsdc?: number;
|
|
25
|
+
resourceName?: string;
|
|
26
|
+
agentIntent?: string;
|
|
27
|
+
/** Default false: an unavailable or malformed preflight does not permit signing. */
|
|
28
|
+
failOpen?: boolean;
|
|
29
|
+
};
|
|
30
|
+
export declare class TwzrdBasePaymentBlockedError extends Error {
|
|
31
|
+
readonly verdict: BasePreflightVerdict;
|
|
32
|
+
constructor(verdict: BasePreflightVerdict);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Calls the live TWZRD preflight with an exact Base mainnet payTo.
|
|
36
|
+
*
|
|
37
|
+
* Both lowercase and checksummed EVM addresses are valid inputs. The address is
|
|
38
|
+
* preserved as received; the endpoint evaluates the exact recipient the client
|
|
39
|
+
* is about to authorize.
|
|
40
|
+
*/
|
|
41
|
+
export declare function twzrdBasePreflight(requirements: CloudflareBaseRequirements, options?: CloudflareBaseGateOptions): Promise<BasePreflightVerdict>;
|
|
42
|
+
/** Cloudflare `withX402Client` callback: true permits the retry; false aborts it. */
|
|
43
|
+
export declare function createTwzrdCloudflareBaseApproval(options?: CloudflareBaseGateOptions): (requirements: CloudflareBaseRequirements) => Promise<boolean>;
|
|
44
|
+
/**
|
|
45
|
+
* Minimal signing interceptor for a Worker or Viem account. The callback is
|
|
46
|
+
* invoked only after the exact Base payTo received a non-block verdict.
|
|
47
|
+
*/
|
|
48
|
+
export declare function withTwzrdBasePreflight<T>(requirements: CloudflareBaseRequirements, options: CloudflareBaseGateOptions, signOrSend: () => Promise<T>): Promise<T>;
|
|
49
|
+
//# sourceMappingURL=cloudflare-base.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-base.d.ts","sourceRoot":"","sources":["../src/cloudflare-base.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,aAAa,OAAO,CAAC;AAClC,eAAO,MAAM,YAAY,gBAA4B,CAAC;AAEtD,MAAM,MAAM,0BAA0B,GAAG;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC1C,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;IACrC,qFAAqF;IACrF,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,iFAAiF;IACjF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC;gBAE3B,OAAO,EAAE,oBAAoB;CAK1C;AAED;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CACtC,YAAY,EAAE,0BAA0B,EACxC,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,oBAAoB,CAAC,CAqB/B;AAED,qFAAqF;AACrF,wBAAgB,iCAAiC,CAC/C,OAAO,GAAE,yBAA8B,GACtC,CAAC,YAAY,EAAE,0BAA0B,KAAK,OAAO,CAAC,OAAO,CAAC,CAQhE;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAAC,CAAC,EAC5C,YAAY,EAAE,0BAA0B,EACxC,OAAO,EAAE,yBAAyB,EAClC,UAAU,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAC3B,OAAO,CAAC,CAAC,CAAC,CAUZ"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Edge-safe Base x402 preflight gate.
|
|
3
|
+
*
|
|
4
|
+
* This module is intentionally standalone: it imports no package code and uses
|
|
5
|
+
* only Fetch/Web-standard APIs, so Workers can import `twzrd-x402-gate/cloudflare-base`
|
|
6
|
+
* without bringing Node modules or Solana adapters into their bundle.
|
|
7
|
+
*/
|
|
8
|
+
export const BASE_CHAIN_ID = 8453;
|
|
9
|
+
export const BASE_NETWORK = `eip155:${BASE_CHAIN_ID}`;
|
|
10
|
+
export class TwzrdBasePaymentBlockedError extends Error {
|
|
11
|
+
verdict;
|
|
12
|
+
constructor(verdict) {
|
|
13
|
+
super(`[twzrd] Base payment blocked: ${verdict.reasons.join(",") || "PREFLIGHT_BLOCK"}`);
|
|
14
|
+
this.name = "TwzrdBasePaymentBlockedError";
|
|
15
|
+
this.verdict = verdict;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Calls the live TWZRD preflight with an exact Base mainnet payTo.
|
|
20
|
+
*
|
|
21
|
+
* Both lowercase and checksummed EVM addresses are valid inputs. The address is
|
|
22
|
+
* preserved as received; the endpoint evaluates the exact recipient the client
|
|
23
|
+
* is about to authorize.
|
|
24
|
+
*/
|
|
25
|
+
export async function twzrdBasePreflight(requirements, options = {}) {
|
|
26
|
+
const payTo = basePayTo(requirements);
|
|
27
|
+
const fetchFn = options.fetch ?? globalThis.fetch;
|
|
28
|
+
if (typeof fetchFn !== "function")
|
|
29
|
+
throw new Error("[twzrd] Worker fetch is unavailable");
|
|
30
|
+
const intelBase = (options.intelBase ?? "https://intel.twzrd.xyz").replace(/\/+$/, "");
|
|
31
|
+
const response = await fetchFn(`${intelBase}/v1/intel/preflight`, {
|
|
32
|
+
method: "POST",
|
|
33
|
+
headers: { "content-type": "application/json" },
|
|
34
|
+
body: JSON.stringify({
|
|
35
|
+
resource_name: options.resourceName ?? requirements.resource ?? "cloudflare_base_x402",
|
|
36
|
+
resource_url: requirements.resource,
|
|
37
|
+
seller_wallet: payTo,
|
|
38
|
+
price_usdc: options.priceUsdc,
|
|
39
|
+
agent_intent: options.agentIntent ?? "cloudflare_base_x402_preflight",
|
|
40
|
+
chain: "base",
|
|
41
|
+
chain_id: BASE_CHAIN_ID,
|
|
42
|
+
}),
|
|
43
|
+
});
|
|
44
|
+
if (!response.ok)
|
|
45
|
+
throw new Error(`[twzrd] Base preflight HTTP ${response.status}`);
|
|
46
|
+
return normalizePreflight(await response.json());
|
|
47
|
+
}
|
|
48
|
+
/** Cloudflare `withX402Client` callback: true permits the retry; false aborts it. */
|
|
49
|
+
export function createTwzrdCloudflareBaseApproval(options = {}) {
|
|
50
|
+
return async (requirements) => {
|
|
51
|
+
try {
|
|
52
|
+
return (await twzrdBasePreflight(requirements, options)).decision !== "block";
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return options.failOpen === true;
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Minimal signing interceptor for a Worker or Viem account. The callback is
|
|
61
|
+
* invoked only after the exact Base payTo received a non-block verdict.
|
|
62
|
+
*/
|
|
63
|
+
export async function withTwzrdBasePreflight(requirements, options, signOrSend) {
|
|
64
|
+
let verdict;
|
|
65
|
+
try {
|
|
66
|
+
verdict = await twzrdBasePreflight(requirements, options);
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
if (options.failOpen === true)
|
|
70
|
+
return signOrSend();
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
if (verdict.decision === "block")
|
|
74
|
+
throw new TwzrdBasePaymentBlockedError(verdict);
|
|
75
|
+
return signOrSend();
|
|
76
|
+
}
|
|
77
|
+
function basePayTo(requirements) {
|
|
78
|
+
const accept = requirements.accepts?.find((candidate) => {
|
|
79
|
+
const network = candidate.network;
|
|
80
|
+
const chainId = candidate.chainId ?? candidate.chain_id;
|
|
81
|
+
return network === BASE_NETWORK || chainId === BASE_CHAIN_ID || chainId === String(BASE_CHAIN_ID);
|
|
82
|
+
});
|
|
83
|
+
const payTo = accept?.payTo ?? accept?.pay_to;
|
|
84
|
+
if (typeof payTo !== "string" || !/^0x[a-fA-F0-9]{40}$/.test(payTo)) {
|
|
85
|
+
throw new Error("[twzrd] Base x402 requirements lack a valid eip155:8453 payTo");
|
|
86
|
+
}
|
|
87
|
+
return payTo;
|
|
88
|
+
}
|
|
89
|
+
function normalizePreflight(value) {
|
|
90
|
+
const response = asRecord(value);
|
|
91
|
+
if (!response)
|
|
92
|
+
throw new Error("[twzrd] Base preflight returned a non-object response");
|
|
93
|
+
const card = asRecord(response.readiness_card) ?? response;
|
|
94
|
+
const decision = card.decision;
|
|
95
|
+
if (decision !== "allow" && decision !== "warn" && decision !== "block") {
|
|
96
|
+
throw new Error("[twzrd] Base preflight returned no valid decision");
|
|
97
|
+
}
|
|
98
|
+
const riskScore = numberOrNull(card.risk_score) ?? numberOrNull(card.trust_score);
|
|
99
|
+
const reasons = strings(card.reasons ?? card.reason_codes ?? asRecord(card.decision_envelope)?.reason_codes);
|
|
100
|
+
return { decision, riskScore, reasons };
|
|
101
|
+
}
|
|
102
|
+
function asRecord(value) {
|
|
103
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
104
|
+
? value
|
|
105
|
+
: undefined;
|
|
106
|
+
}
|
|
107
|
+
function numberOrNull(value) {
|
|
108
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
109
|
+
}
|
|
110
|
+
function strings(value) {
|
|
111
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=cloudflare-base.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-base.js","sourceRoot":"","sources":["../src/cloudflare-base.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare Agents x402 approval adapter.
|
|
3
|
+
*
|
|
4
|
+
* Cloudflare's `withX402Client(...).callTool(onPaymentRequired, ...)` asks the
|
|
5
|
+
* caller to approve a payment before it constructs and submits the payment
|
|
6
|
+
* payload. This adapter turns that callback into the same TWZRD decision used
|
|
7
|
+
* by the generic MCP hook.
|
|
8
|
+
*
|
|
9
|
+
* This is an approval boundary, not a claim that Base has TWZRD behavioral
|
|
10
|
+
* reputation. Base/EVM stays `decision=unknown`: observe permits it and strict
|
|
11
|
+
* refuses it before the Cloudflare client pays.
|
|
12
|
+
*/
|
|
13
|
+
import type { TwzrdGateConfig, X402PaymentRequiredBody } from "./types.js";
|
|
14
|
+
/** Dependency-free structural shape passed to Cloudflare's callback. */
|
|
15
|
+
export type CloudflareX402PaymentRequirements = X402PaymentRequiredBody & {
|
|
16
|
+
resource?: string;
|
|
17
|
+
description?: string;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Build a Cloudflare `onPaymentRequired` callback.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* const approvePayment = createTwzrdCloudflareX402Approval({
|
|
24
|
+
* unsupportedNetworkMode: "strict",
|
|
25
|
+
* });
|
|
26
|
+
* await client.callTool(approvePayment, { name: "paid_tool", arguments: {} });
|
|
27
|
+
*/
|
|
28
|
+
export declare function createTwzrdCloudflareX402Approval(options?: TwzrdGateConfig): (paymentRequirements: CloudflareX402PaymentRequirements) => Promise<boolean>;
|
|
29
|
+
//# sourceMappingURL=cloudflare-x402.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-x402.d.ts","sourceRoot":"","sources":["../src/cloudflare-x402.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAE3E,wEAAwE;AACxE,MAAM,MAAM,iCAAiC,GAAG,uBAAuB,GAAG;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,iCAAiC,CAC/C,OAAO,CAAC,EAAE,eAAe,GACxB,CAAC,mBAAmB,EAAE,iCAAiC,KAAK,OAAO,CAAC,OAAO,CAAC,CAU9E"}
|