@absol-labs/agent 0.11.0 → 0.12.0-rc.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.
Files changed (350) hide show
  1. package/README.md +42 -50
  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.js +1 -1
  12. package/dist/cli/commands/claim.js.map +1 -1
  13. package/dist/cli/commands/close.d.ts.map +1 -1
  14. package/dist/cli/commands/close.js +35 -3
  15. package/dist/cli/commands/close.js.map +1 -1
  16. package/dist/cli/commands/invoke.d.ts +2 -0
  17. package/dist/cli/commands/invoke.d.ts.map +1 -1
  18. package/dist/cli/commands/invoke.js +2 -2
  19. package/dist/cli/commands/invoke.js.map +1 -1
  20. package/dist/cli/commands/mcp.d.ts.map +1 -1
  21. package/dist/cli/commands/mcp.js +15 -2
  22. package/dist/cli/commands/mcp.js.map +1 -1
  23. package/dist/cli/commands/open.d.ts.map +1 -1
  24. package/dist/cli/commands/open.js +58 -4
  25. package/dist/cli/commands/open.js.map +1 -1
  26. package/dist/cli/commands/pay.d.ts +19 -0
  27. package/dist/cli/commands/pay.d.ts.map +1 -0
  28. package/dist/cli/commands/pay.js +355 -0
  29. package/dist/cli/commands/pay.js.map +1 -0
  30. package/dist/cli/commands/reclaim-unverified.d.ts.map +1 -1
  31. package/dist/cli/commands/reclaim-unverified.js +3 -2
  32. package/dist/cli/commands/reclaim-unverified.js.map +1 -1
  33. package/dist/cli/commands/reclaim.d.ts +23 -2
  34. package/dist/cli/commands/reclaim.d.ts.map +1 -1
  35. package/dist/cli/commands/reclaim.js +184 -16
  36. package/dist/cli/commands/reclaim.js.map +1 -1
  37. package/dist/cli/commands/refund.d.ts +13 -0
  38. package/dist/cli/commands/refund.d.ts.map +1 -0
  39. package/dist/cli/commands/refund.js +224 -0
  40. package/dist/cli/commands/refund.js.map +1 -0
  41. package/dist/cli/commands/status.d.ts.map +1 -1
  42. package/dist/cli/commands/status.js +10 -1
  43. package/dist/cli/commands/status.js.map +1 -1
  44. package/dist/cli/commands/streams.js +8 -12
  45. package/dist/cli/commands/streams.js.map +1 -1
  46. package/dist/cli/commands/watch.d.ts.map +1 -1
  47. package/dist/cli/commands/watch.js +2 -1
  48. package/dist/cli/commands/watch.js.map +1 -1
  49. package/dist/cli/config.d.ts +44 -26
  50. package/dist/cli/config.d.ts.map +1 -1
  51. package/dist/cli/config.js +88 -38
  52. package/dist/cli/config.js.map +1 -1
  53. package/dist/cli/deps.d.ts +33 -0
  54. package/dist/cli/deps.d.ts.map +1 -1
  55. package/dist/cli/deps.js +37 -1
  56. package/dist/cli/deps.js.map +1 -1
  57. package/dist/cli/errors.d.ts.map +1 -1
  58. package/dist/cli/errors.js +21 -1
  59. package/dist/cli/errors.js.map +1 -1
  60. package/dist/cli/escape-hatch.d.ts +6 -4
  61. package/dist/cli/escape-hatch.d.ts.map +1 -1
  62. package/dist/cli/escape-hatch.js +6 -2
  63. package/dist/cli/escape-hatch.js.map +1 -1
  64. package/dist/cli/listings.d.ts +18 -0
  65. package/dist/cli/listings.d.ts.map +1 -1
  66. package/dist/cli/listings.js +22 -12
  67. package/dist/cli/listings.js.map +1 -1
  68. package/dist/cli/preflight.d.ts +20 -14
  69. package/dist/cli/preflight.d.ts.map +1 -1
  70. package/dist/cli/preflight.js +34 -20
  71. package/dist/cli/preflight.js.map +1 -1
  72. package/dist/cli/privy.d.ts.map +1 -1
  73. package/dist/cli/privy.js +4 -2
  74. package/dist/cli/privy.js.map +1 -1
  75. package/dist/cli/reclaimability.d.ts +82 -0
  76. package/dist/cli/reclaimability.d.ts.map +1 -0
  77. package/dist/cli/reclaimability.js +80 -0
  78. package/dist/cli/reclaimability.js.map +1 -0
  79. package/dist/cli/run.d.ts +2 -1
  80. package/dist/cli/run.d.ts.map +1 -1
  81. package/dist/cli/run.js +40 -11
  82. package/dist/cli/run.js.map +1 -1
  83. package/dist/cli/scan-range.d.ts +2 -2
  84. package/dist/cli/scan-range.js +2 -2
  85. package/dist/cli/status.d.ts +3 -1
  86. package/dist/cli/status.d.ts.map +1 -1
  87. package/dist/cli/status.js +15 -3
  88. package/dist/cli/status.js.map +1 -1
  89. package/dist/cli/wallet.d.ts +1 -1
  90. package/dist/cli/wallet.js +2 -2
  91. package/dist/cli/wallet.js.map +1 -1
  92. package/dist/discovery/rate-band.d.ts +74 -0
  93. package/dist/discovery/rate-band.d.ts.map +1 -0
  94. package/dist/discovery/rate-band.js +114 -0
  95. package/dist/discovery/rate-band.js.map +1 -0
  96. package/dist/escrow-generation.d.ts +81 -0
  97. package/dist/escrow-generation.d.ts.map +1 -0
  98. package/dist/escrow-generation.js +76 -0
  99. package/dist/escrow-generation.js.map +1 -0
  100. package/dist/frameworks/agentkit.d.ts +8 -4
  101. package/dist/frameworks/agentkit.d.ts.map +1 -1
  102. package/dist/frameworks/agentkit.js +27 -10
  103. package/dist/frameworks/agentkit.js.map +1 -1
  104. package/dist/frameworks/eliza.d.ts +3 -3
  105. package/dist/frameworks/eliza.d.ts.map +1 -1
  106. package/dist/frameworks/eliza.js +15 -6
  107. package/dist/frameworks/eliza.js.map +1 -1
  108. package/dist/frameworks/langchain.d.ts.map +1 -1
  109. package/dist/frameworks/langchain.js +8 -0
  110. package/dist/frameworks/langchain.js.map +1 -1
  111. package/dist/gateway/caller-auth-gateway.d.ts +83 -31
  112. package/dist/gateway/caller-auth-gateway.d.ts.map +1 -1
  113. package/dist/gateway/caller-auth-gateway.js +180 -27
  114. package/dist/gateway/caller-auth-gateway.js.map +1 -1
  115. package/dist/gateway/http-server.d.ts +111 -44
  116. package/dist/gateway/http-server.d.ts.map +1 -1
  117. package/dist/gateway/http-server.js +439 -155
  118. package/dist/gateway/http-server.js.map +1 -1
  119. package/dist/gateway/probe-auth.d.ts +112 -0
  120. package/dist/gateway/probe-auth.d.ts.map +1 -0
  121. package/dist/gateway/probe-auth.js +127 -0
  122. package/dist/gateway/probe-auth.js.map +1 -0
  123. package/dist/gateway/signed-rate-bounds.d.ts +51 -0
  124. package/dist/gateway/signed-rate-bounds.d.ts.map +1 -0
  125. package/dist/gateway/signed-rate-bounds.js +94 -0
  126. package/dist/gateway/signed-rate-bounds.js.map +1 -0
  127. package/dist/gateway/x402-opener-mode.d.ts +215 -0
  128. package/dist/gateway/x402-opener-mode.d.ts.map +1 -0
  129. package/dist/gateway/x402-opener-mode.js +639 -0
  130. package/dist/gateway/x402-opener-mode.js.map +1 -0
  131. package/dist/index.d.ts +6 -2
  132. package/dist/index.d.ts.map +1 -1
  133. package/dist/index.js +10 -5
  134. package/dist/index.js.map +1 -1
  135. package/dist/mcp/server.d.ts +52 -1
  136. package/dist/mcp/server.d.ts.map +1 -1
  137. package/dist/mcp/server.js +228 -37
  138. package/dist/mcp/server.js.map +1 -1
  139. package/dist/mcp/x402-tools.d.ts +104 -0
  140. package/dist/mcp/x402-tools.d.ts.map +1 -0
  141. package/dist/mcp/x402-tools.js +434 -0
  142. package/dist/mcp/x402-tools.js.map +1 -0
  143. package/dist/network.d.ts +119 -0
  144. package/dist/network.d.ts.map +1 -0
  145. package/dist/network.js +199 -0
  146. package/dist/network.js.map +1 -0
  147. package/dist/sdk/invoke.d.ts +4 -0
  148. package/dist/sdk/invoke.d.ts.map +1 -1
  149. package/dist/sdk/invoke.js +2 -5
  150. package/dist/sdk/invoke.js.map +1 -1
  151. package/dist/wallet/autonomous-wallet-broker.d.ts +3 -0
  152. package/dist/wallet/autonomous-wallet-broker.d.ts.map +1 -1
  153. package/dist/wallet/autonomous-wallet-broker.js +27 -23
  154. package/dist/wallet/autonomous-wallet-broker.js.map +1 -1
  155. package/dist/wallet/autonomous-wallet-protocol.d.ts +8 -2
  156. package/dist/wallet/autonomous-wallet-protocol.d.ts.map +1 -1
  157. package/dist/wallet/autonomous-wallet-protocol.js +9 -2
  158. package/dist/wallet/autonomous-wallet-protocol.js.map +1 -1
  159. package/dist/wallet/autonomous-wallet.d.ts.map +1 -1
  160. package/dist/wallet/autonomous-wallet.js +3 -2
  161. package/dist/wallet/autonomous-wallet.js.map +1 -1
  162. package/dist/wallet/broker-escrows.d.ts +222 -0
  163. package/dist/wallet/broker-escrows.d.ts.map +1 -0
  164. package/dist/wallet/broker-escrows.js +36 -0
  165. package/dist/wallet/broker-escrows.js.map +1 -0
  166. package/dist/wallet/lifecycle.d.ts +21 -5
  167. package/dist/wallet/lifecycle.d.ts.map +1 -1
  168. package/dist/wallet/lifecycle.js +32 -12
  169. package/dist/wallet/lifecycle.js.map +1 -1
  170. package/dist/wallet/privy-broker-server.d.ts.map +1 -1
  171. package/dist/wallet/privy-broker-server.js +31 -26
  172. package/dist/wallet/privy-broker-server.js.map +1 -1
  173. package/dist/wallet/privy-session-broker.d.ts +14 -5
  174. package/dist/wallet/privy-session-broker.d.ts.map +1 -1
  175. package/dist/wallet/privy-session-broker.js +29 -17
  176. package/dist/wallet/privy-session-broker.js.map +1 -1
  177. package/dist/wallet/privy-session-provider.d.ts +3 -1
  178. package/dist/wallet/privy-session-provider.d.ts.map +1 -1
  179. package/dist/wallet/privy-session-provider.js +5 -3
  180. package/dist/wallet/privy-session-provider.js.map +1 -1
  181. package/dist/wallet/provider.d.ts.map +1 -1
  182. package/dist/wallet/provider.js +15 -8
  183. package/dist/wallet/provider.js.map +1 -1
  184. package/dist/x402/facilitator.d.ts +3 -30
  185. package/dist/x402/facilitator.d.ts.map +1 -1
  186. package/dist/x402/facilitator.js +0 -27
  187. package/dist/x402/facilitator.js.map +1 -1
  188. package/dist/x402-buyer/guarded-fetch.d.ts +51 -0
  189. package/dist/x402-buyer/guarded-fetch.d.ts.map +1 -0
  190. package/dist/x402-buyer/guarded-fetch.js +147 -0
  191. package/dist/x402-buyer/guarded-fetch.js.map +1 -0
  192. package/dist/x402-buyer/index.d.ts +10 -0
  193. package/dist/x402-buyer/index.d.ts.map +1 -0
  194. package/dist/x402-buyer/index.js +10 -0
  195. package/dist/x402-buyer/index.js.map +1 -0
  196. package/dist/x402-buyer/pay.d.ts +135 -0
  197. package/dist/x402-buyer/pay.d.ts.map +1 -0
  198. package/dist/x402-buyer/pay.js +336 -0
  199. package/dist/x402-buyer/pay.js.map +1 -0
  200. package/dist/x402-opener/abi.d.ts +684 -0
  201. package/dist/x402-opener/abi.d.ts.map +1 -0
  202. package/dist/x402-opener/abi.js +880 -0
  203. package/dist/x402-opener/abi.js.map +1 -0
  204. package/dist/x402-opener/facilitator-entry.d.ts +3 -0
  205. package/dist/x402-opener/facilitator-entry.d.ts.map +1 -0
  206. package/dist/x402-opener/facilitator-entry.js +37 -0
  207. package/dist/x402-opener/facilitator-entry.js.map +1 -0
  208. package/dist/x402-opener/facilitator-server.d.ts +180 -0
  209. package/dist/x402-opener/facilitator-server.d.ts.map +1 -0
  210. package/dist/x402-opener/facilitator-server.js +696 -0
  211. package/dist/x402-opener/facilitator-server.js.map +1 -0
  212. package/dist/x402-opener/index.d.ts +14 -0
  213. package/dist/x402-opener/index.d.ts.map +1 -0
  214. package/dist/x402-opener/index.js +14 -0
  215. package/dist/x402-opener/index.js.map +1 -0
  216. package/dist/x402-opener/listing-resolver.d.ts +55 -0
  217. package/dist/x402-opener/listing-resolver.d.ts.map +1 -0
  218. package/dist/x402-opener/listing-resolver.js +125 -0
  219. package/dist/x402-opener/listing-resolver.js.map +1 -0
  220. package/dist/x402-opener/refund-relay.d.ts +240 -0
  221. package/dist/x402-opener/refund-relay.d.ts.map +1 -0
  222. package/dist/x402-opener/refund-relay.js +1240 -0
  223. package/dist/x402-opener/refund-relay.js.map +1 -0
  224. package/dist/x402-opener/relayer-queue.d.ts +14 -0
  225. package/dist/x402-opener/relayer-queue.d.ts.map +1 -0
  226. package/dist/x402-opener/relayer-queue.js +18 -0
  227. package/dist/x402-opener/relayer-queue.js.map +1 -0
  228. package/dist/x402-opener/scheme.d.ts +217 -0
  229. package/dist/x402-opener/scheme.d.ts.map +1 -0
  230. package/dist/x402-opener/scheme.js +749 -0
  231. package/dist/x402-opener/scheme.js.map +1 -0
  232. package/dist/x402-opener/terms.d.ts +105 -0
  233. package/dist/x402-opener/terms.d.ts.map +1 -0
  234. package/dist/x402-opener/terms.js +212 -0
  235. package/dist/x402-opener/terms.js.map +1 -0
  236. package/dist/zktls/reclaim.d.ts +2 -2
  237. package/dist/zktls/t2-delivery-proof.d.ts +6 -6
  238. package/docs/agent-layer.md +3 -2
  239. package/docs/autonomous-privy-wallet.md +188 -0
  240. package/docs/cli.md +184 -31
  241. package/docs/langchain.md +5 -0
  242. package/docs/mcp-reference.md +75 -5
  243. package/docs/probe-capability.md +191 -0
  244. package/docs/quickstart.md +36 -1
  245. package/docs/threat-model.md +20 -0
  246. package/docs/x402.md +628 -595
  247. package/package.json +19 -17
  248. package/src/capability/probe-capability.ts +355 -0
  249. package/src/cli/args.ts +29 -0
  250. package/src/cli/commands/claim.ts +1 -1
  251. package/src/cli/commands/close.ts +36 -3
  252. package/src/cli/commands/invoke.ts +5 -2
  253. package/src/cli/commands/mcp.ts +18 -2
  254. package/src/cli/commands/open.ts +75 -4
  255. package/src/cli/commands/pay.ts +442 -0
  256. package/src/cli/commands/reclaim-unverified.ts +8 -2
  257. package/src/cli/commands/reclaim.ts +252 -14
  258. package/src/cli/commands/refund.ts +277 -0
  259. package/src/cli/commands/status.ts +17 -1
  260. package/src/cli/commands/streams.ts +10 -14
  261. package/src/cli/commands/watch.ts +7 -1
  262. package/src/cli/config.ts +139 -52
  263. package/src/cli/deps.ts +74 -1
  264. package/src/cli/errors.ts +24 -1
  265. package/src/cli/escape-hatch.ts +19 -5
  266. package/src/cli/listings.ts +43 -10
  267. package/src/cli/preflight.ts +36 -21
  268. package/src/cli/privy.ts +4 -2
  269. package/src/cli/reclaimability.ts +154 -0
  270. package/src/cli/run.ts +54 -11
  271. package/src/cli/scan-range.ts +2 -2
  272. package/src/cli/status.ts +14 -1
  273. package/src/cli/wallet.ts +2 -2
  274. package/src/discovery/rate-band.ts +172 -0
  275. package/src/escrow-generation.ts +135 -0
  276. package/src/frameworks/agentkit.ts +45 -11
  277. package/src/frameworks/eliza.ts +27 -5
  278. package/src/frameworks/langchain.ts +14 -0
  279. package/src/gateway/caller-auth-gateway.ts +320 -68
  280. package/src/gateway/http-server.ts +629 -220
  281. package/src/gateway/probe-auth.ts +191 -0
  282. package/src/gateway/signed-rate-bounds.ts +164 -0
  283. package/src/gateway/x402-opener-mode.ts +974 -0
  284. package/src/index.ts +82 -19
  285. package/src/mcp/server.ts +319 -42
  286. package/src/mcp/x402-tools.ts +621 -0
  287. package/src/network.ts +302 -0
  288. package/src/sdk/invoke.ts +6 -5
  289. package/src/wallet/autonomous-wallet-broker.ts +46 -20
  290. package/src/wallet/autonomous-wallet-protocol.ts +11 -2
  291. package/src/wallet/autonomous-wallet.ts +4 -2
  292. package/src/wallet/broker-escrows.ts +66 -0
  293. package/src/wallet/lifecycle.ts +53 -18
  294. package/src/wallet/privy-broker-server.ts +35 -25
  295. package/src/wallet/privy-session-broker.ts +58 -25
  296. package/src/wallet/privy-session-provider.ts +7 -3
  297. package/src/wallet/provider.ts +20 -15
  298. package/src/x402/facilitator.ts +3 -43
  299. package/src/x402-buyer/guarded-fetch.ts +213 -0
  300. package/src/x402-buyer/index.ts +9 -0
  301. package/src/x402-buyer/pay.ts +517 -0
  302. package/src/x402-opener/abi.ts +881 -0
  303. package/src/x402-opener/facilitator-entry.ts +45 -0
  304. package/src/x402-opener/facilitator-server.ts +1014 -0
  305. package/src/x402-opener/index.ts +13 -0
  306. package/src/x402-opener/listing-resolver.ts +202 -0
  307. package/src/x402-opener/refund-relay.ts +1743 -0
  308. package/src/x402-opener/relayer-queue.ts +18 -0
  309. package/src/x402-opener/scheme.ts +1133 -0
  310. package/src/x402-opener/terms.ts +300 -0
  311. package/dist/gateway/x402-mode.d.ts +0 -123
  312. package/dist/gateway/x402-mode.d.ts.map +0 -1
  313. package/dist/gateway/x402-mode.js +0 -212
  314. package/dist/gateway/x402-mode.js.map +0 -1
  315. package/dist/x402/delivery-oracle.d.ts +0 -108
  316. package/dist/x402/delivery-oracle.d.ts.map +0 -1
  317. package/dist/x402/delivery-oracle.js +0 -174
  318. package/dist/x402/delivery-oracle.js.map +0 -1
  319. package/dist/x402/facilitator-server-entry.d.ts +0 -3
  320. package/dist/x402/facilitator-server-entry.d.ts.map +0 -1
  321. package/dist/x402/facilitator-server-entry.js +0 -27
  322. package/dist/x402/facilitator-server-entry.js.map +0 -1
  323. package/dist/x402/facilitator-server.d.ts +0 -73
  324. package/dist/x402/facilitator-server.d.ts.map +0 -1
  325. package/dist/x402/facilitator-server.js +0 -305
  326. package/dist/x402/facilitator-server.js.map +0 -1
  327. package/dist/x402/payment-requirements.d.ts +0 -106
  328. package/dist/x402/payment-requirements.d.ts.map +0 -1
  329. package/dist/x402/payment-requirements.js +0 -138
  330. package/dist/x402/payment-requirements.js.map +0 -1
  331. package/dist/x402/relayer.d.ts +0 -41
  332. package/dist/x402/relayer.d.ts.map +0 -1
  333. package/dist/x402/relayer.js +0 -64
  334. package/dist/x402/relayer.js.map +0 -1
  335. package/dist/x402/resource-index.d.ts +0 -75
  336. package/dist/x402/resource-index.d.ts.map +0 -1
  337. package/dist/x402/resource-index.js +0 -126
  338. package/dist/x402/resource-index.js.map +0 -1
  339. package/dist/x402/verified-delivery-facilitator.d.ts +0 -285
  340. package/dist/x402/verified-delivery-facilitator.d.ts.map +0 -1
  341. package/dist/x402/verified-delivery-facilitator.js +0 -423
  342. package/dist/x402/verified-delivery-facilitator.js.map +0 -1
  343. package/src/gateway/x402-mode.ts +0 -350
  344. package/src/x402/delivery-oracle.ts +0 -285
  345. package/src/x402/facilitator-server-entry.ts +0 -35
  346. package/src/x402/facilitator-server.ts +0 -445
  347. package/src/x402/payment-requirements.ts +0 -231
  348. package/src/x402/relayer.ts +0 -122
  349. package/src/x402/resource-index.ts +0 -228
  350. package/src/x402/verified-delivery-facilitator.ts +0 -744
@@ -18,6 +18,30 @@ export async function resolveListing(
18
18
  serviceRef: `0x${string}`,
19
19
  options: { readonly required: boolean },
20
20
  ): Promise<ServiceListing | null> {
21
+ return (await resolveListingRecord(context, serviceRef, options)).listing;
22
+ }
23
+
24
+ /**
25
+ * What {@link resolveListing} found, including the verified signed record of a
26
+ * matching row even when that row is not usable as a listing (no signed
27
+ * invocation URL) — `open` still has to read the operator-signed rate band off
28
+ * it — and whether the registry was unreachable (the only case in which a
29
+ * signed band may exist and be unknown).
30
+ */
31
+ export interface ListingRecordResolution {
32
+ readonly listing: ServiceListing | null;
33
+ readonly record: {
34
+ readonly serviceRef: string;
35
+ readonly signed: ServiceListing["signed"];
36
+ } | null;
37
+ readonly registryUnreachable: boolean;
38
+ }
39
+
40
+ export async function resolveListingRecord(
41
+ context: CommandContext,
42
+ serviceRef: `0x${string}`,
43
+ options: { readonly required: boolean },
44
+ ): Promise<ListingRecordResolution> {
21
45
  const { config, out, deps } = context;
22
46
  let services;
23
47
  try {
@@ -32,7 +56,7 @@ export async function resolveListing(
32
56
  out.note(
33
57
  `warning: the registry at ${config.registryUrl} is unreachable; continuing without a verified listing.`,
34
58
  );
35
- return null;
59
+ return { listing: null, record: null, registryUnreachable: true };
36
60
  }
37
61
  throw new CliError(
38
62
  `the service registry at ${config.registryUrl} could not be read`,
@@ -45,7 +69,9 @@ export async function resolveListing(
45
69
  (service) => service.serviceRef.toLowerCase() === wanted,
46
70
  );
47
71
  if (match === undefined) {
48
- if (!options.required) return null;
72
+ if (!options.required) {
73
+ return { listing: null, record: null, registryUnreachable: false };
74
+ }
49
75
  throw new CliError(
50
76
  `no verified listing in the registry matches serviceRef ${serviceRef}`,
51
77
  {
@@ -54,8 +80,11 @@ export async function resolveListing(
54
80
  },
55
81
  );
56
82
  }
83
+ const record = { serviceRef: match.serviceRef, signed: match.signed };
57
84
  if (match.accessUrl === null) {
58
- if (!options.required) return null;
85
+ if (!options.required) {
86
+ return { listing: null, record, registryUnreachable: false };
87
+ }
59
88
  throw new CliError(
60
89
  `listing ${serviceRef} carries no signed invocation URL`,
61
90
  {
@@ -66,12 +95,16 @@ export async function resolveListing(
66
95
  }
67
96
 
68
97
  return {
69
- serviceRef: match.serviceRef,
70
- operator: match.operator,
71
- publicUrl: match.publicUrl,
72
- access: match.access,
73
- accessUrl: match.accessUrl,
74
- signed: match.signed,
75
- category: match.category,
98
+ listing: {
99
+ serviceRef: match.serviceRef,
100
+ operator: match.operator,
101
+ publicUrl: match.publicUrl,
102
+ access: match.access,
103
+ accessUrl: match.accessUrl,
104
+ signed: match.signed,
105
+ category: match.category,
106
+ },
107
+ record,
108
+ registryUnreachable: false,
76
109
  };
77
110
  }
@@ -1,14 +1,10 @@
1
+ import type { MetrikNetwork } from "@absol-labs/sdk";
2
+
3
+ import { gasFundingHint, usdcFundingHint } from "../network.js";
1
4
  import { formatUsdc } from "./amounts.js";
2
5
  import { CliError } from "./errors.js";
3
6
  import type { CliReadClient } from "./deps.js";
4
7
 
5
- /** Where a Base Sepolia test wallet gets its gas. */
6
- export const BASE_SEPOLIA_FAUCET_URL =
7
- "https://www.alchemy.com/faucets/base-sepolia";
8
-
9
- /** Where a Base Sepolia test wallet gets the USDC it escrows. */
10
- export const CIRCLE_USDC_FAUCET_URL = "https://faucet.circle.com";
11
-
12
8
  /**
13
9
  * Refuse to broadcast from a wallet that cannot pay for gas.
14
10
  *
@@ -16,20 +12,29 @@ export const CIRCLE_USDC_FAUCET_URL = "https://faucet.circle.com";
16
12
  * phrasing — `USDC approve transaction failed: Execution reverted with reason:
17
13
  * gas required exceeds allowance (0)` — which reads like a USDC allowance
18
14
  * problem and sends the reader looking at approvals. The actual fact is "this
19
- * address holds no ETH", and it is knowable with one read before anything is
15
+ * address holds no gas", and it is knowable with one read before anything is
20
16
  * signed.
21
17
  *
18
+ * The gas token is read from the network's registry metadata, never assumed to
19
+ * be ETH. Where gas IS the settlement token (`gasToken.isSettlementToken`),
20
+ * there is only one balance: the USDC balance is the gas balance, so that is
21
+ * the one read.
22
+ *
22
23
  * Deliberately only a ZERO check, not an estimate: predicting the gas a hire
23
- * will cost means simulating it, and a wallet with a little ETH that still runs
24
+ * will cost means simulating it, and a wallet with a little gas that still runs
24
25
  * short should get the real revert rather than a guess from this CLI.
25
26
  */
26
27
  export async function assertCanPayGas(
27
28
  read: CliReadClient,
28
29
  address: `0x${string}`,
30
+ network: MetrikNetwork,
29
31
  ): Promise<void> {
32
+ const gasIsUsdc = network.gasToken.isSettlementToken;
30
33
  let balance: bigint;
31
34
  try {
32
- balance = await read.nativeBalance(address);
35
+ balance = gasIsUsdc
36
+ ? await read.usdcBalance(address)
37
+ : await read.nativeBalance(address);
33
38
  } catch {
34
39
  // A balance read is a courtesy, not a gate. If the RPC will not answer it,
35
40
  // let the command proceed and fail with the real error.
@@ -38,10 +43,12 @@ export async function assertCanPayGas(
38
43
  if (balance > 0n) return;
39
44
 
40
45
  throw new CliError(
41
- `wallet ${address} has 0 ETH on Base Sepolia, so it cannot pay for gas`,
46
+ `wallet ${address} has 0 ${network.gasToken.symbol} on ${network.name}, so it cannot pay for gas`,
42
47
  {
43
48
  exitCode: 3,
44
- hint: `fund it at ${BASE_SEPOLIA_FAUCET_URL}, then re-run. (Gas is ETH; the stream itself is escrowed in USDC, which is a separate balance.)`,
49
+ hint: gasIsUsdc
50
+ ? `${gasFundingHint(network)}, then re-run. (On ${network.name} gas is paid in USDC, the same balance a stream is escrowed from.)`
51
+ : `${gasFundingHint(network)}, then re-run. (Gas is ${network.gasToken.symbol}; the stream itself is escrowed in USDC, which is a separate balance.)`,
45
52
  },
46
53
  );
47
54
  }
@@ -49,12 +56,16 @@ export async function assertCanPayGas(
49
56
  /**
50
57
  * Refuse to broadcast a hire the wallet cannot fund.
51
58
  *
52
- * The sibling of `assertCanPayGas`, for the OTHER balance. Gas is ETH and the
53
- * deposit is USDC: they are separate balances on separate tokens, so a wallet
54
- * that passes the gas check can still hold no USDC at all. Without this the
55
- * `open` path sends the USDC approval FIRST — which succeeds, because approving
56
- * an amount you do not hold is legal — and then reverts inside `transferFrom`,
57
- * having already spent gas to learn something that was one `balanceOf` away.
59
+ * The sibling of `assertCanPayGas`, for the OTHER balance. On a network whose
60
+ * gas token is not USDC, gas and deposit are separate balances on separate
61
+ * tokens, so a wallet that passes the gas check can still hold no USDC at all.
62
+ * Without this the `open` path sends the USDC approval FIRST — which succeeds,
63
+ * because approving an amount you do not hold is legal — and then reverts
64
+ * inside `transferFrom`, having already spent gas to learn something that was
65
+ * one `balanceOf` away.
66
+ *
67
+ * Where gas IS USDC there is one balance, and it must cover the deposit AND
68
+ * leave something for gas, so the check is strict (`balance > budget`).
58
69
  *
59
70
  * Deliberately NOT an allowance check. The CLI's own open path grants the
60
71
  * bounded approval it needs (`approveTxHash` in the result), so a missing
@@ -66,7 +77,9 @@ export async function assertCanFundStream(
66
77
  read: CliReadClient,
67
78
  address: `0x${string}`,
68
79
  budgetUsdc: bigint,
80
+ network: MetrikNetwork,
69
81
  ): Promise<void> {
82
+ const gasIsUsdc = network.gasToken.isSettlementToken;
70
83
  let balance: bigint;
71
84
  try {
72
85
  balance = await read.usdcBalance(address);
@@ -76,13 +89,15 @@ export async function assertCanFundStream(
76
89
  // the real error surface.
77
90
  return;
78
91
  }
79
- if (balance >= budgetUsdc) return;
92
+ if (gasIsUsdc ? balance > budgetUsdc : balance >= budgetUsdc) return;
80
93
 
81
94
  throw new CliError(
82
- `wallet ${address} holds ${formatUsdc(balance)} but --budget is ${formatUsdc(budgetUsdc)}`,
95
+ `wallet ${address} holds ${formatUsdc(balance)} but --budget is ${formatUsdc(budgetUsdc)}${gasIsUsdc ? " (plus gas, which is also USDC here)" : ""}`,
83
96
  {
84
97
  exitCode: 3,
85
- hint: `the deposit is escrowed in USDC — fund the wallet at ${CIRCLE_USDC_FAUCET_URL} (Base Sepolia), then re-run. (Gas is a separate ETH balance.)`,
98
+ hint: gasIsUsdc
99
+ ? `the deposit and the gas are both USDC on ${network.name} — ${usdcFundingHint(network)}, then re-run.`
100
+ : `the deposit is escrowed in USDC — ${usdcFundingHint(network)}, then re-run. (Gas is a separate ${network.gasToken.symbol} balance.)`,
86
101
  },
87
102
  );
88
103
  }
package/src/cli/privy.ts CHANGED
@@ -18,6 +18,8 @@ import {
18
18
  type AutonomousWalletRecord,
19
19
  } from "../wallet/autonomous-wallet.js";
20
20
  import { createPrivySessionProvider } from "../wallet/privy-session-provider.js";
21
+ import { PRIVY_BROKER_CHAIN_ID } from "../wallet/privy-session-broker.js";
22
+ import { defaultNetwork } from "../network.js";
21
23
  import {
22
24
  createPrivyEmbeddedAccount,
23
25
  type PrivyEip1193Provider,
@@ -357,12 +359,12 @@ async function embeddedAccount(
357
359
  appId,
358
360
  address,
359
361
  provider,
360
- chainId: 84532,
362
+ chainId: PRIVY_BROKER_CHAIN_ID,
361
363
  });
362
364
  } catch (error) {
363
365
  throw configError(
364
366
  `the Privy wallet could not be prepared: ${describe(error)}`,
365
- `check ${PRIVY_ENV_VARS.join(", ")}. Metrik Privy wallets are Base Sepolia (84532) only.`,
367
+ `check ${PRIVY_ENV_VARS.join(", ")}. Metrik Privy wallets are on the broker's network only (${defaultNetwork().name}, chain ${PRIVY_BROKER_CHAIN_ID}).`,
366
368
  );
367
369
  }
368
370
  }
@@ -0,0 +1,154 @@
1
+ import type { StreamV2 } from "@absol-labs/sdk";
2
+
3
+ import { formatDuration } from "./amounts.js";
4
+ import { formatTimestamp, readChainNow } from "./escape-hatch.js";
5
+ import { settlementCoverage } from "./status.js";
6
+ import type { CliReadClient } from "./deps.js";
7
+
8
+ /**
9
+ * Why a proof-based reclaim cannot go through right now, read from chain state
10
+ * rather than decoded out of a revert.
11
+ *
12
+ * `metrik reclaim` used to learn this only by sending the simulation and
13
+ * reading what came back. That works exactly as well as the endpoint's revert
14
+ * data does, and on the public Base Sepolia RPC it frequently does not work at
15
+ * all: an already-reclaimed stream, a stream that is still running, and a
16
+ * stream whose stop time is not checkpointed yet ALL came back as
17
+ *
18
+ * reclaim simulation failed: Execution reverted for an unknown reason.
19
+ *
20
+ * Three different situations, three completely different next actions — you
21
+ * already have the money / close it first / wait ~20 seconds — and one message
22
+ * that distinguishes none of them. Worse, the retry loop keys off the same
23
+ * undecoded revert, so the wait that this command advertises never engaged.
24
+ *
25
+ * Every one of these conditions is a pure function of state the CLI already
26
+ * reads elsewhere (`getStreamV2`, `latestCheckpointCheckedAt`, the chain clock,
27
+ * and {@link settlementCoverage}, which `metrik status` has always used to
28
+ * decide the same question). So the condition is named from that state, before
29
+ * a transaction is built, the way `reclaim-unverified` has always named
30
+ * "already reclaimed". No revert data required.
31
+ */
32
+ export type ReclaimBlocker =
33
+ /** `AlreadyReclaimed()` / `NothingToReclaim()`: the money is already back. */
34
+ | { readonly kind: "reclaimed" }
35
+ /** `StreamNotReclaimable()`: neither closed nor expired, so the clock runs. */
36
+ | {
37
+ readonly kind: "running";
38
+ readonly expiresAt: number;
39
+ readonly secondsRemaining: number;
40
+ }
41
+ /**
42
+ * `SettlementWindowOpen()`: the stream has stopped but no checkpoint reaches
43
+ * its stop time yet. The ONLY one of the three that is a wait rather than a
44
+ * refusal — the oracle publishes roughly every 15-22s.
45
+ */
46
+ | {
47
+ readonly kind: "awaiting-checkpoint";
48
+ readonly stopAt: number;
49
+ readonly coveredUpTo: number | null;
50
+ };
51
+
52
+ /**
53
+ * The blocker implied by this state, or `null` when nothing in the state blocks
54
+ * a reclaim. Pure, so every branch is testable without an RPC — the same reason
55
+ * {@link settlementCoverage} is.
56
+ *
57
+ * Order matters and follows the escrow: a reclaimed stream is terminal, a
58
+ * running stream cannot be reclaimed at any settlement state, and only then
59
+ * does checkpoint coverage decide.
60
+ */
61
+ export function classifyReclaimBlocker(
62
+ stream: Pick<StreamV2, "status" | "closedAt" | "expiresAt" | "reclaimed">,
63
+ latestCheckpointCheckedAt: number | null,
64
+ nowSeconds: number,
65
+ ): ReclaimBlocker | null {
66
+ if (stream.reclaimed) return { kind: "reclaimed" };
67
+ if (stream.status !== "closed" && nowSeconds < stream.expiresAt) {
68
+ return {
69
+ kind: "running",
70
+ expiresAt: stream.expiresAt,
71
+ secondsRemaining: stream.expiresAt - nowSeconds,
72
+ };
73
+ }
74
+ const coverage = settlementCoverage(
75
+ stream,
76
+ latestCheckpointCheckedAt,
77
+ nowSeconds,
78
+ );
79
+ if (coverage.pending && coverage.stopAt !== null) {
80
+ return {
81
+ kind: "awaiting-checkpoint",
82
+ stopAt: coverage.stopAt,
83
+ coveredUpTo: coverage.coveredUpTo,
84
+ };
85
+ }
86
+ return null;
87
+ }
88
+
89
+ /**
90
+ * Read the state the classifier needs and name the blocker.
91
+ *
92
+ * **Fails open.** A read that does not answer returns `null` — "nothing known
93
+ * to block this" — never a refusal. Refusing to return a buyer's money because
94
+ * a load-balanced endpoint would not answer a view call is the one outcome this
95
+ * command must never produce; the transaction itself remains the authority.
96
+ */
97
+ export async function readReclaimBlocker(
98
+ read: Pick<
99
+ CliReadClient,
100
+ "getStreamV2" | "latestCheckpointCheckedAt" | "latestBlockTimestamp"
101
+ >,
102
+ streamId: `0x${string}`,
103
+ fallbackNowSeconds: number,
104
+ ): Promise<ReclaimBlocker | null> {
105
+ const stream = await read.getStreamV2(streamId).catch(() => null);
106
+ if (stream === null) return null;
107
+ const now = await readChainNow(read, fallbackNowSeconds);
108
+ const latestCheckpointCheckedAt = await read
109
+ .latestCheckpointCheckedAt()
110
+ .catch(() => null);
111
+ return classifyReclaimBlocker(stream, latestCheckpointCheckedAt, now.seconds);
112
+ }
113
+
114
+ /**
115
+ * The refusal for a blocker that will not clear on its own, as message + hint.
116
+ *
117
+ * `awaiting-checkpoint` has no entry here on purpose: it clears by itself, so
118
+ * it is narrated by {@link describeAwaitingCheckpoint} and waited out, never
119
+ * raised as an error.
120
+ */
121
+ export function describeTerminalBlocker(
122
+ blocker: ReclaimBlocker,
123
+ streamId: `0x${string}`,
124
+ ): { readonly message: string; readonly hint: string } | null {
125
+ if (blocker.kind === "reclaimed") {
126
+ return {
127
+ message:
128
+ "this stream has already been reclaimed — there is nothing left to take back",
129
+ hint: `check where it went: metrik status ${streamId}`,
130
+ };
131
+ }
132
+ if (blocker.kind === "running") {
133
+ return {
134
+ message: `the stream is still running, so there is nothing to reclaim yet — the meter stops at ${formatTimestamp(
135
+ blocker.expiresAt,
136
+ )}, in ${formatDuration(blocker.secondsRemaining)}`,
137
+ hint: `stop the clock first — \`metrik close ${streamId}\`, or re-run this command with --close-first — or wait for it to expire. (If you closed it moments ago, the RPC endpoint may not have caught up yet: re-run this command.)`,
138
+ };
139
+ }
140
+ return null;
141
+ }
142
+
143
+ /** The wait, named: what stopped, how far the oracle has published. */
144
+ export function describeAwaitingCheckpoint(
145
+ blocker: Extract<ReclaimBlocker, { kind: "awaiting-checkpoint" }>,
146
+ ): string {
147
+ const covered =
148
+ blocker.coveredUpTo === null
149
+ ? "how far the oracle has published could not be read"
150
+ : `the oracle has published only to ${formatTimestamp(blocker.coveredUpTo)}`;
151
+ return `the stream stopped at ${formatTimestamp(
152
+ blocker.stopAt,
153
+ )} and ${covered}. Reclaim reverts until a checkpoint reaches the stop time — the oracle publishes roughly every 15-22s, so this waits.`;
154
+ }
package/src/cli/run.ts CHANGED
@@ -1,16 +1,27 @@
1
- import { assertKnownFlags, booleanFlag, parseArgs } from "./args.js";
1
+ import {
2
+ assertKnownFlags,
3
+ booleanFlag,
4
+ jsonOutputRequested,
5
+ parseArgs,
6
+ } from "./args.js";
2
7
  import {
3
8
  CONFIG_FLAGS,
4
9
  GLOBAL_FLAGS,
5
- TESTNET_CHAIN_IDS,
10
+ CLI_NETWORK_POLICY,
6
11
  resolveConfig,
7
12
  } from "./config.js";
13
+ import { describeAllowedNetworks } from "../network.js";
8
14
  import { CliError, describeError, usageError } from "./errors.js";
9
15
  import { createPrinter, type CliStreams } from "./output.js";
10
16
  import { startHeartbeat } from "./progress.js";
11
17
  import { defaultCliDependencies, type CliDependencies } from "./deps.js";
12
18
  import { packageVersion } from "./version.js";
13
- import { CDP_ENV_VARS, PRIVATE_KEY_ENV } from "./wallet.js";
19
+ import {
20
+ CDP_ENV_VARS,
21
+ PRIVATE_KEY_ENV,
22
+ PRIVY_SESSION_ENV_VARS,
23
+ PRIVY_WALLET_KEY_ENV,
24
+ } from "./wallet.js";
14
25
  import type { CommandSpec } from "./context.js";
15
26
 
16
27
  import { claimCommand } from "./commands/claim.js";
@@ -19,15 +30,39 @@ import { discoverCommand } from "./commands/discover.js";
19
30
  import { invokeCommand } from "./commands/invoke.js";
20
31
  import { mcpCommand } from "./commands/mcp.js";
21
32
  import { openCommand } from "./commands/open.js";
33
+ import { payCommand } from "./commands/pay.js";
22
34
  import { reclaimCommand } from "./commands/reclaim.js";
35
+ import { refundCommand } from "./commands/refund.js";
23
36
  import { reclaimUnverifiedCommand } from "./commands/reclaim-unverified.js";
24
37
  import { statusCommand } from "./commands/status.js";
25
38
  import { streamsCommand } from "./commands/streams.js";
26
39
  import { watchCommand } from "./commands/watch.js";
27
40
 
41
+ /**
42
+ * Every wallet source, named in the order they are resolved. Privy shipped in
43
+ * 0.11.0 as resolution step 3 and worked, but appeared in no user-facing text
44
+ * at all — so the only way to learn the CLI could use it was to read the
45
+ * source or the docs. An external reviewer running the published package
46
+ * concluded it did not exist.
47
+ */
48
+ const WALLET_SOURCES =
49
+ `${PRIVATE_KEY_ENV}, ${CDP_ENV_VARS.join(" + ")}, ` +
50
+ `or ${PRIVY_WALLET_KEY_ENV} (Privy; or a broker session via ${PRIVY_SESSION_ENV_VARS.join(" + ")}).`;
51
+
52
+ /**
53
+ * Said in the help, not only discovered at runtime. `claim` is the OPERATOR's
54
+ * side and a Privy broker policy allowlists BUYER actions only, so a Privy
55
+ * wallet cannot sign one — the broker refuses, and the client cannot raise its
56
+ * own ceiling. The command already warns when it runs; someone choosing a
57
+ * wallet needs it before that.
58
+ */
59
+ const PRIVY_CLAIM_NOTE =
60
+ "`metrik claim` does NOT work on a Privy wallet: the broker's transaction allowlist is buyer-only. Claim with the operator's own key.";
61
+
28
62
  /**
29
63
  * The command surface, in the order the product loop runs:
30
- * discover -> open -> invoke -> watch/status -> close -> reclaim, then the
64
+ * discover -> open (or pay, over x402) -> invoke -> watch/status -> close ->
65
+ * reclaim (or refund, through an x402 seller's gasless relay), then the
31
66
  * operator's own redemption (`claim`) and the buyer's last-resort exit
32
67
  * (`reclaim-unverified`). `streams` (which of them are mine?) sits inside that
33
68
  * loop; `mcp` (run the same loop from an MCP host) sits after it.
@@ -35,12 +70,14 @@ import { watchCommand } from "./commands/watch.js";
35
70
  export const COMMANDS: readonly CommandSpec[] = [
36
71
  discoverCommand,
37
72
  openCommand,
73
+ payCommand,
38
74
  statusCommand,
39
75
  streamsCommand,
40
76
  watchCommand,
41
77
  invokeCommand,
42
78
  closeCommand,
43
79
  reclaimCommand,
80
+ refundCommand,
44
81
  reclaimUnverifiedCommand,
45
82
  claimCommand,
46
83
  mcpCommand,
@@ -69,7 +106,7 @@ export async function runCli(options: RunCliOptions): Promise<number> {
69
106
  let json = false;
70
107
  try {
71
108
  const args = parseArgs(options.argv);
72
- json = booleanFlag(args, "json");
109
+ json = jsonOutputRequested(args);
73
110
  const out = createPrinter(streams, json);
74
111
 
75
112
  // `--version` is checked before the bare-invocation help branch: `metrik
@@ -160,12 +197,13 @@ export function renderHelp(commandName?: string): string {
160
197
  ...(command.signs
161
198
  ? [
162
199
  "",
163
- `Broadcasts a transaction. Requires a wallet in the environment: ${PRIVATE_KEY_ENV}, or ${CDP_ENV_VARS.join(" + ")}.`,
200
+ `Broadcasts a transaction. Requires a wallet in the environment: ${WALLET_SOURCES}`,
201
+ ...(command.name === "claim" ? [PRIVY_CLAIM_NOTE] : []),
164
202
  ]
165
203
  : command.requiresKey === true
166
204
  ? [
167
205
  "",
168
- `Broadcasts nothing, but signs an off-chain EIP-712 capability, so it requires a wallet in the environment: ${PRIVATE_KEY_ENV}, or ${CDP_ENV_VARS.join(" + ")}.`,
206
+ `Broadcasts nothing, but signs an off-chain EIP-712 capability, so it requires a wallet in the environment: ${WALLET_SOURCES}`,
169
207
  ]
170
208
  : []),
171
209
  ].join("\n");
@@ -194,16 +232,21 @@ export function renderHelp(commandName?: string): string {
194
232
  " --json machine-readable output on stdout",
195
233
  " --help this text, or `metrik <command> --help`",
196
234
  "",
197
- "Settlement flags (all default to Metrik's Base Sepolia deployment):",
198
- " --chain-id --rpc-url --escrow --usdc --oracle-url --registry-url",
235
+ "Settlement flags (all default to the selected network's registry entry):",
236
+ " --network --chain-id --rpc-url --escrow --usdc --oracle-url --registry-url",
237
+ " --network takes a registry id, chain id or CAIP-2 id (env METRIK_NETWORK).",
199
238
  "",
200
239
  "Wallet (read from the environment only; never accepted as a flag):",
201
- ` ${PRIVATE_KEY_ENV} a Base Sepolia test key. Checked first.`,
240
+ ` ${PRIVATE_KEY_ENV} a testnet key. Checked first.`,
202
241
  ` ${CDP_ENV_VARS.join(", ")}`,
203
242
  " a Coinbase CDP Server Wallet v2 account. Used when no private key is set.",
243
+ ` ${PRIVY_WALLET_KEY_ENV}`,
244
+ " a Privy wallet you provisioned, reached through a broker. Checked third.",
245
+ ` A broker session instead: ${PRIVY_SESSION_ENV_VARS.join(", ")}.`,
246
+ ` ${PRIVY_CLAIM_NOTE}`,
204
247
  " A credential on the command line lands in shell history; these never are.",
205
248
  "",
206
- `TESTNET ONLY: this CLI refuses any chain outside ${TESTNET_CHAIN_IDS.join(", ")} (Base Sepolia),`,
249
+ `TESTNET ONLY: this CLI refuses any chain outside the registered testnets: ${describeAllowedNetworks(CLI_NETWORK_POLICY)},`,
207
250
  "and re-checks the chain id the RPC actually serves before signing. There is no override.",
208
251
  ].join("\n");
209
252
  }
@@ -19,14 +19,14 @@
19
19
  * `buyer` and `operator` are INDEXED on `StreamOpened`, so a per-wallet scan is
20
20
  * a server-side topic filter whose response is tiny regardless of range. What
21
21
  * public RPCs cap is the per-call BLOCK RANGE, and a topic filter does not lift
22
- * it: `sepolia.base.org` answers a wider range with `-32614 "eth_getLogs is
22
+ * it: the public RPC answers a wider range with `-32614 "eth_getLogs is
23
23
  * limited to a 10,000 range"`. So the page size is the cap, not a guess below
24
24
  * it.
25
25
  */
26
26
 
27
27
  /**
28
28
  * getLogs page size. The window is `chunk + 1` blocks (bounds inclusive), so
29
- * 9 999 is the largest page `sepolia.base.org` accepts.
29
+ * 9 999 is the largest page the public RPC accepts.
30
30
  */
31
31
  export const LOG_SCAN_CHUNK = 9_999n;
32
32
 
package/src/cli/status.ts CHANGED
@@ -126,6 +126,8 @@ export async function readStreamReport(
126
126
  read: CliReadClient,
127
127
  streamId: `0x${string}`,
128
128
  nowSeconds: number,
129
+ /** The escrow's generation (decides the escape-hatch rule). Default `v2`. */
130
+ escrowGeneration = "v2",
129
131
  ): Promise<StreamReport> {
130
132
  const stream = await read.getStreamV2(streamId);
131
133
 
@@ -164,6 +166,7 @@ export async function readStreamReport(
164
166
  stream,
165
167
  await read.escapeGraceSeconds(),
166
168
  now.seconds,
169
+ escrowGeneration,
167
170
  );
168
171
  } catch {
169
172
  escapeHatch = null;
@@ -208,6 +211,11 @@ export function streamReportJson(report: StreamReport): unknown {
208
211
  status: stream.status,
209
212
  reclaimed: stream.reclaimed,
210
213
  buyer: stream.buyer,
214
+ // An x402 stream's `buyer` is the X402StreamOpener contract, not the
215
+ // person who paid; `payer` is who any refund goes to.
216
+ opener: stream.opener ?? null,
217
+ payer: stream.payer ?? stream.buyer,
218
+ boughtVia: (stream.opener ?? null) === null ? "wallet" : "x402-opener",
211
219
  operator: stream.operator,
212
220
  serviceRef: stream.serviceRef,
213
221
  deposit: stream.deposit,
@@ -275,7 +283,12 @@ export function streamReportRows(
275
283
  "status",
276
284
  stream.reclaimed ? `${stream.status} (reclaimed)` : stream.status,
277
285
  ],
278
- ["buyer", stream.buyer],
286
+ ...(stream.opener === null || stream.opener === undefined
287
+ ? ([["buyer", stream.buyer]] as const)
288
+ : ([
289
+ ["buyer", `${stream.buyer} (x402 opener contract)`],
290
+ ["payer", `${stream.payer} (refunds go here)`],
291
+ ] as const)),
279
292
  ["operator", stream.operator],
280
293
  ["serviceRef", stream.serviceRef],
281
294
  ["deposit", formatUsdc(stream.deposit)],
package/src/cli/wallet.ts CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  *
25
25
  * ## Resolution order (first match wins, checked in this order)
26
26
  *
27
- * 1. `METRIK_AGENT_PRIVATE_KEY` — a raw Base Sepolia test key.
27
+ * 1. `METRIK_AGENT_PRIVATE_KEY` — a raw testnet key.
28
28
  * 2. The `CDP_*` / `METRIK_AGENT_CDP_*` quartet — a Coinbase CDP Server Wallet
29
29
  * v2 account, resolved through this package's own `resolveAgentWallet()`.
30
30
  * 3. The `METRIK_AGENT_PRIVY_*` variables — a Privy wallet reached through a
@@ -123,7 +123,7 @@ export interface CliWallet {
123
123
  }
124
124
 
125
125
  const WALLET_HINT =
126
- `export ${PRIVATE_KEY_ENV}=0x… (a funded Base Sepolia test key), configure a Coinbase CDP wallet with ` +
126
+ `export ${PRIVATE_KEY_ENV}=0x… (a funded testnet key), configure a Coinbase CDP wallet with ` +
127
127
  `${CDP_ENV_VARS.join(", ")}, or configure a Privy wallet with ${PRIVY_WALLET_KEY_ENV} (agent-owned) or ` +
128
128
  `${PRIVY_SESSION_ENV_VARS.join(", ")} (broker session). All are read from the environment only — never pass a credential as a command-line flag.`;
129
129