@absol-labs/agent 0.11.0 → 0.12.0-rc.2

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.
Files changed (366) hide show
  1. package/README.md +43 -51
  2. package/dist/capability/invocation-capability.d.ts +2 -2
  3. package/dist/capability/probe-capability.d.ts +225 -0
  4. package/dist/capability/probe-capability.d.ts.map +1 -0
  5. package/dist/capability/probe-capability.js +248 -0
  6. package/dist/capability/probe-capability.js.map +1 -0
  7. package/dist/cli/args.d.ts +11 -0
  8. package/dist/cli/args.d.ts.map +1 -1
  9. package/dist/cli/args.js +23 -0
  10. package/dist/cli/args.js.map +1 -1
  11. package/dist/cli/commands/claim.d.ts +32 -0
  12. package/dist/cli/commands/claim.d.ts.map +1 -1
  13. package/dist/cli/commands/claim.js +116 -28
  14. package/dist/cli/commands/claim.js.map +1 -1
  15. package/dist/cli/commands/close.d.ts.map +1 -1
  16. package/dist/cli/commands/close.js +35 -3
  17. package/dist/cli/commands/close.js.map +1 -1
  18. package/dist/cli/commands/invoke.d.ts +2 -0
  19. package/dist/cli/commands/invoke.d.ts.map +1 -1
  20. package/dist/cli/commands/invoke.js +2 -2
  21. package/dist/cli/commands/invoke.js.map +1 -1
  22. package/dist/cli/commands/mcp.d.ts.map +1 -1
  23. package/dist/cli/commands/mcp.js +15 -2
  24. package/dist/cli/commands/mcp.js.map +1 -1
  25. package/dist/cli/commands/open.d.ts.map +1 -1
  26. package/dist/cli/commands/open.js +58 -4
  27. package/dist/cli/commands/open.js.map +1 -1
  28. package/dist/cli/commands/pay.d.ts +34 -0
  29. package/dist/cli/commands/pay.d.ts.map +1 -0
  30. package/dist/cli/commands/pay.js +479 -0
  31. package/dist/cli/commands/pay.js.map +1 -0
  32. package/dist/cli/commands/reclaim-unverified.d.ts.map +1 -1
  33. package/dist/cli/commands/reclaim-unverified.js +3 -2
  34. package/dist/cli/commands/reclaim-unverified.js.map +1 -1
  35. package/dist/cli/commands/reclaim.d.ts +23 -2
  36. package/dist/cli/commands/reclaim.d.ts.map +1 -1
  37. package/dist/cli/commands/reclaim.js +204 -20
  38. package/dist/cli/commands/reclaim.js.map +1 -1
  39. package/dist/cli/commands/refund.d.ts +31 -0
  40. package/dist/cli/commands/refund.d.ts.map +1 -0
  41. package/dist/cli/commands/refund.js +397 -0
  42. package/dist/cli/commands/refund.js.map +1 -0
  43. package/dist/cli/commands/status.d.ts.map +1 -1
  44. package/dist/cli/commands/status.js +21 -2
  45. package/dist/cli/commands/status.js.map +1 -1
  46. package/dist/cli/commands/streams.js +8 -12
  47. package/dist/cli/commands/streams.js.map +1 -1
  48. package/dist/cli/commands/watch.d.ts.map +1 -1
  49. package/dist/cli/commands/watch.js +4 -1
  50. package/dist/cli/commands/watch.js.map +1 -1
  51. package/dist/cli/config.d.ts +44 -26
  52. package/dist/cli/config.d.ts.map +1 -1
  53. package/dist/cli/config.js +88 -38
  54. package/dist/cli/config.js.map +1 -1
  55. package/dist/cli/context.d.ts +19 -0
  56. package/dist/cli/context.d.ts.map +1 -1
  57. package/dist/cli/deps.d.ts +53 -0
  58. package/dist/cli/deps.d.ts.map +1 -1
  59. package/dist/cli/deps.js +51 -1
  60. package/dist/cli/deps.js.map +1 -1
  61. package/dist/cli/errors.d.ts.map +1 -1
  62. package/dist/cli/errors.js +21 -1
  63. package/dist/cli/errors.js.map +1 -1
  64. package/dist/cli/escape-hatch.d.ts +6 -4
  65. package/dist/cli/escape-hatch.d.ts.map +1 -1
  66. package/dist/cli/escape-hatch.js +6 -2
  67. package/dist/cli/escape-hatch.js.map +1 -1
  68. package/dist/cli/listings.d.ts +18 -0
  69. package/dist/cli/listings.d.ts.map +1 -1
  70. package/dist/cli/listings.js +22 -12
  71. package/dist/cli/listings.js.map +1 -1
  72. package/dist/cli/preflight.d.ts +20 -14
  73. package/dist/cli/preflight.d.ts.map +1 -1
  74. package/dist/cli/preflight.js +34 -20
  75. package/dist/cli/preflight.js.map +1 -1
  76. package/dist/cli/privy.d.ts.map +1 -1
  77. package/dist/cli/privy.js +4 -2
  78. package/dist/cli/privy.js.map +1 -1
  79. package/dist/cli/progress.d.ts +13 -1
  80. package/dist/cli/progress.d.ts.map +1 -1
  81. package/dist/cli/progress.js +7 -1
  82. package/dist/cli/progress.js.map +1 -1
  83. package/dist/cli/reclaimability.d.ts +82 -0
  84. package/dist/cli/reclaimability.d.ts.map +1 -0
  85. package/dist/cli/reclaimability.js +80 -0
  86. package/dist/cli/reclaimability.js.map +1 -0
  87. package/dist/cli/run.d.ts +2 -1
  88. package/dist/cli/run.d.ts.map +1 -1
  89. package/dist/cli/run.js +62 -21
  90. package/dist/cli/run.js.map +1 -1
  91. package/dist/cli/scan-range.d.ts +2 -2
  92. package/dist/cli/scan-range.js +2 -2
  93. package/dist/cli/status.d.ts +106 -1
  94. package/dist/cli/status.d.ts.map +1 -1
  95. package/dist/cli/status.js +325 -44
  96. package/dist/cli/status.js.map +1 -1
  97. package/dist/cli/wallet.d.ts +2 -2
  98. package/dist/cli/wallet.d.ts.map +1 -1
  99. package/dist/cli/wallet.js +29 -5
  100. package/dist/cli/wallet.js.map +1 -1
  101. package/dist/discovery/rate-band.d.ts +74 -0
  102. package/dist/discovery/rate-band.d.ts.map +1 -0
  103. package/dist/discovery/rate-band.js +114 -0
  104. package/dist/discovery/rate-band.js.map +1 -0
  105. package/dist/escrow-generation.d.ts +81 -0
  106. package/dist/escrow-generation.d.ts.map +1 -0
  107. package/dist/escrow-generation.js +76 -0
  108. package/dist/escrow-generation.js.map +1 -0
  109. package/dist/frameworks/agentkit.d.ts +8 -4
  110. package/dist/frameworks/agentkit.d.ts.map +1 -1
  111. package/dist/frameworks/agentkit.js +27 -10
  112. package/dist/frameworks/agentkit.js.map +1 -1
  113. package/dist/frameworks/eliza.d.ts +3 -3
  114. package/dist/frameworks/eliza.d.ts.map +1 -1
  115. package/dist/frameworks/eliza.js +15 -6
  116. package/dist/frameworks/eliza.js.map +1 -1
  117. package/dist/frameworks/langchain.d.ts.map +1 -1
  118. package/dist/frameworks/langchain.js +8 -0
  119. package/dist/frameworks/langchain.js.map +1 -1
  120. package/dist/gateway/caller-auth-gateway.d.ts +83 -31
  121. package/dist/gateway/caller-auth-gateway.d.ts.map +1 -1
  122. package/dist/gateway/caller-auth-gateway.js +180 -27
  123. package/dist/gateway/caller-auth-gateway.js.map +1 -1
  124. package/dist/gateway/http-server.d.ts +128 -44
  125. package/dist/gateway/http-server.d.ts.map +1 -1
  126. package/dist/gateway/http-server.js +506 -155
  127. package/dist/gateway/http-server.js.map +1 -1
  128. package/dist/gateway/probe-auth.d.ts +112 -0
  129. package/dist/gateway/probe-auth.d.ts.map +1 -0
  130. package/dist/gateway/probe-auth.js +127 -0
  131. package/dist/gateway/probe-auth.js.map +1 -0
  132. package/dist/gateway/signed-rate-bounds.d.ts +51 -0
  133. package/dist/gateway/signed-rate-bounds.d.ts.map +1 -0
  134. package/dist/gateway/signed-rate-bounds.js +94 -0
  135. package/dist/gateway/signed-rate-bounds.js.map +1 -0
  136. package/dist/gateway/x402-opener-mode.d.ts +252 -0
  137. package/dist/gateway/x402-opener-mode.d.ts.map +1 -0
  138. package/dist/gateway/x402-opener-mode.js +772 -0
  139. package/dist/gateway/x402-opener-mode.js.map +1 -0
  140. package/dist/index.d.ts +6 -2
  141. package/dist/index.d.ts.map +1 -1
  142. package/dist/index.js +10 -5
  143. package/dist/index.js.map +1 -1
  144. package/dist/mcp/server.d.ts +64 -1
  145. package/dist/mcp/server.d.ts.map +1 -1
  146. package/dist/mcp/server.js +247 -37
  147. package/dist/mcp/server.js.map +1 -1
  148. package/dist/mcp/x402-tools.d.ts +109 -0
  149. package/dist/mcp/x402-tools.d.ts.map +1 -0
  150. package/dist/mcp/x402-tools.js +554 -0
  151. package/dist/mcp/x402-tools.js.map +1 -0
  152. package/dist/network.d.ts +119 -0
  153. package/dist/network.d.ts.map +1 -0
  154. package/dist/network.js +199 -0
  155. package/dist/network.js.map +1 -0
  156. package/dist/sdk/client.d.ts +13 -0
  157. package/dist/sdk/client.d.ts.map +1 -1
  158. package/dist/sdk/client.js +15 -0
  159. package/dist/sdk/client.js.map +1 -1
  160. package/dist/sdk/invoke.d.ts +4 -0
  161. package/dist/sdk/invoke.d.ts.map +1 -1
  162. package/dist/sdk/invoke.js +2 -5
  163. package/dist/sdk/invoke.js.map +1 -1
  164. package/dist/wallet/autonomous-wallet-broker.d.ts +3 -0
  165. package/dist/wallet/autonomous-wallet-broker.d.ts.map +1 -1
  166. package/dist/wallet/autonomous-wallet-broker.js +27 -23
  167. package/dist/wallet/autonomous-wallet-broker.js.map +1 -1
  168. package/dist/wallet/autonomous-wallet-protocol.d.ts +8 -2
  169. package/dist/wallet/autonomous-wallet-protocol.d.ts.map +1 -1
  170. package/dist/wallet/autonomous-wallet-protocol.js +9 -2
  171. package/dist/wallet/autonomous-wallet-protocol.js.map +1 -1
  172. package/dist/wallet/autonomous-wallet.d.ts.map +1 -1
  173. package/dist/wallet/autonomous-wallet.js +3 -2
  174. package/dist/wallet/autonomous-wallet.js.map +1 -1
  175. package/dist/wallet/broker-escrows.d.ts +222 -0
  176. package/dist/wallet/broker-escrows.d.ts.map +1 -0
  177. package/dist/wallet/broker-escrows.js +36 -0
  178. package/dist/wallet/broker-escrows.js.map +1 -0
  179. package/dist/wallet/lifecycle.d.ts +21 -5
  180. package/dist/wallet/lifecycle.d.ts.map +1 -1
  181. package/dist/wallet/lifecycle.js +32 -12
  182. package/dist/wallet/lifecycle.js.map +1 -1
  183. package/dist/wallet/privy-broker-server.d.ts.map +1 -1
  184. package/dist/wallet/privy-broker-server.js +31 -26
  185. package/dist/wallet/privy-broker-server.js.map +1 -1
  186. package/dist/wallet/privy-session-broker.d.ts +14 -5
  187. package/dist/wallet/privy-session-broker.d.ts.map +1 -1
  188. package/dist/wallet/privy-session-broker.js +29 -17
  189. package/dist/wallet/privy-session-broker.js.map +1 -1
  190. package/dist/wallet/privy-session-provider.d.ts +3 -1
  191. package/dist/wallet/privy-session-provider.d.ts.map +1 -1
  192. package/dist/wallet/privy-session-provider.js +5 -3
  193. package/dist/wallet/privy-session-provider.js.map +1 -1
  194. package/dist/wallet/provider.d.ts.map +1 -1
  195. package/dist/wallet/provider.js +15 -8
  196. package/dist/wallet/provider.js.map +1 -1
  197. package/dist/x402/facilitator.d.ts +3 -30
  198. package/dist/x402/facilitator.d.ts.map +1 -1
  199. package/dist/x402/facilitator.js +0 -27
  200. package/dist/x402/facilitator.js.map +1 -1
  201. package/dist/x402-buyer/guarded-fetch.d.ts +51 -0
  202. package/dist/x402-buyer/guarded-fetch.d.ts.map +1 -0
  203. package/dist/x402-buyer/guarded-fetch.js +147 -0
  204. package/dist/x402-buyer/guarded-fetch.js.map +1 -0
  205. package/dist/x402-buyer/index.d.ts +10 -0
  206. package/dist/x402-buyer/index.d.ts.map +1 -0
  207. package/dist/x402-buyer/index.js +10 -0
  208. package/dist/x402-buyer/index.js.map +1 -0
  209. package/dist/x402-buyer/pay.d.ts +157 -0
  210. package/dist/x402-buyer/pay.d.ts.map +1 -0
  211. package/dist/x402-buyer/pay.js +366 -0
  212. package/dist/x402-buyer/pay.js.map +1 -0
  213. package/dist/x402-opener/abi.d.ts +684 -0
  214. package/dist/x402-opener/abi.d.ts.map +1 -0
  215. package/dist/x402-opener/abi.js +880 -0
  216. package/dist/x402-opener/abi.js.map +1 -0
  217. package/dist/x402-opener/facilitator-entry.d.ts +3 -0
  218. package/dist/x402-opener/facilitator-entry.d.ts.map +1 -0
  219. package/dist/x402-opener/facilitator-entry.js +40 -0
  220. package/dist/x402-opener/facilitator-entry.js.map +1 -0
  221. package/dist/x402-opener/facilitator-server.d.ts +209 -0
  222. package/dist/x402-opener/facilitator-server.d.ts.map +1 -0
  223. package/dist/x402-opener/facilitator-server.js +762 -0
  224. package/dist/x402-opener/facilitator-server.js.map +1 -0
  225. package/dist/x402-opener/index.d.ts +14 -0
  226. package/dist/x402-opener/index.d.ts.map +1 -0
  227. package/dist/x402-opener/index.js +14 -0
  228. package/dist/x402-opener/index.js.map +1 -0
  229. package/dist/x402-opener/listing-resolver.d.ts +55 -0
  230. package/dist/x402-opener/listing-resolver.d.ts.map +1 -0
  231. package/dist/x402-opener/listing-resolver.js +125 -0
  232. package/dist/x402-opener/listing-resolver.js.map +1 -0
  233. package/dist/x402-opener/refund-relay.d.ts +266 -0
  234. package/dist/x402-opener/refund-relay.d.ts.map +1 -0
  235. package/dist/x402-opener/refund-relay.js +1341 -0
  236. package/dist/x402-opener/refund-relay.js.map +1 -0
  237. package/dist/x402-opener/relayer-queue.d.ts +14 -0
  238. package/dist/x402-opener/relayer-queue.d.ts.map +1 -0
  239. package/dist/x402-opener/relayer-queue.js +18 -0
  240. package/dist/x402-opener/relayer-queue.js.map +1 -0
  241. package/dist/x402-opener/scheme.d.ts +296 -0
  242. package/dist/x402-opener/scheme.d.ts.map +1 -0
  243. package/dist/x402-opener/scheme.js +1035 -0
  244. package/dist/x402-opener/scheme.js.map +1 -0
  245. package/dist/x402-opener/terms.d.ts +165 -0
  246. package/dist/x402-opener/terms.d.ts.map +1 -0
  247. package/dist/x402-opener/terms.js +304 -0
  248. package/dist/x402-opener/terms.js.map +1 -0
  249. package/dist/zktls/reclaim.d.ts +2 -2
  250. package/dist/zktls/t2-delivery-proof.d.ts +6 -6
  251. package/docs/agent-layer.md +3 -2
  252. package/docs/autonomous-privy-wallet.md +188 -0
  253. package/docs/cli.md +265 -34
  254. package/docs/langchain.md +5 -0
  255. package/docs/mcp-reference.md +91 -5
  256. package/docs/probe-capability.md +191 -0
  257. package/docs/quickstart.md +39 -4
  258. package/docs/threat-model.md +20 -0
  259. package/docs/x402.md +766 -593
  260. package/package.json +19 -17
  261. package/src/capability/probe-capability.ts +355 -0
  262. package/src/cli/args.ts +29 -0
  263. package/src/cli/commands/claim.ts +168 -33
  264. package/src/cli/commands/close.ts +36 -3
  265. package/src/cli/commands/invoke.ts +5 -2
  266. package/src/cli/commands/mcp.ts +18 -2
  267. package/src/cli/commands/open.ts +75 -4
  268. package/src/cli/commands/pay.ts +584 -0
  269. package/src/cli/commands/reclaim-unverified.ts +8 -2
  270. package/src/cli/commands/reclaim.ts +277 -19
  271. package/src/cli/commands/refund.ts +475 -0
  272. package/src/cli/commands/status.ts +28 -2
  273. package/src/cli/commands/streams.ts +10 -14
  274. package/src/cli/commands/watch.ts +11 -1
  275. package/src/cli/config.ts +139 -52
  276. package/src/cli/context.ts +16 -0
  277. package/src/cli/deps.ts +111 -1
  278. package/src/cli/errors.ts +24 -1
  279. package/src/cli/escape-hatch.ts +19 -5
  280. package/src/cli/listings.ts +43 -10
  281. package/src/cli/preflight.ts +36 -21
  282. package/src/cli/privy.ts +4 -2
  283. package/src/cli/progress.ts +21 -2
  284. package/src/cli/reclaimability.ts +154 -0
  285. package/src/cli/run.ts +77 -21
  286. package/src/cli/scan-range.ts +2 -2
  287. package/src/cli/status.ts +454 -44
  288. package/src/cli/wallet.ts +45 -8
  289. package/src/discovery/rate-band.ts +172 -0
  290. package/src/escrow-generation.ts +135 -0
  291. package/src/frameworks/agentkit.ts +45 -11
  292. package/src/frameworks/eliza.ts +27 -5
  293. package/src/frameworks/langchain.ts +14 -0
  294. package/src/gateway/caller-auth-gateway.ts +320 -68
  295. package/src/gateway/http-server.ts +717 -220
  296. package/src/gateway/probe-auth.ts +191 -0
  297. package/src/gateway/signed-rate-bounds.ts +164 -0
  298. package/src/gateway/x402-opener-mode.ts +1171 -0
  299. package/src/index.ts +82 -19
  300. package/src/mcp/server.ts +351 -42
  301. package/src/mcp/x402-tools.ts +785 -0
  302. package/src/network.ts +302 -0
  303. package/src/sdk/client.ts +25 -0
  304. package/src/sdk/invoke.ts +6 -5
  305. package/src/wallet/autonomous-wallet-broker.ts +46 -20
  306. package/src/wallet/autonomous-wallet-protocol.ts +11 -2
  307. package/src/wallet/autonomous-wallet.ts +4 -2
  308. package/src/wallet/broker-escrows.ts +66 -0
  309. package/src/wallet/lifecycle.ts +53 -18
  310. package/src/wallet/privy-broker-server.ts +35 -25
  311. package/src/wallet/privy-session-broker.ts +58 -25
  312. package/src/wallet/privy-session-provider.ts +7 -3
  313. package/src/wallet/provider.ts +20 -15
  314. package/src/x402/facilitator.ts +3 -43
  315. package/src/x402-buyer/guarded-fetch.ts +213 -0
  316. package/src/x402-buyer/index.ts +9 -0
  317. package/src/x402-buyer/pay.ts +555 -0
  318. package/src/x402-opener/abi.ts +881 -0
  319. package/src/x402-opener/facilitator-entry.ts +48 -0
  320. package/src/x402-opener/facilitator-server.ts +1114 -0
  321. package/src/x402-opener/index.ts +13 -0
  322. package/src/x402-opener/listing-resolver.ts +202 -0
  323. package/src/x402-opener/refund-relay.ts +1873 -0
  324. package/src/x402-opener/relayer-queue.ts +18 -0
  325. package/src/x402-opener/scheme.ts +1551 -0
  326. package/src/x402-opener/terms.ts +453 -0
  327. package/dist/gateway/x402-mode.d.ts +0 -123
  328. package/dist/gateway/x402-mode.d.ts.map +0 -1
  329. package/dist/gateway/x402-mode.js +0 -212
  330. package/dist/gateway/x402-mode.js.map +0 -1
  331. package/dist/x402/delivery-oracle.d.ts +0 -108
  332. package/dist/x402/delivery-oracle.d.ts.map +0 -1
  333. package/dist/x402/delivery-oracle.js +0 -174
  334. package/dist/x402/delivery-oracle.js.map +0 -1
  335. package/dist/x402/facilitator-server-entry.d.ts +0 -3
  336. package/dist/x402/facilitator-server-entry.d.ts.map +0 -1
  337. package/dist/x402/facilitator-server-entry.js +0 -27
  338. package/dist/x402/facilitator-server-entry.js.map +0 -1
  339. package/dist/x402/facilitator-server.d.ts +0 -73
  340. package/dist/x402/facilitator-server.d.ts.map +0 -1
  341. package/dist/x402/facilitator-server.js +0 -305
  342. package/dist/x402/facilitator-server.js.map +0 -1
  343. package/dist/x402/payment-requirements.d.ts +0 -106
  344. package/dist/x402/payment-requirements.d.ts.map +0 -1
  345. package/dist/x402/payment-requirements.js +0 -138
  346. package/dist/x402/payment-requirements.js.map +0 -1
  347. package/dist/x402/relayer.d.ts +0 -41
  348. package/dist/x402/relayer.d.ts.map +0 -1
  349. package/dist/x402/relayer.js +0 -64
  350. package/dist/x402/relayer.js.map +0 -1
  351. package/dist/x402/resource-index.d.ts +0 -75
  352. package/dist/x402/resource-index.d.ts.map +0 -1
  353. package/dist/x402/resource-index.js +0 -126
  354. package/dist/x402/resource-index.js.map +0 -1
  355. package/dist/x402/verified-delivery-facilitator.d.ts +0 -285
  356. package/dist/x402/verified-delivery-facilitator.d.ts.map +0 -1
  357. package/dist/x402/verified-delivery-facilitator.js +0 -423
  358. package/dist/x402/verified-delivery-facilitator.js.map +0 -1
  359. package/src/gateway/x402-mode.ts +0 -350
  360. package/src/x402/delivery-oracle.ts +0 -285
  361. package/src/x402/facilitator-server-entry.ts +0 -35
  362. package/src/x402/facilitator-server.ts +0 -445
  363. package/src/x402/payment-requirements.ts +0 -231
  364. package/src/x402/relayer.ts +0 -122
  365. package/src/x402/resource-index.ts +0 -228
  366. package/src/x402/verified-delivery-facilitator.ts +0 -744
@@ -1,350 +0,0 @@
1
- import type {
2
- PaymentRequirements,
3
- SettleResponse,
4
- VerifyResponse,
5
- } from "x402/types";
6
-
7
- import {
8
- buildMetrikPaymentRequirements,
9
- buildX402PaymentRequiredBody,
10
- type SignedListingPayee,
11
- type X402AssetConfig,
12
- type X402PaymentRequiredBody,
13
- } from "../x402/payment-requirements.js";
14
-
15
- /**
16
- * Standard x402 mode for the seller-side gateway (Tier 1).
17
- *
18
- * The gateway already authorizes requests that carry a Metrik
19
- * `InvocationCapability` — a buyer with an open, funded, verified stream. That
20
- * path is untouched. This module adds a SECOND, independent way in: a request
21
- * with no capability gets an ordinary HTTP `402` carrying real x402
22
- * `PaymentRequirements`, so an agent holding nothing but `x402-fetch` can pay
23
- * per call without knowing Metrik exists.
24
- *
25
- * ## The two paths never overlap
26
- *
27
- * - capability header present -> stream path, exactly as before;
28
- * - capability header absent, x402 mode on -> `402` + `PaymentRequirements`;
29
- * - BOTH present -> rejected. A request that looks like both is ambiguous about
30
- * which one is paying for it, and the failure mode of guessing is charging the
31
- * caller twice for one call. Better a loud `400` than a silent double charge.
32
- *
33
- * ## The per-call price is the seller's, and is not signed
34
- *
35
- * `payTo` comes from the operator-signed descriptor (see
36
- * `../x402/payment-requirements.ts`). The price does not — the descriptor has no
37
- * per-call price field. It is the seller's own number on the seller's own
38
- * gateway, which is legitimate, but a buyer cannot cross-check it against the
39
- * registry the way they can cross-check the payee.
40
- */
41
-
42
- /** Request header carrying the x402 payment payload (x402 v1). */
43
- export const X402_PAYMENT_HEADER = "x-payment" as const;
44
-
45
- /** Response header carrying the settlement result (x402 v1). */
46
- export const X402_PAYMENT_RESPONSE_HEADER = "x-payment-response" as const;
47
-
48
- /** The facilitator the gateway delegates verify/settle to. */
49
- export interface X402FacilitatorClient {
50
- verify(
51
- payload: unknown,
52
- requirements: PaymentRequirements,
53
- ): Promise<VerifyResponse>;
54
- settle(
55
- payload: unknown,
56
- requirements: PaymentRequirements,
57
- ): Promise<SettleResponse>;
58
- }
59
-
60
- export interface HttpFacilitatorClientOptions {
61
- /** Base URL of a facilitator exposing `/verify` and `/settle`. */
62
- readonly url: string;
63
- /**
64
- * Shared secret for the facilitator's `/settle`. Sent as
65
- * `Authorization: Bearer`. Metrik's own facilitator refuses to start a
66
- * settling deployment without one, because `/settle` spends its gas.
67
- */
68
- readonly token?: string;
69
- readonly headers?: Readonly<Record<string, string>>;
70
- readonly timeoutMs?: number;
71
- readonly fetch?: typeof globalThis.fetch;
72
- }
73
-
74
- /**
75
- * Talks to any x402 facilitator over HTTP — including Metrik's own
76
- * (`../x402/facilitator-server.ts`), which is the point: a seller who wires this
77
- * gateway to the Metrik facilitator gets a paywall whose payments stop when
78
- * their own delivery stops being verifiable.
79
- */
80
- export function createHttpFacilitatorClient(
81
- options: HttpFacilitatorClientOptions,
82
- ): X402FacilitatorClient {
83
- const base = options.url.replace(/\/+$/, "");
84
- const timeoutMs = options.timeoutMs ?? 10_000;
85
- const doFetch = options.fetch ?? globalThis.fetch;
86
-
87
- async function post(path: string, body: unknown): Promise<unknown> {
88
- const controller = new AbortController();
89
- const timer = setTimeout(() => controller.abort(), timeoutMs);
90
- try {
91
- const response = await doFetch(`${base}${path}`, {
92
- method: "POST",
93
- headers: {
94
- "content-type": "application/json",
95
- ...(options.token === undefined
96
- ? {}
97
- : { authorization: `Bearer ${options.token}` }),
98
- ...(options.headers ?? {}),
99
- },
100
- body: JSON.stringify(body),
101
- signal: controller.signal,
102
- });
103
- return await response.json();
104
- } finally {
105
- clearTimeout(timer);
106
- }
107
- }
108
-
109
- return {
110
- async verify(payload, requirements) {
111
- const result = (await post("/verify", {
112
- x402Version: 1,
113
- paymentPayload: payload,
114
- paymentRequirements: requirements,
115
- })) as VerifyResponse;
116
- // Fail closed: an unreadable facilitator answer is not a pass.
117
- if (typeof result !== "object" || result === null) {
118
- return { isValid: false, invalidReason: "unexpected_verify_error" };
119
- }
120
- return result;
121
- },
122
- async settle(payload, requirements) {
123
- return (await post("/settle", {
124
- x402Version: 1,
125
- paymentPayload: payload,
126
- paymentRequirements: requirements,
127
- })) as SettleResponse;
128
- },
129
- };
130
- }
131
-
132
- /** Builds the `PaymentRequirements` quoted for a given request. */
133
- export type X402Quoter = (request: {
134
- readonly method: string;
135
- readonly path: string;
136
- }) => readonly PaymentRequirements[] | Promise<readonly PaymentRequirements[]>;
137
-
138
- export interface ListingPriceQuoterOptions {
139
- /** Projected from the OPERATOR-SIGNED descriptor. `payTo` comes from here. */
140
- readonly listing: SignedListingPayee;
141
- /** Public origin buyers reach this gateway on, e.g. `https://svc.example.com`. */
142
- readonly resourceBaseUrl: string;
143
- /** Per-call price in the asset's base units (micro-USDC). Seller-set, unsigned. */
144
- readonly pricePerCallBaseUnits: bigint;
145
- readonly chainId: number;
146
- readonly description?: string;
147
- readonly mimeType?: string;
148
- readonly maxTimeoutSeconds?: number;
149
- readonly asset?: X402AssetConfig;
150
- }
151
-
152
- /** One flat price per call, quoted against the request's own path. */
153
- export function createListingPriceQuoter(
154
- options: ListingPriceQuoterOptions,
155
- ): X402Quoter {
156
- const base = new URL(options.resourceBaseUrl);
157
- return (request) => [
158
- buildMetrikPaymentRequirements({
159
- listing: options.listing,
160
- resource: new URL(request.path, base).toString(),
161
- priceBaseUnits: options.pricePerCallBaseUnits,
162
- chainId: options.chainId,
163
- ...(options.description === undefined
164
- ? {}
165
- : { description: options.description }),
166
- ...(options.mimeType === undefined ? {} : { mimeType: options.mimeType }),
167
- ...(options.maxTimeoutSeconds === undefined
168
- ? {}
169
- : { maxTimeoutSeconds: options.maxTimeoutSeconds }),
170
- ...(options.asset === undefined ? {} : { asset: options.asset }),
171
- }),
172
- ];
173
- }
174
-
175
- export interface GatewayX402Mode {
176
- readonly quote: X402Quoter;
177
- readonly facilitator: X402FacilitatorClient;
178
- }
179
-
180
- /** Decoded, still-untrusted `X-PAYMENT` payload plus the quote it answers. */
181
- export interface AcceptedX402Payment {
182
- readonly payload: unknown;
183
- readonly requirements: PaymentRequirements;
184
- readonly payer?: string;
185
- /**
186
- * Unix seconds after which the signed authorization is no longer settleable
187
- * (`validBefore`), when the payload carried one.
188
- *
189
- * The upstream runs BEFORE settlement, so an upstream slower than this window
190
- * would produce its side effects and then fail to be paid for. The server
191
- * bounds the upstream call by this instead of letting that happen.
192
- */
193
- readonly validBefore?: number;
194
- }
195
-
196
- export type X402EvaluationOutcome =
197
- | {
198
- readonly outcome: "payment-required";
199
- readonly body: X402PaymentRequiredBody;
200
- }
201
- | { readonly outcome: "accepted"; readonly payment: AcceptedX402Payment };
202
-
203
- /**
204
- * Evaluates the x402 half of a request. Returns either the `402` body to send or
205
- * an accepted payment to settle after the upstream has been called.
206
- */
207
- export async function evaluateX402Request(
208
- request: { readonly method: string; readonly path: string },
209
- paymentHeader: string | undefined,
210
- mode: GatewayX402Mode,
211
- ): Promise<X402EvaluationOutcome> {
212
- const accepts = await mode.quote(request);
213
- if (paymentHeader === undefined || paymentHeader.trim().length === 0) {
214
- return {
215
- outcome: "payment-required",
216
- body: buildX402PaymentRequiredBody(accepts),
217
- };
218
- }
219
-
220
- const payload = decodePaymentHeader(paymentHeader);
221
- if (payload === undefined) {
222
- return {
223
- outcome: "payment-required",
224
- body: buildX402PaymentRequiredBody(
225
- accepts,
226
- "X-PAYMENT header is not base64-encoded JSON",
227
- ),
228
- };
229
- }
230
-
231
- const requirements = selectRequirements(accepts, payload);
232
- if (requirements === undefined) {
233
- return {
234
- outcome: "payment-required",
235
- body: buildX402PaymentRequiredBody(
236
- accepts,
237
- "X-PAYMENT does not match any offered payment requirements",
238
- ),
239
- };
240
- }
241
-
242
- let verified: VerifyResponse;
243
- try {
244
- verified = await mode.facilitator.verify(payload, requirements);
245
- } catch (error) {
246
- // An unreachable facilitator is a refusal, never a free call.
247
- return {
248
- outcome: "payment-required",
249
- body: buildX402PaymentRequiredBody(
250
- accepts,
251
- `payment could not be verified: ${
252
- error instanceof Error ? error.message : "facilitator unreachable"
253
- }`,
254
- ),
255
- };
256
- }
257
- if (!verified.isValid) {
258
- return {
259
- outcome: "payment-required",
260
- body: buildX402PaymentRequiredBody(
261
- accepts,
262
- verified.invalidReason ?? "payment rejected by facilitator",
263
- ),
264
- };
265
- }
266
-
267
- // A Metrik facilitator reports up front whether it would actually broadcast
268
- // this payment (asset allowlist, settlement scope, ...). Refusing HERE — before
269
- // the upstream runs — is the difference between "the caller gets nothing" and
270
- // "the caller gets the upstream's side effects for free and we cannot charge
271
- // for them". A non-Metrik facilitator omits the field and nothing changes.
272
- const annotation = (
273
- verified as { metrik?: { settleable?: boolean; detail?: string } }
274
- ).metrik;
275
- if (annotation?.settleable === false) {
276
- return {
277
- outcome: "payment-required",
278
- body: buildX402PaymentRequiredBody(
279
- accepts,
280
- `facilitator will not settle this payment: ${annotation.detail ?? "refused"}`,
281
- ),
282
- };
283
- }
284
-
285
- const validBefore = readValidBefore(payload);
286
-
287
- return {
288
- outcome: "accepted",
289
- payment: {
290
- payload,
291
- requirements,
292
- ...(verified.payer === undefined ? {} : { payer: verified.payer }),
293
- ...(validBefore === undefined ? {} : { validBefore }),
294
- },
295
- };
296
- }
297
-
298
- /** Reads the exact-EVM authorization deadline, if the payload has one. */
299
- function readValidBefore(payload: unknown): number | undefined {
300
- const authorization = (
301
- payload as { payload?: { authorization?: { validBefore?: unknown } } }
302
- )?.payload?.authorization;
303
- const raw = authorization?.validBefore;
304
- if (typeof raw !== "string" && typeof raw !== "number") {
305
- return undefined;
306
- }
307
- const parsed = Number(raw);
308
- return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
309
- }
310
-
311
- /**
312
- * Seconds of headroom the settlement leg needs after the upstream returns.
313
- *
314
- * The x402 `exact` verifier rejects an authorization whose `validBefore` is less
315
- * than 6 s away, so an upstream allowed to run right up to the deadline would
316
- * hand back a response that can never be settled. This leaves room for that
317
- * check plus the settle round trip.
318
- */
319
- export const X402_SETTLEMENT_HEADROOM_SECONDS = 10;
320
-
321
- /** Matches the payload's scheme+network against the quotes actually offered. */
322
- function selectRequirements(
323
- accepts: readonly PaymentRequirements[],
324
- payload: unknown,
325
- ): PaymentRequirements | undefined {
326
- if (typeof payload !== "object" || payload === null) {
327
- return undefined;
328
- }
329
- const { scheme, network } = payload as {
330
- scheme?: unknown;
331
- network?: unknown;
332
- };
333
- return accepts.find(
334
- (candidate) => candidate.scheme === scheme && candidate.network === network,
335
- );
336
- }
337
-
338
- export function decodePaymentHeader(header: string): unknown {
339
- try {
340
- const decoded = Buffer.from(header.trim(), "base64").toString("utf8");
341
- const parsed: unknown = JSON.parse(decoded);
342
- return typeof parsed === "object" && parsed !== null ? parsed : undefined;
343
- } catch {
344
- return undefined;
345
- }
346
- }
347
-
348
- export function encodeSettleResponseHeader(response: SettleResponse): string {
349
- return Buffer.from(JSON.stringify(response), "utf8").toString("base64");
350
- }
@@ -1,285 +0,0 @@
1
- import { z } from "zod";
2
-
3
- /**
4
- * The Metrik delivery signal, as consumed by the x402 facilitator.
5
- *
6
- * ## Where the signal comes from
7
- *
8
- * The oracle publishes `GET /services` — one row per `serviceRef`, reduced from
9
- * the oracle's own settlement-path verdicts for the active streams of that
10
- * service (`streamproof-oracle/src/service-status.ts`). Its three states are
11
- * already buyer-favouring at the source: a FRESH failing stream beats a fresh
12
- * delivering sibling, and absent/stale evidence is reported as `unverified`
13
- * rather than folded into "fine".
14
- *
15
- * ## Why this module re-checks freshness the oracle already checked
16
- *
17
- * The oracle's own bound is 5 minutes (`DEFAULT_SERVICE_STATUS_TTL_MS`), which
18
- * is a sensible bound for a dashboard and far too loose for a payment gate. On
19
- * top of that the HTTP response itself can be cached or served by a stale
20
- * replica, and `lastCheckedAt` is the only field that dates the EVIDENCE rather
21
- * than the response. So the age of `lastCheckedAt` is re-derived here against
22
- * this module's own, much tighter window.
23
- *
24
- * ## The freshness window: 90 seconds, and why
25
- *
26
- * The oracle checkpoints every ~15–22 s, and a service's recorded status is
27
- * refreshed on that cadence. A window has to be wide enough that ordinary
28
- * jitter does not flap the gate, and narrow enough that "verified" still means
29
- * "now":
30
- *
31
- * - 3 worst-case probe intervals (3 x 22 s = 66 s) absorbs two consecutive
32
- * missed or slow ticks, which is normal operational noise, not evidence of
33
- * a dead service.
34
- * - plus the oracle's own record-and-serve latency and this process's fetch
35
- * round trip, rounded up: **90 s**.
36
- *
37
- * Tighter (e.g. 30 s) and a single slow tick turns a healthy service's gate
38
- * indeterminate. Looser (e.g. the oracle's own 300 s) and a service that died
39
- * four minutes ago still reads `delivering`, which is exactly the claim this
40
- * gate exists to avoid making.
41
- *
42
- * Note this window is strictly stricter than the oracle's: a verdict aged
43
- * between 90 s and 300 s is `delivering` at the source and `indeterminate`
44
- * here.
45
- */
46
- export const DEFAULT_DELIVERY_FRESHNESS_SECONDS = 90;
47
-
48
- /** How long a fetched `/services` snapshot may be reused. See `verdictFor`. */
49
- export const DEFAULT_DELIVERY_SNAPSHOT_TTL_MS = 10_000;
50
-
51
- /** Request timeout for the oracle read. Short: this sits in a payment hot path. */
52
- export const DEFAULT_DELIVERY_ORACLE_TIMEOUT_MS = 4_000;
53
-
54
- export const DEFAULT_METRIK_ORACLE_URL = "https://oracle.metrik.live";
55
-
56
- /**
57
- * Three states, never two.
58
- *
59
- * `indeterminate` is deliberately NOT collapsed into either of the others: the
60
- * facilitator's policy for "no fresh evidence" is a separate, configurable
61
- * decision (see `./verified-delivery-facilitator.ts`), and folding it into
62
- * `failing` would make Metrik refuse every payment whenever its own oracle is
63
- * quiet or unreachable.
64
- */
65
- export type DeliveryVerdict = "delivering" | "failing" | "indeterminate";
66
-
67
- /** Why the verdict is what it is — machine-readable, for logs and for tests. */
68
- export type DeliveryVerdictReason =
69
- | "fresh-delivering"
70
- | "fresh-failing"
71
- | "no-fresh-evidence"
72
- | "verdict-stale"
73
- | "service-not-listed"
74
- | "oracle-unreachable"
75
- | "oracle-malformed";
76
-
77
- export interface DeliveryAssessment {
78
- readonly serviceRef: string;
79
- readonly verdict: DeliveryVerdict;
80
- readonly reason: DeliveryVerdictReason;
81
- /** Unix ms of the newest recorded check for this service, when known. */
82
- readonly lastCheckedAt: number | null;
83
- /** Age of `lastCheckedAt` in seconds, when known. */
84
- readonly ageSeconds: number | null;
85
- /** The freshness window applied, in seconds. */
86
- readonly freshnessWindowSeconds: number;
87
- }
88
-
89
- export interface MetrikDeliveryOracle {
90
- /** Never throws: an unreachable oracle is an `indeterminate` verdict, not an exception. */
91
- verdictFor(serviceRef: string): Promise<DeliveryAssessment>;
92
- }
93
-
94
- /** The subset of the oracle's `/services` row this module relies on. */
95
- const serviceStatusRowSchema = z
96
- .object({
97
- serviceRef: z.string(),
98
- status: z.enum(["delivering", "failing", "unverified"]),
99
- lastCheckedAt: z.number().nullable().optional(),
100
- })
101
- .passthrough();
102
-
103
- const servicesResponseSchema = z.object({
104
- services: z.array(serviceStatusRowSchema),
105
- });
106
-
107
- export type FetchLike = (
108
- input: string,
109
- init?: { signal?: AbortSignal; headers?: Record<string, string> },
110
- ) => Promise<{
111
- ok: boolean;
112
- status: number;
113
- json(): Promise<unknown>;
114
- }>;
115
-
116
- export interface OracleDeliveryOracleOptions {
117
- /** Oracle base URL. Default {@link DEFAULT_METRIK_ORACLE_URL}. */
118
- readonly oracleUrl?: string;
119
- /** Default {@link DEFAULT_DELIVERY_FRESHNESS_SECONDS}. */
120
- readonly freshnessWindowSeconds?: number;
121
- /** Default {@link DEFAULT_DELIVERY_SNAPSHOT_TTL_MS}. */
122
- readonly snapshotTtlMs?: number;
123
- readonly timeoutMs?: number;
124
- readonly fetch?: FetchLike;
125
- /** Injectable clock (unix ms), for tests. */
126
- readonly now?: () => number;
127
- }
128
-
129
- /**
130
- * Reads `GET {oracleUrl}/services` and projects one row onto a
131
- * {@link DeliveryAssessment}.
132
- *
133
- * The snapshot is cached for {@link DEFAULT_DELIVERY_SNAPSHOT_TTL_MS} so a burst
134
- * of payments does not become a burst of oracle reads. The cache is SHORTER than
135
- * the freshness window by design, and caching cannot make a verdict look fresher
136
- * than it is: freshness is computed from the row's own `lastCheckedAt`, never
137
- * from when we fetched it.
138
- */
139
- export function createOracleDeliveryOracle(
140
- options: OracleDeliveryOracleOptions = {},
141
- ): MetrikDeliveryOracle {
142
- const oracleUrl = (options.oracleUrl ?? DEFAULT_METRIK_ORACLE_URL).replace(
143
- /\/+$/,
144
- "",
145
- );
146
- const freshnessWindowSeconds =
147
- options.freshnessWindowSeconds ?? DEFAULT_DELIVERY_FRESHNESS_SECONDS;
148
- if (!Number.isFinite(freshnessWindowSeconds) || freshnessWindowSeconds <= 0) {
149
- throw new Error("freshnessWindowSeconds must be a positive number");
150
- }
151
- const snapshotTtlMs =
152
- options.snapshotTtlMs ?? DEFAULT_DELIVERY_SNAPSHOT_TTL_MS;
153
- const timeoutMs = options.timeoutMs ?? DEFAULT_DELIVERY_ORACLE_TIMEOUT_MS;
154
- const doFetch = (options.fetch ??
155
- ((input, init) => globalThis.fetch(input, init))) as FetchLike;
156
- const now = options.now ?? (() => Date.now());
157
-
158
- type Snapshot = {
159
- readonly fetchedAtMs: number;
160
- readonly rows: Map<string, z.infer<typeof serviceStatusRowSchema>> | null;
161
- readonly failure: DeliveryVerdictReason | null;
162
- };
163
- let snapshot: Snapshot | undefined;
164
- let inFlight: Promise<Snapshot> | undefined;
165
-
166
- async function loadSnapshot(): Promise<Snapshot> {
167
- const controller = new AbortController();
168
- const timer = setTimeout(() => controller.abort(), timeoutMs);
169
- try {
170
- const response = await doFetch(`${oracleUrl}/services`, {
171
- signal: controller.signal,
172
- headers: { accept: "application/json", "cache-control": "no-cache" },
173
- });
174
- if (!response.ok) {
175
- return {
176
- fetchedAtMs: now(),
177
- rows: null,
178
- failure: "oracle-unreachable",
179
- };
180
- }
181
- const parsed = servicesResponseSchema.safeParse(await response.json());
182
- if (!parsed.success) {
183
- return { fetchedAtMs: now(), rows: null, failure: "oracle-malformed" };
184
- }
185
- const rows = new Map<string, z.infer<typeof serviceStatusRowSchema>>();
186
- for (const row of parsed.data.services) {
187
- rows.set(row.serviceRef.toLowerCase(), row);
188
- }
189
- return { fetchedAtMs: now(), rows, failure: null };
190
- } catch {
191
- return { fetchedAtMs: now(), rows: null, failure: "oracle-unreachable" };
192
- } finally {
193
- clearTimeout(timer);
194
- }
195
- }
196
-
197
- async function currentSnapshot(): Promise<Snapshot> {
198
- const cached = snapshot;
199
- if (cached !== undefined && now() - cached.fetchedAtMs < snapshotTtlMs) {
200
- return cached;
201
- }
202
- // Collapse concurrent misses onto one request.
203
- inFlight ??= loadSnapshot().finally(() => {
204
- inFlight = undefined;
205
- });
206
- const loaded = await inFlight;
207
- snapshot = loaded;
208
- return loaded;
209
- }
210
-
211
- return {
212
- async verdictFor(serviceRef) {
213
- const key = serviceRef.toLowerCase();
214
- const loaded = await currentSnapshot();
215
- if (loaded.rows === null) {
216
- return indeterminate(
217
- serviceRef,
218
- loaded.failure ?? "oracle-unreachable",
219
- null,
220
- freshnessWindowSeconds,
221
- );
222
- }
223
- const row = loaded.rows.get(key);
224
- if (row === undefined) {
225
- return indeterminate(
226
- serviceRef,
227
- "service-not-listed",
228
- null,
229
- freshnessWindowSeconds,
230
- );
231
- }
232
- const lastCheckedAt = row.lastCheckedAt ?? null;
233
- const ageSeconds =
234
- lastCheckedAt === null
235
- ? null
236
- : Math.max(0, Math.round((now() - lastCheckedAt) / 1000));
237
-
238
- if (row.status === "unverified") {
239
- return indeterminate(
240
- serviceRef,
241
- "no-fresh-evidence",
242
- lastCheckedAt,
243
- freshnessWindowSeconds,
244
- ageSeconds,
245
- );
246
- }
247
- // The oracle dated its own verdict; if it cannot be shown to be recent it
248
- // is not evidence of anything, in EITHER direction.
249
- if (ageSeconds === null || ageSeconds > freshnessWindowSeconds) {
250
- return indeterminate(
251
- serviceRef,
252
- "verdict-stale",
253
- lastCheckedAt,
254
- freshnessWindowSeconds,
255
- ageSeconds,
256
- );
257
- }
258
- return {
259
- serviceRef,
260
- verdict: row.status === "failing" ? "failing" : "delivering",
261
- reason: row.status === "failing" ? "fresh-failing" : "fresh-delivering",
262
- lastCheckedAt,
263
- ageSeconds,
264
- freshnessWindowSeconds,
265
- };
266
- },
267
- };
268
- }
269
-
270
- function indeterminate(
271
- serviceRef: string,
272
- reason: DeliveryVerdictReason,
273
- lastCheckedAt: number | null,
274
- freshnessWindowSeconds: number,
275
- ageSeconds: number | null = null,
276
- ): DeliveryAssessment {
277
- return {
278
- serviceRef,
279
- verdict: "indeterminate",
280
- reason,
281
- lastCheckedAt,
282
- ageSeconds,
283
- freshnessWindowSeconds,
284
- };
285
- }
@@ -1,35 +0,0 @@
1
- #!/usr/bin/env node
2
- import { startMetrikFacilitatorServerFromEnv } from "./facilitator-server.js";
3
-
4
- /**
5
- * Runnable entrypoint for Metrik's x402 facilitator (`metrik-x402-facilitator`).
6
- * All configuration comes from the environment; see
7
- * {@link parseMetrikFacilitatorEnvConfig}. Refuses to start without a
8
- * settlement relayer unless `METRIK_X402_ALLOW_VERIFY_ONLY=true`.
9
- */
10
- async function main(): Promise<void> {
11
- const server = await startMetrikFacilitatorServerFromEnv();
12
- const host = process.env.METRIK_X402_HOST ?? "0.0.0.0";
13
- console.error(
14
- `[metrik-x402-facilitator] listening on http://${host}:${server.boundPort}`,
15
- );
16
-
17
- const shutdown = (signal: string) => {
18
- console.error(
19
- `[metrik-x402-facilitator] ${signal} received — shutting down`,
20
- );
21
- server
22
- .close()
23
- .then(() => process.exit(0))
24
- .catch(() => process.exit(1));
25
- };
26
- process.on("SIGINT", () => shutdown("SIGINT"));
27
- process.on("SIGTERM", () => shutdown("SIGTERM"));
28
- }
29
-
30
- main().catch((error) => {
31
- console.error(
32
- error instanceof Error ? error.message : "failed to start x402 facilitator",
33
- );
34
- process.exit(1);
35
- });