@agentcash/router 1.5.0 → 1.5.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 +80 -17
- package/dist/index.cjs +336 -77
- package/dist/index.d.cts +99 -42
- package/dist/index.d.ts +99 -42
- package/dist/index.js +336 -77
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -47,8 +47,10 @@ export const env = createEnv({
|
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
Without these keys, x402 routes that use the default EVM facilitator will fail to initialize.
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
`createRouter(config)` always validates the base URL and protocol list, throws protocol config
|
|
51
|
+
errors in production, and keeps protocol-specific init errors as request-time JSON 500s in
|
|
52
|
+
development. Call `validateRouterConfig(config)` before `createRouter(config)` when you want
|
|
53
|
+
missing facilitator, MPP, or store configuration to throw immediately in every environment.
|
|
52
54
|
|
|
53
55
|
### Recommended strict setup
|
|
54
56
|
|
|
@@ -61,17 +63,18 @@ import {
|
|
|
61
63
|
type ProtocolType,
|
|
62
64
|
} from '@agentcash/router';
|
|
63
65
|
|
|
64
|
-
const
|
|
66
|
+
const payeeAddress = process.env.X402_WALLET_ADDRESS ?? process.env.X402_PAYEE_ADDRESS;
|
|
67
|
+
const accepts = x402AcceptsFromEnv(process.env, { payeeAddress });
|
|
65
68
|
const protocols: ProtocolType[] = process.env.MPP_SECRET_KEY ? ['x402', 'mpp'] : ['x402'];
|
|
66
69
|
|
|
67
70
|
const config = {
|
|
68
|
-
payeeAddress:
|
|
71
|
+
payeeAddress: payeeAddress!,
|
|
69
72
|
baseUrl: process.env.NEXT_PUBLIC_BASE_URL!,
|
|
70
73
|
strictRoutes: true,
|
|
71
74
|
protocols,
|
|
72
75
|
x402: { accepts },
|
|
73
76
|
mpp: mppFromEnv(process.env, {
|
|
74
|
-
recipient:
|
|
77
|
+
recipient: payeeAddress,
|
|
75
78
|
useDefaultStore: true,
|
|
76
79
|
}),
|
|
77
80
|
discovery: {
|
|
@@ -86,11 +89,18 @@ export const router = createRouter(config);
|
|
|
86
89
|
|
|
87
90
|
`x402AcceptsFromEnv()` always adds Base (`BASE_NETWORK`) and also adds Solana
|
|
88
91
|
mainnet (`SOLANA_MAINNET_NETWORK`) when `SOLANA_PAYEE_ADDRESS` is set. Solana
|
|
89
|
-
addresses are case-sensitive and are preserved as-is.
|
|
92
|
+
addresses are case-sensitive and are preserved as-is. It reads
|
|
93
|
+
`X402_WALLET_ADDRESS` by default; older services that still use
|
|
94
|
+
`X402_PAYEE_ADDRESS` can pass `{ payeeEnv: 'X402_PAYEE_ADDRESS' }`.
|
|
90
95
|
|
|
91
96
|
`mppFromEnv()` returns `undefined` when no MPP env vars are present. If any MPP
|
|
92
97
|
env var is present, the full trio is required: `MPP_SECRET_KEY`, `MPP_CURRENCY`,
|
|
93
|
-
and `TEMPO_RPC_URL`.
|
|
98
|
+
and `TEMPO_RPC_URL`. `MPP_CURRENCY` must be the Tempo currency address; for
|
|
99
|
+
Tempo USDC use `TEMPO_USDC_CURRENCY`. Optional `MPP_FEE_PAYER_KEY` is included
|
|
100
|
+
when present and validated as a 32-byte EVM private key. `mppFromEnv()` only
|
|
101
|
+
builds config; call `validateRouterConfig(config)` before `createRouter(config)`
|
|
102
|
+
to fail fast on `mpp.useDefaultStore` store env (`KV_REST_API_URL` and
|
|
103
|
+
`KV_REST_API_TOKEN`) when you use the default store.
|
|
94
104
|
|
|
95
105
|
## Quick Start
|
|
96
106
|
|
|
@@ -101,7 +111,7 @@ and `TEMPO_RPC_URL`.
|
|
|
101
111
|
import { createRouter } from '@agentcash/router';
|
|
102
112
|
|
|
103
113
|
export const router = createRouter({
|
|
104
|
-
payeeAddress: process.env.
|
|
114
|
+
payeeAddress: process.env.X402_WALLET_ADDRESS!,
|
|
105
115
|
baseUrl: process.env.NEXT_PUBLIC_BASE_URL!,
|
|
106
116
|
strictRoutes: true, // recommended
|
|
107
117
|
discovery: {
|
|
@@ -187,8 +197,8 @@ Creates a `ServiceRouter` instance.
|
|
|
187
197
|
| `plugin` | `RouterPlugin` | `undefined` | Observability plugin |
|
|
188
198
|
| `prices` | `Record<string, string>` | `undefined` | Central pricing map (auto-applied) |
|
|
189
199
|
| `siwx.nonceStore` | `NonceStore` | `MemoryNonceStore` | Custom nonce store |
|
|
190
|
-
| `mpp` | `{ secretKey, currency, recipient?, rpcUrl?, useDefaultStore? }` | `undefined` | MPP config |
|
|
191
|
-
| `protocols` | `('x402' \| 'mpp')[]` | `['x402']` | Default protocols for
|
|
200
|
+
| `mpp` | `{ secretKey, currency, recipient?, rpcUrl?, feePayerKey?, useDefaultStore? }` | `undefined` | MPP config |
|
|
201
|
+
| `protocols` | `('x402' \| 'mpp')[]` | `['x402']` | Default protocols for paid routes |
|
|
192
202
|
| `strictRoutes` | `boolean` | `false` | Enforce `route({ path })` and prevent key/path divergence |
|
|
193
203
|
|
|
194
204
|
### Config validation helpers
|
|
@@ -207,7 +217,9 @@ import {
|
|
|
207
217
|
```
|
|
208
218
|
|
|
209
219
|
- `validateRouterConfig(config)` throws `RouterConfigError` with structured
|
|
210
|
-
issues. Use it when you want invalid env/config to fail at startup
|
|
220
|
+
issues. Use it when you want invalid env/config to fail at startup in every
|
|
221
|
+
environment; `createRouter(config)` still performs its own validation and
|
|
222
|
+
keeps deferred protocol init errors in development for request-time feedback.
|
|
211
223
|
- `getRouterConfigIssues(config)` returns the same structured issues without
|
|
212
224
|
throwing.
|
|
213
225
|
- `x402AcceptsFromEnv(env)` builds Base and optional Solana x402 accepts from
|
|
@@ -216,6 +228,8 @@ import {
|
|
|
216
228
|
partial MPP env.
|
|
217
229
|
- `paidOptionsForProtocols(protocols)` copies a protocol array into a
|
|
218
230
|
route-level `PaidOptions` object.
|
|
231
|
+
- Manual `.paid(price)` routes inherit `createRouter({ protocols })` unless
|
|
232
|
+
the route passes its own `options.protocols`.
|
|
219
233
|
|
|
220
234
|
### Path-First Routing
|
|
221
235
|
|
|
@@ -241,11 +255,13 @@ The fluent builder ensures compile-time safety:
|
|
|
241
255
|
- `.siwx()` - SIWX wallet auth
|
|
242
256
|
- `.apiKey(resolver)` - API key auth (composable with `.paid()`)
|
|
243
257
|
- `.unprotected()` - No auth
|
|
244
|
-
- `.body(zodSchema)` - Request body validation
|
|
245
|
-
- `.query(zodSchema)` - Query parameter validation
|
|
246
|
-
- `.output(zodSchema)` - Response schema
|
|
258
|
+
- `.body(zodSchema, example?)` - Request body validation with optional discovery example
|
|
259
|
+
- `.query(zodSchema, example?)` - Query parameter validation with optional discovery example
|
|
260
|
+
- `.output(zodSchema, example?)` - Response schema with optional discovery example
|
|
261
|
+
- `.inputExample(sample)` / `.outputExample(sample)` - Optional examples when a separate call reads better
|
|
247
262
|
- `.description(text)` - Route description (for OpenAPI)
|
|
248
263
|
- `.provider(name, config?)` - Provider monitoring (see [Provider Monitoring](#provider-monitoring))
|
|
264
|
+
- `.settlement({ beforeSettle, afterSettle, onSettledHandlerError, onSettlementError })` - Payment lifecycle hooks
|
|
249
265
|
- `.handler(fn)` - Terminal method, returns Next.js handler
|
|
250
266
|
|
|
251
267
|
### Pricing Modes
|
|
@@ -343,6 +359,38 @@ and best-effort recipient/transaction/receipt metadata when the protocol
|
|
|
343
359
|
provides it. x402 handlers currently see `status: 'verified'` because settlement
|
|
344
360
|
happens after a successful handler response.
|
|
345
361
|
|
|
362
|
+
### Settlement Lifecycle
|
|
363
|
+
|
|
364
|
+
For paid routes, use `.settlement()` when final checks belong after handler work
|
|
365
|
+
but before router-controlled settlement:
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
router.route('render')
|
|
369
|
+
.paid('0.10')
|
|
370
|
+
.body(schema, { prompt: 'city at dusk' })
|
|
371
|
+
.settlement({
|
|
372
|
+
beforeSettle: async ({ result }) => {
|
|
373
|
+
if (!isUsableResult(result)) {
|
|
374
|
+
throw Object.assign(new Error('Render failed validation'), { status: 502 });
|
|
375
|
+
}
|
|
376
|
+
},
|
|
377
|
+
afterSettle: async ({ payment, result }) => {
|
|
378
|
+
await ledger.record({ tx: payment.transaction, result });
|
|
379
|
+
},
|
|
380
|
+
onSettledHandlerError: async ({ payment, error }) => {
|
|
381
|
+
await compensationQueue.enqueue({ receipt: payment.receipt, error });
|
|
382
|
+
},
|
|
383
|
+
})
|
|
384
|
+
.handler(async ({ body }) => render(body));
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
`beforeSettle` can still prevent the charge for x402 and MPP
|
|
388
|
+
transaction-payload flows. `afterSettle` is for durable ledgers, analytics, and
|
|
389
|
+
post-settlement bookkeeping. `onSettledHandlerError` covers already-settled
|
|
390
|
+
MPP requests whose handler returns an error response, which is the right place
|
|
391
|
+
to enqueue app-owned refund or compensation work. The router cannot generically
|
|
392
|
+
refund protocol payments because it does not hold merchant signing keys.
|
|
393
|
+
|
|
346
394
|
### RouterPlugin
|
|
347
395
|
|
|
348
396
|
Pluggable observability. All hooks are optional and fire-and-forget.
|
|
@@ -361,8 +409,13 @@ const myPlugin: RouterPlugin = {
|
|
|
361
409
|
};
|
|
362
410
|
|
|
363
411
|
export const router = createRouter({
|
|
364
|
-
payeeAddress: process.env.
|
|
412
|
+
payeeAddress: process.env.X402_WALLET_ADDRESS!,
|
|
413
|
+
baseUrl: process.env.NEXT_PUBLIC_BASE_URL!,
|
|
365
414
|
plugin: myPlugin,
|
|
415
|
+
discovery: {
|
|
416
|
+
title: 'My API',
|
|
417
|
+
version: '1.0.0',
|
|
418
|
+
},
|
|
366
419
|
});
|
|
367
420
|
```
|
|
368
421
|
|
|
@@ -372,8 +425,13 @@ Built-in `consolePlugin()` logs lifecycle events:
|
|
|
372
425
|
import { createRouter, consolePlugin } from '@agentcash/router';
|
|
373
426
|
|
|
374
427
|
export const router = createRouter({
|
|
375
|
-
payeeAddress: process.env.
|
|
428
|
+
payeeAddress: process.env.X402_WALLET_ADDRESS!,
|
|
429
|
+
baseUrl: process.env.NEXT_PUBLIC_BASE_URL!,
|
|
376
430
|
plugin: consolePlugin(),
|
|
431
|
+
discovery: {
|
|
432
|
+
title: 'My API',
|
|
433
|
+
version: '1.0.0',
|
|
434
|
+
},
|
|
377
435
|
});
|
|
378
436
|
```
|
|
379
437
|
|
|
@@ -383,7 +441,12 @@ For services with many static-priced routes:
|
|
|
383
441
|
|
|
384
442
|
```typescript
|
|
385
443
|
const router = createRouter({
|
|
386
|
-
payeeAddress: process.env.
|
|
444
|
+
payeeAddress: process.env.X402_WALLET_ADDRESS!,
|
|
445
|
+
baseUrl: process.env.NEXT_PUBLIC_BASE_URL!,
|
|
446
|
+
discovery: {
|
|
447
|
+
title: 'My API',
|
|
448
|
+
version: '1.0.0',
|
|
449
|
+
},
|
|
387
450
|
prices: {
|
|
388
451
|
'search': '0.02',
|
|
389
452
|
'lookup': '0.05',
|