@absol-labs/agent 0.9.1 → 0.10.0

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 (199) hide show
  1. package/README.md +77 -3
  2. package/dist/cli/amounts.d.ts +26 -0
  3. package/dist/cli/amounts.d.ts.map +1 -0
  4. package/dist/cli/amounts.js +71 -0
  5. package/dist/cli/amounts.js.map +1 -0
  6. package/dist/cli/args.d.ts +32 -0
  7. package/dist/cli/args.d.ts.map +1 -0
  8. package/dist/cli/args.js +119 -0
  9. package/dist/cli/args.js.map +1 -0
  10. package/dist/cli/attesters.d.ts +51 -0
  11. package/dist/cli/attesters.d.ts.map +1 -0
  12. package/dist/cli/attesters.js +58 -0
  13. package/dist/cli/attesters.js.map +1 -0
  14. package/dist/cli/bin.d.ts +3 -0
  15. package/dist/cli/bin.d.ts.map +1 -0
  16. package/dist/cli/bin.js +5 -0
  17. package/dist/cli/bin.js.map +1 -0
  18. package/dist/cli/commands/claim.d.ts +13 -0
  19. package/dist/cli/commands/claim.d.ts.map +1 -0
  20. package/dist/cli/commands/claim.js +86 -0
  21. package/dist/cli/commands/claim.js.map +1 -0
  22. package/dist/cli/commands/close.d.ts +3 -0
  23. package/dist/cli/commands/close.d.ts.map +1 -0
  24. package/dist/cli/commands/close.js +43 -0
  25. package/dist/cli/commands/close.js.map +1 -0
  26. package/dist/cli/commands/discover.d.ts +3 -0
  27. package/dist/cli/commands/discover.d.ts.map +1 -0
  28. package/dist/cli/commands/discover.js +183 -0
  29. package/dist/cli/commands/discover.js.map +1 -0
  30. package/dist/cli/commands/invoke.d.ts +3 -0
  31. package/dist/cli/commands/invoke.d.ts.map +1 -0
  32. package/dist/cli/commands/invoke.js +141 -0
  33. package/dist/cli/commands/invoke.js.map +1 -0
  34. package/dist/cli/commands/mcp.d.ts +3 -0
  35. package/dist/cli/commands/mcp.d.ts.map +1 -0
  36. package/dist/cli/commands/mcp.js +113 -0
  37. package/dist/cli/commands/mcp.js.map +1 -0
  38. package/dist/cli/commands/open.d.ts +3 -0
  39. package/dist/cli/commands/open.d.ts.map +1 -0
  40. package/dist/cli/commands/open.js +279 -0
  41. package/dist/cli/commands/open.js.map +1 -0
  42. package/dist/cli/commands/reclaim-unverified.d.ts +3 -0
  43. package/dist/cli/commands/reclaim-unverified.d.ts.map +1 -0
  44. package/dist/cli/commands/reclaim-unverified.js +164 -0
  45. package/dist/cli/commands/reclaim-unverified.js.map +1 -0
  46. package/dist/cli/commands/reclaim.d.ts +33 -0
  47. package/dist/cli/commands/reclaim.d.ts.map +1 -0
  48. package/dist/cli/commands/reclaim.js +297 -0
  49. package/dist/cli/commands/reclaim.js.map +1 -0
  50. package/dist/cli/commands/status.d.ts +3 -0
  51. package/dist/cli/commands/status.d.ts.map +1 -0
  52. package/dist/cli/commands/status.js +43 -0
  53. package/dist/cli/commands/status.js.map +1 -0
  54. package/dist/cli/commands/streams.d.ts +3 -0
  55. package/dist/cli/commands/streams.d.ts.map +1 -0
  56. package/dist/cli/commands/streams.js +257 -0
  57. package/dist/cli/commands/streams.js.map +1 -0
  58. package/dist/cli/commands/watch.d.ts +3 -0
  59. package/dist/cli/commands/watch.d.ts.map +1 -0
  60. package/dist/cli/commands/watch.js +55 -0
  61. package/dist/cli/commands/watch.js.map +1 -0
  62. package/dist/cli/config.d.ts +61 -0
  63. package/dist/cli/config.d.ts.map +1 -0
  64. package/dist/cli/config.js +140 -0
  65. package/dist/cli/config.js.map +1 -0
  66. package/dist/cli/context.d.ts +29 -0
  67. package/dist/cli/context.d.ts.map +1 -0
  68. package/dist/cli/context.js +2 -0
  69. package/dist/cli/context.js.map +1 -0
  70. package/dist/cli/deps.d.ts +145 -0
  71. package/dist/cli/deps.d.ts.map +1 -0
  72. package/dist/cli/deps.js +227 -0
  73. package/dist/cli/deps.js.map +1 -0
  74. package/dist/cli/descriptor.d.ts +61 -0
  75. package/dist/cli/descriptor.d.ts.map +1 -0
  76. package/dist/cli/descriptor.js +78 -0
  77. package/dist/cli/descriptor.js.map +1 -0
  78. package/dist/cli/errors.d.ts +48 -0
  79. package/dist/cli/errors.d.ts.map +1 -0
  80. package/dist/cli/errors.js +129 -0
  81. package/dist/cli/errors.js.map +1 -0
  82. package/dist/cli/escape-hatch.d.ts +50 -0
  83. package/dist/cli/escape-hatch.d.ts.map +1 -0
  84. package/dist/cli/escape-hatch.js +45 -0
  85. package/dist/cli/escape-hatch.js.map +1 -0
  86. package/dist/cli/index.d.ts +8 -0
  87. package/dist/cli/index.d.ts.map +1 -0
  88. package/dist/cli/index.js +7 -0
  89. package/dist/cli/index.js.map +1 -0
  90. package/dist/cli/listings.d.ts +15 -0
  91. package/dist/cli/listings.d.ts.map +1 -0
  92. package/dist/cli/listings.js +56 -0
  93. package/dist/cli/listings.js.map +1 -0
  94. package/dist/cli/mandate.d.ts +66 -0
  95. package/dist/cli/mandate.d.ts.map +1 -0
  96. package/dist/cli/mandate.js +61 -0
  97. package/dist/cli/mandate.js.map +1 -0
  98. package/dist/cli/output.d.ts +33 -0
  99. package/dist/cli/output.d.ts.map +1 -0
  100. package/dist/cli/output.js +34 -0
  101. package/dist/cli/output.js.map +1 -0
  102. package/dist/cli/preflight.d.ts +38 -0
  103. package/dist/cli/preflight.d.ts.map +1 -0
  104. package/dist/cli/preflight.js +72 -0
  105. package/dist/cli/preflight.js.map +1 -0
  106. package/dist/cli/progress.d.ts +36 -0
  107. package/dist/cli/progress.d.ts.map +1 -0
  108. package/dist/cli/progress.js +40 -0
  109. package/dist/cli/progress.js.map +1 -0
  110. package/dist/cli/run.d.ts +25 -0
  111. package/dist/cli/run.d.ts.map +1 -0
  112. package/dist/cli/run.js +173 -0
  113. package/dist/cli/run.js.map +1 -0
  114. package/dist/cli/scan-range.d.ts +55 -0
  115. package/dist/cli/scan-range.d.ts.map +1 -0
  116. package/dist/cli/scan-range.js +57 -0
  117. package/dist/cli/scan-range.js.map +1 -0
  118. package/dist/cli/status.d.ts +90 -0
  119. package/dist/cli/status.d.ts.map +1 -0
  120. package/dist/cli/status.js +252 -0
  121. package/dist/cli/status.js.map +1 -0
  122. package/dist/cli/timeout.d.ts +30 -0
  123. package/dist/cli/timeout.d.ts.map +1 -0
  124. package/dist/cli/timeout.js +55 -0
  125. package/dist/cli/timeout.js.map +1 -0
  126. package/dist/cli/version.d.ts +12 -0
  127. package/dist/cli/version.d.ts.map +1 -0
  128. package/dist/cli/version.js +22 -0
  129. package/dist/cli/version.js.map +1 -0
  130. package/dist/cli/wallet.d.ts +95 -0
  131. package/dist/cli/wallet.d.ts.map +1 -0
  132. package/dist/cli/wallet.js +169 -0
  133. package/dist/cli/wallet.js.map +1 -0
  134. package/dist/frameworks/crewai.d.ts +10 -0
  135. package/dist/frameworks/crewai.d.ts.map +1 -1
  136. package/dist/frameworks/crewai.js +12 -2
  137. package/dist/frameworks/crewai.js.map +1 -1
  138. package/dist/gateway/server-entry.d.ts +1 -0
  139. package/dist/gateway/server-entry.js +1 -0
  140. package/dist/gateway/server-entry.js.map +1 -1
  141. package/dist/mcp/http-server.d.ts +1 -0
  142. package/dist/mcp/http-server.js +1 -0
  143. package/dist/mcp/http-server.js.map +1 -1
  144. package/dist/mcp/server.d.ts.map +1 -1
  145. package/dist/mcp/server.js +20 -1
  146. package/dist/mcp/server.js.map +1 -1
  147. package/dist/mcp/stdio.d.ts +1 -0
  148. package/dist/mcp/stdio.js +1 -0
  149. package/dist/mcp/stdio.js.map +1 -1
  150. package/dist/sdk/client.d.ts +25 -1
  151. package/dist/sdk/client.d.ts.map +1 -1
  152. package/dist/sdk/client.js +20 -1
  153. package/dist/sdk/client.js.map +1 -1
  154. package/docs/agent-layer.md +2 -1
  155. package/docs/cli.md +569 -0
  156. package/docs/crewai.md +6 -2
  157. package/docs/eliza.md +6 -1
  158. package/docs/mcp-hosted.md +3 -2
  159. package/docs/quickstart.md +18 -5
  160. package/package.json +17 -5
  161. package/src/cli/amounts.ts +102 -0
  162. package/src/cli/args.ts +150 -0
  163. package/src/cli/attesters.ts +86 -0
  164. package/src/cli/bin.ts +5 -0
  165. package/src/cli/commands/claim.ts +100 -0
  166. package/src/cli/commands/close.ts +53 -0
  167. package/src/cli/commands/discover.ts +228 -0
  168. package/src/cli/commands/invoke.ts +177 -0
  169. package/src/cli/commands/mcp.ts +135 -0
  170. package/src/cli/commands/open.ts +331 -0
  171. package/src/cli/commands/reclaim-unverified.ts +206 -0
  172. package/src/cli/commands/reclaim.ts +360 -0
  173. package/src/cli/commands/status.ts +56 -0
  174. package/src/cli/commands/streams.ts +344 -0
  175. package/src/cli/commands/watch.ts +70 -0
  176. package/src/cli/config.ts +208 -0
  177. package/src/cli/context.ts +30 -0
  178. package/src/cli/deps.ts +437 -0
  179. package/src/cli/descriptor.ts +133 -0
  180. package/src/cli/errors.ts +162 -0
  181. package/src/cli/escape-hatch.ts +92 -0
  182. package/src/cli/index.ts +27 -0
  183. package/src/cli/listings.ts +77 -0
  184. package/src/cli/mandate.ts +126 -0
  185. package/src/cli/output.ts +63 -0
  186. package/src/cli/preflight.ts +88 -0
  187. package/src/cli/progress.ts +75 -0
  188. package/src/cli/run.ts +209 -0
  189. package/src/cli/scan-range.ts +76 -0
  190. package/src/cli/status.ts +381 -0
  191. package/src/cli/timeout.ts +64 -0
  192. package/src/cli/version.ts +21 -0
  193. package/src/cli/wallet.ts +241 -0
  194. package/src/frameworks/crewai.ts +13 -2
  195. package/src/gateway/server-entry.ts +1 -0
  196. package/src/mcp/http-server.ts +1 -0
  197. package/src/mcp/server.ts +25 -1
  198. package/src/mcp/stdio.ts +1 -0
  199. package/src/sdk/client.ts +33 -1
package/README.md CHANGED
@@ -25,7 +25,18 @@ Docs: [metrik.live](https://metrik.live). Frontdoor: [github.com/Absol-Labs](htt
25
25
  pnpm add @absol-labs/agent
26
26
  ```
27
27
 
28
- The package is **ESM-only** (`"type": "module"`) and targets **Node `>=20 <21`**. It ships
28
+ The package ships four executables. Run the CLI with no install at all:
29
+
30
+ ```bash
31
+ npx --package @absol-labs/agent metrik discover
32
+ ```
33
+
34
+ `metrik` is the CLI below; `metrik-mcp` starts the MCP server over stdio
35
+ (`metrik mcp --print-config` prints the client config that runs it);
36
+ `metrik-mcp-http` starts the hosted multi-tenant MCP transport; `metrik-gateway`
37
+ starts the reference caller-auth gateway a seller puts in front of their service.
38
+
39
+ The package is **ESM-only** (`"type": "module"`) and targets **Node `>=20.19 <25`** (verified on 20.19, 22 LTS and 24). It ships
29
40
  `@absol-labs/sdk`, `@absol-labs/shared`, `viem`, `zod`, the Reclaim zkTLS SDKs, the
30
41
  Coinbase CDP SDK, and the MCP SDK as dependencies. The autonomous wallet path also
31
42
  installs a prebuilt native OS-keyring binding; it is
@@ -224,15 +235,78 @@ const { streamId } = await facilitator.open(header); // mandate-gated, opens on-
224
235
  The scheme name is exported as `X402_SCHEME`; `parseX402ChallengeJson`,
225
236
  `parseX402PayloadHeader`, and `encodeX402PayloadHeader` handle the wire format on both sides.
226
237
 
238
+ ### The CLI
239
+
240
+ `metrik` runs the whole product loop from a terminal — the first surface that does.
241
+ Every command is the same mandate-gated SDK path the library exposes; nothing is
242
+ reimplemented.
243
+
244
+ ```bash
245
+ metrik discover --min-tier 2
246
+ metrik open --service <serviceRef> --rate 0.000001 --budget 0.27 --duration 1h [--dry-run]
247
+ metrik status <streamId>
248
+ metrik streams --role buyer --limit 5
249
+ metrik watch <streamId> --interval 15
250
+ metrik invoke <streamId> --method POST --path /transform --body '{"text":"hi"}'
251
+ metrik close <streamId>
252
+ metrik reclaim <streamId>
253
+ metrik mcp --print-config
254
+ ```
255
+
256
+ - **Testnet only.** The CLI refuses any chain outside Base Sepolia (`84532`), and
257
+ re-reads the chain id the RPC endpoint actually serves before it signs anything.
258
+ There is no override flag.
259
+ - **`open --dry-run` needs no wallet at all.** It broadcasts nothing, so it prints the
260
+ intended call (and says which parts it could not produce without a key) rather than
261
+ demanding one. `invoke` does need the key even though it broadcasts nothing: it signs
262
+ an off-chain EIP-712 invocation capability as the stream's buyer.
263
+ - **A wallet with no gas is caught before anything is signed**, with the faucet named,
264
+ instead of surfacing the node's `gas required exceeds allowance (0)`. So is a wallet
265
+ that cannot cover `--budget` in USDC — a separate balance, and otherwise a revert you
266
+ pay for the approval to discover.
267
+ - **Two wallet sources**, resolved in a fixed order: `METRIK_AGENT_PRIVATE_KEY`, then a
268
+ Coinbase CDP Server Wallet v2 account. Both are read from the environment only.
269
+ - **`metrik streams` lists a wallet's streams** from the escrow's indexed `StreamOpened`
270
+ logs, anchored at the deploy block — and reports a partial or untrustworthy scan as
271
+ partial rather than as "you have no streams".
272
+ - **The key is read from `METRIK_AGENT_PRIVATE_KEY` only** — never from a flag, which
273
+ would land it in shell history and process listings. Commands print the derived
274
+ address, never the key.
275
+ - **Settlement config defaults to the live Base Sepolia deployment** and is overridable
276
+ per command with `--rpc-url`, `--escrow`, `--usdc`, `--oracle-url`, `--registry-url`,
277
+ `--chain-id`.
278
+ - **Amounts are decimal USDC by default**; pass `--units atomic` for raw 6-decimal base
279
+ units (what `METRIK_E2E_*` and the escrow use).
280
+ - **`--json`** puts a machine-readable document on stdout; warnings and progress always
281
+ go to stderr, so the stream stays parseable. `metrik watch --json` emits **JSON Lines**
282
+ — one compact object per poll, each parseable on its own line.
283
+ - **`reclaim` waits for you.** `reclaimWithProof` reverts with `SettlementWindowOpen`
284
+ until a checkpoint covering the stream's stop time has posted — in practice that means
285
+ the first attempt fails and one ~30s later succeeds. The CLI retries that one
286
+ condition on a bounded schedule (`--attempts`, `--interval`) and narrates the wait;
287
+ every other failure is surfaced immediately.
288
+ - **Accrual comes from the oracle**, not from the stream tuple: `getStream` reports
289
+ `claimedCumulative`/`settledCumulative` as `0` for a stream's entire active life, so
290
+ `status` and `watch` read `GET /checkpoint/proof` instead.
291
+
292
+ `metrik --help`, or `metrik <command> --help`, prints the full flag surface. The runner
293
+ is also importable (`@absol-labs/agent/cli`) if you want to embed it.
294
+
227
295
  ### Run the MCP server
228
296
 
229
297
  The server runs over stdio and reads its settlement config from the environment; it never
230
298
  asks for a raw key in a tool argument.
231
299
 
232
300
  ```bash
233
- pnpm mcp:stdio # or: pnpm mcp:http
301
+ metrik-mcp # installed, or: npx --package @absol-labs/agent metrik-mcp
302
+ metrik-mcp-http # the hosted, multi-tenant Streamable-HTTP transport
303
+ pnpm mcp:stdio # inside this repo; `pnpm mcp:http` for the hosted transport
234
304
  ```
235
305
 
306
+ `metrik mcp --print-config` prints the `mcpServers` block to paste into Claude Desktop
307
+ (or any MCP client), already carrying every variable the server fails closed without.
308
+ Credentials in it are placeholders: the CLI never prints a key back.
309
+
236
310
  Programmatically:
237
311
 
238
312
  ```ts
@@ -429,7 +503,7 @@ Per-framework guides: [`docs/langchain.md`](./docs/langchain.md),
429
503
 
430
504
  ## Requirements
431
505
 
432
- - Node **`>=20 <21`**, package manager `pnpm@9.15.x`.
506
+ - Node **`>=20.19 <25`** (20.19+, 22 LTS and 24 all supported), package manager `pnpm@9.15.x`.
433
507
  - **ESM only** — this package has no CommonJS build; consume it from an ESM context.
434
508
  - Framework adapters need their matching optional peer dependency installed. Known caveat:
435
509
  the AgentKit adapter pulls a heavy graph (`@coinbase/agentkit` / ZeroDev) that can break a
@@ -0,0 +1,26 @@
1
+ /** Circle USDC — 6 decimals on every deployment Metrik settles against. */
2
+ export declare const USDC_DECIMALS = 6;
3
+ /**
4
+ * How `--budget` / `--rate` are interpreted.
5
+ *
6
+ * - `"usdc"` (default): decimal USDC, e.g. `--budget 0.27`.
7
+ * - `"atomic"`: raw 6-decimal base units, e.g. `--budget 270000`.
8
+ *
9
+ * Both are offered because the two audiences count differently: a human reads
10
+ * USDC, while the escrow, the oracle and every `METRIK_E2E_*` fixture are in
11
+ * atomic units. Guessing from the magnitude of the number would be a silent
12
+ * 1,000,000x mistake on a fund-moving command, so the unit is explicit.
13
+ */
14
+ export type AmountUnits = "usdc" | "atomic";
15
+ export declare function parseAmountUnits(raw: string | undefined): AmountUnits;
16
+ /** Parses a `--budget`/`--rate` value into atomic USDC units. */
17
+ export declare function parseUsdcAmount(raw: string, units: AmountUnits, flagName: string): bigint;
18
+ /** `270000n` -> `"0.27 USDC (270000 atomic)"`. */
19
+ export declare function formatUsdc(atomic: bigint): string;
20
+ /**
21
+ * Parses `--duration`: bare seconds, or a `s`/`m`/`h`/`d` suffix.
22
+ */
23
+ export declare function parseDurationSeconds(raw: string): number;
24
+ /** Seconds -> a compact human duration (`3600` -> `1h`). */
25
+ export declare function formatDuration(seconds: number): string;
26
+ //# sourceMappingURL=amounts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"amounts.d.ts","sourceRoot":"","sources":["../../src/cli/amounts.ts"],"names":[],"mappings":"AAIA,2EAA2E;AAC3E,eAAO,MAAM,aAAa,IAAI,CAAC;AAE/B;;;;;;;;;;GAUG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,QAAQ,CAAC;AAE5C,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,WAAW,CAIrE;AAED,iEAAiE;AACjE,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,MAAM,GACf,MAAM,CAuBR;AAED,kDAAkD;AAClD,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAmBxD;AAED,4DAA4D;AAC5D,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAetD"}
@@ -0,0 +1,71 @@
1
+ import { formatUnits, parseUnits } from "viem";
2
+ import { usageError } from "./errors.js";
3
+ /** Circle USDC — 6 decimals on every deployment Metrik settles against. */
4
+ export const USDC_DECIMALS = 6;
5
+ export function parseAmountUnits(raw) {
6
+ if (raw === undefined || raw === "usdc")
7
+ return "usdc";
8
+ if (raw === "atomic")
9
+ return "atomic";
10
+ throw usageError(`--units must be "usdc" or "atomic" (got "${raw}")`);
11
+ }
12
+ /** Parses a `--budget`/`--rate` value into atomic USDC units. */
13
+ export function parseUsdcAmount(raw, units, flagName) {
14
+ if (units === "atomic") {
15
+ if (!/^\d+$/.test(raw)) {
16
+ throw usageError(`--${flagName} must be a non-negative integer of atomic USDC units (got "${raw}")`);
17
+ }
18
+ return BigInt(raw);
19
+ }
20
+ if (!/^\d+(\.\d+)?$/.test(raw)) {
21
+ throw usageError(`--${flagName} must be a decimal USDC amount (got "${raw}")`, "pass --units atomic to supply raw 6-decimal base units instead.");
22
+ }
23
+ const fraction = raw.split(".")[1];
24
+ if (fraction !== undefined && fraction.length > USDC_DECIMALS) {
25
+ throw usageError(`--${flagName} has more precision than USDC supports (${USDC_DECIMALS} decimals): ${raw}`);
26
+ }
27
+ return parseUnits(raw, USDC_DECIMALS);
28
+ }
29
+ /** `270000n` -> `"0.27 USDC (270000 atomic)"`. */
30
+ export function formatUsdc(atomic) {
31
+ return `${formatUnits(atomic, USDC_DECIMALS)} USDC (${atomic.toString()} atomic)`;
32
+ }
33
+ /**
34
+ * Parses `--duration`: bare seconds, or a `s`/`m`/`h`/`d` suffix.
35
+ */
36
+ export function parseDurationSeconds(raw) {
37
+ const match = /^(\d+)([smhd]?)$/.exec(raw);
38
+ if (match === null) {
39
+ throw usageError(`--duration must be seconds, optionally suffixed s/m/h/d (got "${raw}")`);
40
+ }
41
+ const value = Number(match[1]);
42
+ const multiplier = { "": 1, s: 1, m: 60, h: 3_600, d: 86_400 }[match[2] ?? ""];
43
+ if (multiplier === undefined) {
44
+ throw usageError(`--duration has an unsupported unit: ${raw}`);
45
+ }
46
+ const seconds = value * multiplier;
47
+ if (!Number.isSafeInteger(seconds) || seconds <= 0) {
48
+ throw usageError(`--duration must be a positive number of seconds: ${raw}`);
49
+ }
50
+ return seconds;
51
+ }
52
+ /** Seconds -> a compact human duration (`3600` -> `1h`). */
53
+ export function formatDuration(seconds) {
54
+ if (seconds <= 0)
55
+ return "0s";
56
+ const parts = [];
57
+ let remaining = seconds;
58
+ for (const [unit, size] of [
59
+ ["d", 86_400],
60
+ ["h", 3_600],
61
+ ["m", 60],
62
+ ["s", 1],
63
+ ]) {
64
+ const count = Math.floor(remaining / size);
65
+ if (count > 0)
66
+ parts.push(`${count}${unit}`);
67
+ remaining -= count * size;
68
+ }
69
+ return parts.join(" ");
70
+ }
71
+ //# sourceMappingURL=amounts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"amounts.js","sourceRoot":"","sources":["../../src/cli/amounts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * A deliberately small GNU-style argument parser. The CLI ships no argument
3
+ * parsing dependency: the surface is seven commands with a handful of flags, and
4
+ * the packed tarball should stay as light as the library it wraps.
5
+ *
6
+ * Supported forms: `--flag`, `--flag value`, `--flag=value`, `--no-flag`, and
7
+ * positional arguments. `--` ends flag parsing.
8
+ */
9
+ export interface ParsedArgs {
10
+ readonly command: string | null;
11
+ readonly positionals: readonly string[];
12
+ /**
13
+ * Every occurrence of each flag, in order. Repeats are kept rather than
14
+ * overwritten so `--header` can be given more than once; scalar readers take
15
+ * the last occurrence.
16
+ */
17
+ readonly flags: ReadonlyMap<string, readonly (string | boolean)[]>;
18
+ }
19
+ export declare function parseArgs(argv: readonly string[]): ParsedArgs;
20
+ /** Reads a flag that must carry a string value. */
21
+ export declare function stringFlag(args: ParsedArgs, name: string): string | undefined;
22
+ /** Every occurrence of a repeatable string flag (e.g. `--header`). */
23
+ export declare function stringFlags(args: ParsedArgs, name: string): string[];
24
+ export declare function requiredStringFlag(args: ParsedArgs, name: string): string;
25
+ export declare function booleanFlag(args: ParsedArgs, name: string): boolean;
26
+ export declare function integerFlag(args: ParsedArgs, name: string): number | undefined;
27
+ /**
28
+ * Rejects flags the command does not understand. A silently ignored `--budgt`
29
+ * on a fund-moving command is how someone spends more than they meant to.
30
+ */
31
+ export declare function assertKnownFlags(args: ParsedArgs, known: readonly string[]): void;
32
+ //# sourceMappingURL=args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAEA;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC;CACpE;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,UAAU,CAiD7D;AAQD,mDAAmD;AACnD,wBAAgB,UAAU,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQ7E;AAED,sEAAsE;AACtE,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAQpE;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAMzE;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CASnE;AAED,wBAAgB,WAAW,CACzB,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,SAAS,CAOpB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,IAAI,CAWN"}
@@ -0,0 +1,119 @@
1
+ import { usageError } from "./errors.js";
2
+ export function parseArgs(argv) {
3
+ const positionals = [];
4
+ const flags = new Map();
5
+ const push = (name, value) => {
6
+ const existing = flags.get(name);
7
+ if (existing === undefined)
8
+ flags.set(name, [value]);
9
+ else
10
+ existing.push(value);
11
+ };
12
+ let passthrough = false;
13
+ for (let index = 0; index < argv.length; index++) {
14
+ const token = argv[index];
15
+ if (token === undefined)
16
+ continue;
17
+ if (passthrough || !token.startsWith("--")) {
18
+ positionals.push(token);
19
+ continue;
20
+ }
21
+ if (token === "--") {
22
+ passthrough = true;
23
+ continue;
24
+ }
25
+ const body = token.slice(2);
26
+ const equals = body.indexOf("=");
27
+ if (equals !== -1) {
28
+ const name = body.slice(0, equals);
29
+ assertFlagName(name, token);
30
+ push(name, body.slice(equals + 1));
31
+ continue;
32
+ }
33
+ assertFlagName(body, token);
34
+ if (body.startsWith("no-")) {
35
+ push(body.slice(3), false);
36
+ continue;
37
+ }
38
+ const next = argv[index + 1];
39
+ if (next === undefined || next.startsWith("--")) {
40
+ push(body, true);
41
+ continue;
42
+ }
43
+ push(body, next);
44
+ index++;
45
+ }
46
+ const [command = null, ...rest] = positionals;
47
+ return { command, positionals: rest, flags };
48
+ }
49
+ function assertFlagName(name, token) {
50
+ if (name.length === 0) {
51
+ throw usageError(`not a valid flag: ${token}`);
52
+ }
53
+ }
54
+ /** Reads a flag that must carry a string value. */
55
+ export function stringFlag(args, name) {
56
+ const values = args.flags.get(name);
57
+ if (values === undefined)
58
+ return undefined;
59
+ const value = values[values.length - 1];
60
+ if (typeof value !== "string") {
61
+ throw usageError(`--${name} requires a value`);
62
+ }
63
+ return value;
64
+ }
65
+ /** Every occurrence of a repeatable string flag (e.g. `--header`). */
66
+ export function stringFlags(args, name) {
67
+ const values = args.flags.get(name) ?? [];
68
+ return values.map((value) => {
69
+ if (typeof value !== "string") {
70
+ throw usageError(`--${name} requires a value`);
71
+ }
72
+ return value;
73
+ });
74
+ }
75
+ export function requiredStringFlag(args, name) {
76
+ const value = stringFlag(args, name);
77
+ if (value === undefined) {
78
+ throw usageError(`--${name} is required`);
79
+ }
80
+ return value;
81
+ }
82
+ export function booleanFlag(args, name) {
83
+ const values = args.flags.get(name);
84
+ if (values === undefined)
85
+ return false;
86
+ const value = values[values.length - 1];
87
+ if (value === undefined)
88
+ return false;
89
+ if (typeof value === "boolean")
90
+ return value;
91
+ if (value === "true")
92
+ return true;
93
+ if (value === "false")
94
+ return false;
95
+ throw usageError(`--${name} takes no value (got "${value}")`);
96
+ }
97
+ export function integerFlag(args, name) {
98
+ const raw = stringFlag(args, name);
99
+ if (raw === undefined)
100
+ return undefined;
101
+ if (!/^\d+$/.test(raw)) {
102
+ throw usageError(`--${name} must be a non-negative integer (got "${raw}")`);
103
+ }
104
+ return Number(raw);
105
+ }
106
+ /**
107
+ * Rejects flags the command does not understand. A silently ignored `--budgt`
108
+ * on a fund-moving command is how someone spends more than they meant to.
109
+ */
110
+ export function assertKnownFlags(args, known) {
111
+ const allowed = new Set(known);
112
+ const unknown = [...args.flags.keys()].filter((name) => !allowed.has(name));
113
+ if (unknown.length > 0) {
114
+ throw usageError(`unknown flag${unknown.length > 1 ? "s" : ""}: ${unknown
115
+ .map((name) => `--${name}`)
116
+ .join(", ")}`, `known flags: ${known.map((name) => `--${name}`).join(", ")}`);
117
+ }
118
+ }
119
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import type { CliReadClient } from "./deps.js";
2
+ /**
3
+ * Who attests, read off the escrow.
4
+ *
5
+ * Metrik's whole claim is that money moves only for delivery that a NEUTRAL
6
+ * verifier checked. On the live Base Sepolia deployment that neutrality is
7
+ * partial: `getOracleSigners()` returns three addresses, and one of them is the
8
+ * operator of the listings a buyer is being offered. A tool that prints
9
+ * "cryptographically verified" and "tier 2" without saying so is letting the
10
+ * buyer assume something the deployment does not provide.
11
+ *
12
+ * This is DISCLOSURE, not enforcement. The CLI does not refuse such a listing
13
+ * and does not score it — it states the fact and leaves the judgement to the
14
+ * buyer. Fixing the deployment is a deployment decision, not a CLI one.
15
+ */
16
+ export interface AttesterSet {
17
+ /** The signer set, lowercased. `null` when the escrow would not answer. */
18
+ readonly signers: readonly string[] | null;
19
+ /** Why the set could not be read, when applicable. */
20
+ readonly unavailable: string | null;
21
+ }
22
+ /**
23
+ * Read the signer set, failing SOFT.
24
+ *
25
+ * An unreadable set must never render as "no overlap": "we checked and the
26
+ * operator does not attest" and "we could not check" are different facts, and
27
+ * conflating them would turn an outage into a false assurance. `signers: null`
28
+ * is carried through to every caller and printed as UNKNOWN.
29
+ */
30
+ export declare function readAttesterSet(read: Pick<CliReadClient, "oracleSigners">): Promise<AttesterSet>;
31
+ /**
32
+ * Is this operator also one of the escrow's attesters?
33
+ *
34
+ * `null` means unknown (the set could not be read) and must be rendered as
35
+ * unknown, never as `false`.
36
+ */
37
+ export declare function operatorAttestsOwnDelivery(set: AttesterSet, operator: string): boolean | null;
38
+ /**
39
+ * One factual stderr line, or `null` when there is nothing to disclose.
40
+ *
41
+ * Deliberately not alarming and deliberately not a verdict: it reports a
42
+ * configuration fact and what that fact means, and stops there.
43
+ */
44
+ export declare function attesterDisclosure(set: AttesterSet, operator: string): string | null;
45
+ /**
46
+ * The one warning that belongs to the ESCROW rather than to any one listing:
47
+ * the signer set could not be read at all. Printed once per command, not once
48
+ * per row, and never as "no overlap".
49
+ */
50
+ export declare function attesterSetUnavailableWarning(set: AttesterSet): string | null;
51
+ //# sourceMappingURL=attesters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attesters.d.ts","sourceRoot":"","sources":["../../src/cli/attesters.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,WAAW;IAC1B,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IAC3C,sDAAsD;IACtD,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CACrC;AAED;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,eAAe,CAAC,GACzC,OAAO,CAAC,WAAW,CAAC,CAUtB;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,WAAW,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,GAAG,IAAI,CAGhB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,WAAW,EAChB,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,IAAI,CAMf;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAG7E"}
@@ -0,0 +1,58 @@
1
+ import { describeError } from "./errors.js";
2
+ /**
3
+ * Read the signer set, failing SOFT.
4
+ *
5
+ * An unreadable set must never render as "no overlap": "we checked and the
6
+ * operator does not attest" and "we could not check" are different facts, and
7
+ * conflating them would turn an outage into a false assurance. `signers: null`
8
+ * is carried through to every caller and printed as UNKNOWN.
9
+ */
10
+ export async function readAttesterSet(read) {
11
+ try {
12
+ const signers = await read.oracleSigners();
13
+ return {
14
+ signers: signers.map((signer) => signer.toLowerCase()),
15
+ unavailable: null,
16
+ };
17
+ }
18
+ catch (error) {
19
+ return { signers: null, unavailable: describeError(error) };
20
+ }
21
+ }
22
+ /**
23
+ * Is this operator also one of the escrow's attesters?
24
+ *
25
+ * `null` means unknown (the set could not be read) and must be rendered as
26
+ * unknown, never as `false`.
27
+ */
28
+ export function operatorAttestsOwnDelivery(set, operator) {
29
+ if (set.signers === null)
30
+ return null;
31
+ return set.signers.includes(operator.toLowerCase());
32
+ }
33
+ /**
34
+ * One factual stderr line, or `null` when there is nothing to disclose.
35
+ *
36
+ * Deliberately not alarming and deliberately not a verdict: it reports a
37
+ * configuration fact and what that fact means, and stops there.
38
+ */
39
+ export function attesterDisclosure(set, operator) {
40
+ const overlap = operatorAttestsOwnDelivery(set, operator);
41
+ if (overlap === null)
42
+ return attesterSetUnavailableWarning(set);
43
+ if (!overlap)
44
+ return null;
45
+ const total = set.signers?.length ?? 0;
46
+ return `warning: operator ${operator} is also one of the ${total} oracle signer${total === 1 ? "" : "s"} configured on this escrow. The party being paid is among the parties attesting that delivery happened. Verification here is not independent of the seller.`;
47
+ }
48
+ /**
49
+ * The one warning that belongs to the ESCROW rather than to any one listing:
50
+ * the signer set could not be read at all. Printed once per command, not once
51
+ * per row, and never as "no overlap".
52
+ */
53
+ export function attesterSetUnavailableWarning(set) {
54
+ if (set.signers !== null)
55
+ return null;
56
+ return `warning: the escrow's oracle signer set could not be read (${set.unavailable ?? "no reason given"}) — whether the operator being paid is also one of the parties attesting to its own delivery is UNKNOWN, not "no overlap".`;
57
+ }
58
+ //# sourceMappingURL=attesters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attesters.js","sourceRoot":"","sources":["../../src/cli/attesters.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAwB5C;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAA0C;IAE1C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;QAC3C,OAAO;YACL,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YACtD,WAAW,EAAE,IAAI;SAClB,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;IAC9D,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CACxC,GAAgB,EAChB,QAAgB;IAEhB,IAAI,GAAG,CAAC,OAAO,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACtC,OAAO,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;AACtD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,GAAgB,EAChB,QAAgB;IAEhB,MAAM,OAAO,GAAG,0BAA0B,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IAC1D,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,6BAA6B,CAAC,GAAG,CAAC,CAAC;IAChE,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC,CAAC;IACvC,OAAO,qBAAqB,QAAQ,uBAAuB,KAAK,iBAAiB,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,6JAA6J,CAAC;AACvQ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,6BAA6B,CAAC,GAAgB;IAC5D,IAAI,GAAG,CAAC,OAAO,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACtC,OAAO,8DAA8D,GAAG,CAAC,WAAW,IAAI,iBAAiB,4HAA4H,CAAC;AACxO,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=bin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../../src/cli/bin.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+ import { runCli } from "./run.js";
3
+ const exitCode = await runCli({ argv: process.argv.slice(2) });
4
+ process.exitCode = exitCode;
5
+ //# sourceMappingURL=bin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../../src/cli/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AAC/D,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { CommandSpec } from "../context.js";
2
+ /**
3
+ * The seller's side of the loop.
4
+ *
5
+ * `status` has always computed and printed an `operator claimable` row — a
6
+ * redeemable balance the CLI could see and could not act on. `claimWithProof` is
7
+ * operator-gated on-chain, so this command refuses a wallet that is not the
8
+ * stream's operator BEFORE it broadcasts: an address mismatch is knowable with
9
+ * one read, and letting it become a raw `NotOperator()` revert would cost gas to
10
+ * learn a fact that was already available.
11
+ */
12
+ export declare const claimCommand: CommandSpec;
13
+ //# sourceMappingURL=claim.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claim.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/claim.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAkB,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjE;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,EAAE,WAiF1B,CAAC"}
@@ -0,0 +1,86 @@
1
+ import { formatUsdc } from "../amounts.js";
2
+ import { assertTestnetRpc, parseBytes32 } from "../config.js";
3
+ import { CliError, describeError, usageError } from "../errors.js";
4
+ import { signClaimMandate } from "../mandate.js";
5
+ import { assertCanPayGas } from "../preflight.js";
6
+ import { PRIVATE_KEY_ENV, resolveCliWallet } from "../wallet.js";
7
+ /**
8
+ * The seller's side of the loop.
9
+ *
10
+ * `status` has always computed and printed an `operator claimable` row — a
11
+ * redeemable balance the CLI could see and could not act on. `claimWithProof` is
12
+ * operator-gated on-chain, so this command refuses a wallet that is not the
13
+ * stream's operator BEFORE it broadcasts: an address mismatch is knowable with
14
+ * one read, and letting it become a raw `NotOperator()` revert would cost gas to
15
+ * learn a fact that was already available.
16
+ */
17
+ export const claimCommand = {
18
+ name: "claim",
19
+ summary: "Operator: redeem what this stream has verifiably earned",
20
+ usage: "metrik claim <streamId>",
21
+ flags: [],
22
+ signs: true,
23
+ async run(context) {
24
+ const { args, config, env, out, deps } = context;
25
+ const [streamIdArg] = args.positionals;
26
+ if (streamIdArg === undefined) {
27
+ throw usageError("metrik claim requires a <streamId>");
28
+ }
29
+ const streamId = parseBytes32(streamIdArg, "<streamId>");
30
+ const { account } = await resolveCliWallet(env, { note: out.note });
31
+ const read = deps.createReadClient(config);
32
+ await assertTestnetRpc(config, () => read.getChainId());
33
+ await assertCanPayGas(read, account.address);
34
+ const stream = await read.getStreamV2(streamId);
35
+ if (stream.operator.toLowerCase() !== account.address.toLowerCase()) {
36
+ throw new CliError(`wallet ${account.address} is not the operator of this stream`, {
37
+ exitCode: 3,
38
+ hint: `only ${stream.operator} can claim it — the escrow enforces this, so no other wallet can. Set ${PRIVATE_KEY_ENV} to the operator's key. (The buyer's side of this stream is \`metrik reclaim ${streamId}\`.)`,
39
+ });
40
+ }
41
+ // Accrual lives in the oracle's checkpoint proof, never in the stream tuple.
42
+ // No proof means no claim is possible yet, and saying that is better than
43
+ // paying gas for a `NothingToClaim()` revert.
44
+ let cumulativeAmount;
45
+ try {
46
+ cumulativeAmount = (await read.checkpointProof(streamId))
47
+ .cumulativeAmount;
48
+ }
49
+ catch (error) {
50
+ throw new CliError("no checkpoint covers this stream yet, so there is nothing provable to claim", {
51
+ exitCode: 4,
52
+ hint: `the oracle publishes roughly every 15-22s and pays only for intervals that verified; ${describeError(error)}`,
53
+ cause: error,
54
+ });
55
+ }
56
+ const claimable = await read.claimableForV2(streamId, cumulativeAmount);
57
+ if (claimable === 0n) {
58
+ throw new CliError("this stream has nothing claimable right now", {
59
+ exitCode: 4,
60
+ hint: `everything checkpointed so far has already been claimed. See where it stands: metrik status ${streamId}`,
61
+ });
62
+ }
63
+ const write = deps.createWriteClient(config, account);
64
+ const result = await write.claimStream({
65
+ streamId,
66
+ signedMandate: await signClaimMandate(account, config.chainId),
67
+ spentSoFarUsdc: 0n,
68
+ nowSeconds: deps.nowSeconds(),
69
+ });
70
+ out.result({
71
+ streamId,
72
+ txHash: result.txHash,
73
+ operator: stream.operator,
74
+ claimed: claimable,
75
+ verifiedCumulative: cumulativeAmount,
76
+ });
77
+ out.print("Claimed.");
78
+ out.table([
79
+ ["streamId", streamId],
80
+ ["tx", result.txHash],
81
+ ["operator", stream.operator],
82
+ ["claimed", `${formatUsdc(claimable)} (net of protocol fee)`],
83
+ ]);
84
+ },
85
+ };
86
+ //# sourceMappingURL=claim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claim.js","sourceRoot":"","sources":["../../../src/cli/commands/claim.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { CommandSpec } from "../context.js";
2
+ export declare const closeCommand: CommandSpec;
3
+ //# sourceMappingURL=close.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"close.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/close.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAkB,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjE,eAAO,MAAM,YAAY,EAAE,WA6C1B,CAAC"}