|
| 1 | +# @zbdpay/agent-fetch |
| 2 | + |
| 3 | +L402-aware fetch client for paid HTTP resources. |
| 4 | + |
| 5 | +This package handles the full payment challenge flow: |
| 6 | +- parse `402 Payment Required` responses |
| 7 | +- support both `L402` and `LSAT` schemes |
| 8 | +- pay using caller-provided hooks |
| 9 | +- retry with `Authorization` proof |
| 10 | +- cache proofs locally to avoid duplicate payments |
| 11 | + |
| 12 | +## Requirements |
| 13 | + |
| 14 | +- Node.js `>=22` |
| 15 | +- npm |
| 16 | + |
| 17 | +## Install |
| 18 | + |
| 19 | +```bash |
| 20 | +npm install @zbdpay/agent-fetch |
| 21 | +``` |
| 22 | + |
| 23 | +## Quick Start |
| 24 | + |
| 25 | +```ts |
| 26 | +import { agentFetch, FileTokenCache } from "@zbdpay/agent-fetch"; |
| 27 | + |
| 28 | +const tokenCache = new FileTokenCache(`${process.env.HOME}/.zbd-wallet/token-cache.json`); |
| 29 | + |
| 30 | +const response = await agentFetch("https://example.com/protected", { |
| 31 | + tokenCache, |
| 32 | + maxPaymentSats: 100, |
| 33 | + pay: async (challenge) => { |
| 34 | + // Pay challenge.invoice with your wallet implementation. |
| 35 | + // Return preimage, plus optional paymentId/amountPaidSats. |
| 36 | + return { |
| 37 | + preimage: "<payment-preimage>", |
| 38 | + paymentId: "<payment-id>", |
| 39 | + amountPaidSats: challenge.amountSats, |
| 40 | + }; |
| 41 | + }, |
| 42 | + waitForPayment: async (paymentId) => { |
| 43 | + // Optional poller for async settlement. |
| 44 | + // Return pending/completed/failed. |
| 45 | + return { |
| 46 | + status: "completed", |
| 47 | + paymentId, |
| 48 | + preimage: "<payment-preimage>", |
| 49 | + amountPaidSats: 21, |
| 50 | + }; |
| 51 | + }, |
| 52 | +}); |
| 53 | + |
| 54 | +const body = await response.json(); |
| 55 | +console.log(response.status, body); |
| 56 | +``` |
| 57 | + |
| 58 | +## Behavior |
| 59 | + |
| 60 | +- If cached auth exists and is not expired, request is sent immediately with proof. |
| 61 | +- If response is not `402`, original response is returned untouched. |
| 62 | +- If response is `402`, challenge is parsed from `WWW-Authenticate` and/or JSON body. |
| 63 | +- If payment succeeds, proof is generated as `<SCHEME> <macaroon-or-token>:<preimage>` and request is retried. |
| 64 | +- If `maxPaymentSats` is set and challenge exceeds it, call fails before payment. |
| 65 | +- If async settlement is used and times out, call fails with a timeout error. |
| 66 | + |
| 67 | +## Public API |
| 68 | + |
| 69 | +Exports from `src/index.ts`: |
| 70 | + |
| 71 | +- `agentFetch` |
| 72 | +- `requestChallenge` |
| 73 | +- `payChallenge` |
| 74 | +- `fetchWithProof` |
| 75 | +- `FileTokenCache` |
| 76 | +- types: `AgentFetchOptions`, `PaymentChallenge`, `PaidChallenge`, `PaymentSettlement`, `TokenCache`, `TokenRecord`, `ChallengeScheme` |
| 77 | + |
| 78 | +## Options (`AgentFetchOptions`) |
| 79 | + |
| 80 | +- `pay` (required): function to pay a parsed challenge |
| 81 | +- `waitForPayment` (optional): poller for async settlement |
| 82 | +- `tokenCache` (optional): token cache backend |
| 83 | +- `requestInit` (optional): forwarded fetch options |
| 84 | +- `fetchImpl` (optional): custom fetch implementation |
| 85 | +- `maxPaymentSats` (optional): payment guardrail |
| 86 | +- `paymentTimeoutMs` (optional, default `30000`) |
| 87 | +- `paymentPollIntervalMs` (optional, default `300`) |
| 88 | +- `now`, `sleep` (optional testability hooks) |
| 89 | + |
| 90 | +## Token Cache |
| 91 | + |
| 92 | +`FileTokenCache` stores per-URL tokens as JSON and writes atomically. |
| 93 | + |
| 94 | +- no `expiresAt`: token is reused until overwritten/deleted |
| 95 | +- with `expiresAt`: expired token is evicted on read |
| 96 | + |
| 97 | +Default cache location is chosen by the caller. In this suite, `agent-wallet` uses `~/.zbd-wallet/token-cache.json`. |
| 98 | + |
| 99 | +## Scripts |
| 100 | + |
| 101 | +```bash |
| 102 | +npm run build |
| 103 | +npm run test |
| 104 | +npm run lint |
| 105 | +npm run typecheck |
| 106 | +npm run smoke:imports |
| 107 | +npm run release:dry-run |
| 108 | +``` |
| 109 | + |
| 110 | +## Related Packages |
| 111 | + |
| 112 | +- `@zbdpay/agent-wallet` uses this package for `zbdw fetch` |
| 113 | +- `@zbdpay/agent-pay` provides middleware that emits L402 challenges this client can consume |
0 commit comments