@blackpool/cli 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md ADDED
@@ -0,0 +1,393 @@
1
+ # AGENTS.md — Blackpool for agents
2
+
3
+ Private payments for AI agents and scripts. This file is the complete guide to using Blackpool through its CLI: setup, every command, the JSON each one returns, chains and limits, recipes, integration code, errors and safety rules. Start with the quickstart; everything after it is reference.
4
+
5
+ | | |
6
+ |---|---|
7
+ | This file | https://blackpool.cash/AGENTS.md (plain markdown, no JavaScript, no bot challenge) |
8
+ | Mirrors | https://raw.githubusercontent.com/blackpooldev/blackpool/main/AGENTS.md · inside the npm package at `node_modules/@blackpool/cli/AGENTS.md` |
9
+ | Index for LLMs | https://blackpool.cash/llms.txt |
10
+ | CLI | [`@blackpool/cli`](https://www.npmjs.com/package/@blackpool/cli) — Node.js 18+ |
11
+ | API | `https://api.blackpool.cash` (the CLI calls it for you) |
12
+ | Live status | https://blackpool.cash/status |
13
+ | Human docs | https://blackpool.cash/docs |
14
+
15
+ ---
16
+
17
+ ## 1. Quickstart (60 seconds)
18
+
19
+ ```sh
20
+ npm i -g @blackpool/cli # or prefix every command with: npx -y @blackpool/cli@latest
21
+ export BLACKPOOL_KEY=0x<agent-wallet-private-key> # a dedicated wallet, never your main one
22
+ export BLACKPOOL_JSON=1 # every command prints one JSON object on stdout
23
+
24
+ blackpool address # → {"ok":true,"command":"address","address":"0x…"} fund this address
25
+ blackpool deposit 25 --pool usdc --chain base # shield 25 USDC (the only step that needs gas)
26
+ blackpool send 0xRECIPIENT 10 --pool usdc --chain base # private payment, relayer pays gas
27
+ blackpool balance --chain base --pool usdc # → {"ok":true,…,"balance":"13.965",…}
28
+ ```
29
+
30
+ Paste this into your agent's instructions:
31
+
32
+ ```text
33
+ You can make private crypto payments with the `blackpool` CLI (env BLACKPOOL_KEY and BLACKPOOL_JSON=1 are set).
34
+ Always run commands with a timeout of at least 300 seconds and parse the single JSON object on stdout.
35
+ "ok": true means it succeeded; "ok": false has an "error" to report. Exit code 2 means you called it wrong.
36
+ Before paying, check `blackpool balance --chain <c> --pool <p>`; if it is short, `blackpool deposit` first.
37
+ Never retry deposit, send, swap or bridge after a timeout or unclear failure: run `blackpool balance --wait`
38
+ (or `blackpool bridges`) to see whether it landed. `blackpool recover --chain all` is always safe to run.
39
+ Never print, log or share BLACKPOOL_KEY or anything in ~/.blackpool.
40
+ Full guide: https://blackpool.cash/AGENTS.md
41
+ ```
42
+
43
+ ---
44
+
45
+ ## 2. How Blackpool works (what an agent needs to know)
46
+
47
+ - **Public balance**: tokens in the agent wallet, visible to everyone.
48
+ - **Private balance**: notes inside a Blackpool pool contract. Spending them reveals neither the agent wallet nor which deposit paid.
49
+ - **Shield (`deposit`)**: moves public funds into the pool. Signed and paid (gas) by the agent wallet. This is the only on-chain action that shows your wallet.
50
+ - **Private operations (`send`, `swap`, `bridge`)**: a zero-knowledge proof spends notes; the Blackpool relayer submits the transaction and pays gas. The fee is taken from the amount moved, so the agent needs no gas for these.
51
+ - **Burner**: a one-time wallet used inside `swap` and `bridge`. Its key is encrypted locally and registered before it is funded, so `recover` can always sweep it.
52
+ - **Account**: derived from one signature of the Blackpool sign-in message by the agent wallet. Same key → same private account on every chain and machine.
53
+ - **Pools are per chain and per asset.** A private USDC balance on Base cannot pay on Arbitrum; use `bridge` to move between chains.
54
+
55
+ ---
56
+
57
+ ## 3. Setup
58
+
59
+ ### 3.1 Install
60
+
61
+ ```sh
62
+ npm i -g @blackpool/cli # global: `blackpool …`
63
+ npx -y @blackpool/cli@latest … # no install, always the newest version
64
+ blackpool version --json # {"ok":true,"command":"version","version":"0.2.0"}
65
+ blackpool latest --json # {"ok":true,"command":"latest","installed":"0.2.0","latest":"0.2.0","upToDate":true}
66
+ ```
67
+
68
+ ### 3.2 Configure
69
+
70
+ | Variable | Required | Purpose |
71
+ |---|---|---|
72
+ | `BLACKPOOL_KEY` | yes (or run `blackpool login` once) | Agent wallet private key, 0x-prefixed hex. Used only locally. |
73
+ | `BLACKPOOL_JSON` | recommended | `1` = JSON output for every command (same as adding `--json`). |
74
+ | `BLACKPOOL_HOME` | no | Config/cache directory, default `~/.blackpool`. Set it when the home directory is read-only (containers, serverless). |
75
+ | `BLACKPOOL_API` | no | API base URL, default `https://api.blackpool.cash`. |
76
+
77
+ Without `BLACKPOOL_KEY`, `blackpool login` asks for the key with a hidden prompt and stores it at `~/.blackpool/wallet.json` (mode 600). Agents should prefer the environment variable.
78
+
79
+ ### 3.3 Fund
80
+
81
+ ```sh
82
+ blackpool address --json
83
+ ```
84
+
85
+ Send the agent wallet the asset you want to shield **plus a little native gas for the deposit** (a deposit uses about 1.3M gas; keep at least 0.0005 ETH on Robinhood Chain, Base or Arbitrum, or 0.002 BNB on BNB Chain). After shielding, private operations need no gas.
86
+
87
+ ### 3.4 Verify
88
+
89
+ ```sh
90
+ blackpool balance --chain all --json
91
+ ```
92
+
93
+ ---
94
+
95
+ ## 4. Command reference
96
+
97
+ Every command accepts `--json`. Amounts are plain decimals in token units (`10`, `0.25`), never wei. `--chain` defaults to `base`. Typical durations assume a healthy network.
98
+
99
+ ### `address`
100
+ Print the agent wallet address.
101
+ ```json
102
+ {"ok":true,"command":"address","address":"0x70997970C51812dc3A010C7d01b50e0d17dc79C8"}
103
+ ```
104
+
105
+ ### `balance [--chain C|all] [--pool P] [--wait]`
106
+ Private and public balances. `--pool` limits the answer to one private pool. `--wait` polls up to 30 s while a private balance reads 0 (use right after a deposit). ~5–15 s.
107
+ ```json
108
+ {"ok":true,"command":"balance","chain":"base","pool":"usdc","address":"0x…","balance":"13.965","symbol":"USDC","utxos":2}
109
+ ```
110
+ Without `--pool` (one entry per chain; `--chain all` returns four):
111
+ ```json
112
+ {"ok":true,"command":"balance","balances":[{"chain":"base","address":"0x…",
113
+ "private":{"ETH":{"balance":"0.0","utxos":0},"USDC":{"balance":"13.965","utxos":2}},
114
+ "public":{"ETH":"0.00412","USDC":"0.0"}}]}
115
+ ```
116
+ A value of `"?"` with an `error` field means that read failed; retry.
117
+
118
+ ### `deposit <amount> [--pool P] [--chain C]`
119
+ Shield public funds from the agent wallet. ERC-20 pools approve first (one extra transaction). Needs native gas. ~30–90 s.
120
+ ```json
121
+ {"ok":true,"command":"deposit","chain":"base","pool":"usdc","amount":25,"symbol":"USDC",
122
+ "txHash":"0x…","explorer":"https://basescan.org/tx/0x…","privateBalance":"25.0"}
123
+ ```
124
+ `--pool` defaults to the chain's stablecoin pool.
125
+
126
+ ### `send <recipient> <amount> [--pool P] [--chain C]`
127
+ Private payment to any EVM address on the same chain. `amount` is what leaves your private balance; the recipient gets `amount − fee`. No gas needed. ~30–90 s.
128
+ ```json
129
+ {"ok":true,"command":"send","chain":"base","pool":"usdc","recipient":"0x…","amount":10,
130
+ "received":"8.965","fee":"1.035","symbol":"USDC","txHash":"0x…","explorer":"https://basescan.org/tx/0x…"}
131
+ ```
132
+ To make the recipient receive exactly `X`, send `(X + flat) / (1 − 0.0035)` rounded up (see §5).
133
+
134
+ ### `swap <amount> --from P [--chain C] [--slippage BPS]`
135
+ Private same-chain swap between the chain's native pool and stablecoin pool; the output is re-shielded into your private balance. `--slippage` in basis points, default `100` (1%). No gas needed. ~1–3 min.
136
+ ```json
137
+ {"ok":true,"command":"swap","chain":"base","from":"eth","amount":0.01,"received":"22.41",
138
+ "symbol":"USDC","txHash":"0x…","explorer":"https://basescan.org/tx/0x…","privateBalance":"36.375"}
139
+ ```
140
+ If it fails half-way, funds sit on a registered burner: run `recover`.
141
+
142
+ ### `bridge <dest-asset> <recipient> <amount> --from ORIGIN [--no-wait]`
143
+ Private cross-chain transfer through NEAR Intents. `ORIGIN` is the private pool paying (default `base_usdc`); `dest-asset` and recipient format are listed in §6. The withdrawal is private; the solver delivers on the destination chain. Without `--no-wait` the CLI polls up to 6 minutes.
144
+ ```json
145
+ {"ok":true,"command":"bridge","from":"base_usdc","to":"sol_usdc","recipient":"7Gx4…kQ2m","amount":10,
146
+ "amountOutQuote":"8.91","withdrawTx":"0x…","depositAddress":"0x…","statusUrl":"https://…",
147
+ "status":"SUCCESS","amountOut":"8.93"}
148
+ ```
149
+ `status` is `SUBMITTED` (with `--no-wait`), `SUCCESS`, or `PENDING` (still running after 6 min; check `bridges` later). A refund or failure returns `"ok":false`; refunds go to the agent wallet on the origin chain.
150
+
151
+ ### `bridges`
152
+ Every bridge this account has made, newest first, from the server-side intent registry (works from any machine).
153
+ ```json
154
+ {"ok":true,"command":"bridges","bridges":[{"status":"SUCCESS","createdAt":1793760000000,"amountIn":"10",
155
+ "fromAsset":"base_usdc","toAsset":"sol_usdc","recipient":"7Gx4…","refundTo":"0x…","originChain":"base",
156
+ "amountOutQuote":"8.91","amountOut":"8.93","destTx":"…","depositAddress":"0x…","statusUrl":"https://…"}]}
157
+ ```
158
+
159
+ ### `assets`
160
+ Bridge destinations and valid origins, live from the API.
161
+ ```json
162
+ {"ok":true,"command":"assets","assets":{"sol_usdc":{"chain":"sol","symbol":"USDC","decimals":6,"isNative":false},"…":{}},
163
+ "origins":["base_eth","base_usdc","hood_eth","hood_usdg","arb_eth","arb_usdc","bsc_bnb","bsc_usdc","bsc_usdt"]}
164
+ ```
165
+
166
+ ### `recover [--to ADDR] [--chain C|all]`
167
+ Sweep every registered burner that still holds funds to the agent wallet (or `--to`). Idempotent and safe at any time. Swept funds land in the **public** wallet; `deposit` again to make them private. ~10–60 s per chain.
168
+ ```json
169
+ {"ok":true,"command":"recover","recovered":[{"chain":"base","to":"0x…","swept":1,"dust":0,"errors":0}]}
170
+ ```
171
+
172
+ ### `login`, `logout`, `version`, `latest`, `help`
173
+ `login` signs and caches the sign-in message (`{"address"}`); `logout` deletes the stored key and cache (`{"removed":[…]}`); `version` and `latest` report versions.
174
+
175
+ ---
176
+
177
+ ## 5. Chains, pools, fees and minimums
178
+
179
+ | `--chain` | Pools (`--pool`) | Min deposit | Min send / swap / bridge | Flat fee | DEX for `swap` |
180
+ |---|---|---|---|---|---|
181
+ | `base` (default) | `eth`, `usdc` | 0.001 ETH · 3 USDC | 0.001 ETH · 2 USDC | 0.00015 ETH · 1 USDC | Uniswap V3 |
182
+ | `robinhood` | `eth`, `usdg` | 0.001 ETH · 3 USDG | 0.001 ETH · 2 USDG | 0.00015 ETH · 0.5 USDG | Uniswap V3 |
183
+ | `arbitrum` | `eth`, `usdc` | 0.001 ETH · 3 USDC | 0.001 ETH · 2 USDC | 0.00015 ETH · 0.5 USDC | Uniswap V3 |
184
+ | `bsc` | `bnb`, `usdc`, `usdt` | 0.02 BNB · 3 USDC/USDT | 0.001 BNB · 2 USDC/USDT | 0.001 BNB · 0.5 USDC/USDT | PancakeSwap V3 |
185
+
186
+ - **Fee** on every private spend = flat fee + 0.35% of the amount, deducted from the amount. Example: `send 10` USDC on Base → fee 1 + 0.035 = 1.035, recipient gets 8.965.
187
+ - A spend must be more than twice its own fee; the CLI enforces the minimums above before doing anything.
188
+ - Bridges pay the pool fee plus the solver's quote (shown as `amountOutQuote`); each route also has a solver minimum.
189
+ - `deposit` costs only network gas; there is no Blackpool fee on shielding.
190
+
191
+ ---
192
+
193
+ ## 6. Bridging
194
+
195
+ **Origins** (`--from`): `base_eth`, `base_usdc`, `hood_eth`, `hood_usdg` (Robinhood Chain), `arb_eth`, `arb_usdc`, `bsc_bnb`, `bsc_usdc`, `bsc_usdt`.
196
+
197
+ **Destinations** (`dest-asset`; run `blackpool assets` for the live list):
198
+
199
+ | Chain | Assets | Recipient format |
200
+ |---|---|---|
201
+ | Base | `base_eth`, `base_weth`, `base_usdc`, `base_cbbtc` | EVM `0x…` |
202
+ | Ethereum | `eth_eth`, `eth_weth`, `eth_usdc`, `eth_usdt`, `eth_cbbtc` | EVM `0x…` |
203
+ | Arbitrum | `arb_eth`, `arb_weth`, `arb_usdc`, `arb_usdt`, `arb_arb` | EVM `0x…` |
204
+ | BNB Chain | `bsc_bnb`, `bsc_usdc`, `bsc_usdt` | EVM `0x…` |
205
+ | Robinhood Chain | `hood_eth`, `hood_usdg`, `hood_weth`, `hood_usde` | EVM `0x…` |
206
+ | Solana | `sol_sol`, `sol_usdc`, `sol_usdt` | base58 public key |
207
+
208
+ **Statuses** in `bridges`: `PENDING_DEPOSIT` → `PROCESSING` → `SUCCESS`, or `REFUNDED` / `FAILED` (refund to `refundTo` on `originChain`). Treat any other value as in progress.
209
+
210
+ ---
211
+
212
+ ## 7. Recipes
213
+
214
+ ### Pay an invoice exactly
215
+ ```sh
216
+ # recipient must receive 50 USDC on Base: amount = (50 + 1) / 0.9965 = 51.18 (round up)
217
+ blackpool balance --chain base --pool usdc --json # need >= 51.18
218
+ blackpool send 0xVENDOR 51.18 --pool usdc --chain base --json
219
+ ```
220
+
221
+ ### Top up only when needed
222
+ ```sh
223
+ bal=$(blackpool balance --chain base --pool usdc --json | jq -r .balance)
224
+ if (( $(echo "$bal < 51.18" | bc -l) )); then blackpool deposit 100 --pool usdc --chain base --json; fi
225
+ ```
226
+
227
+ ### Batch payouts (sequential, never parallel)
228
+ ```sh
229
+ while IFS=, read -r to amount; do
230
+ out=$(blackpool send "$to" "$amount" --pool usdc --chain base --json)
231
+ echo "$out" >> payouts.jsonl
232
+ [ "$(echo "$out" | jq -r .ok)" = "true" ] || { echo "stopped at $to"; break; }
233
+ done < payouts.csv
234
+ ```
235
+ Run payouts one at a time: each spend consumes notes and returns change, so parallel sends from one account can race.
236
+
237
+ ### Swap ETH to USDC privately
238
+ ```sh
239
+ blackpool swap 0.01 --from eth --chain base --json
240
+ ```
241
+
242
+ ### Bridge to Solana without blocking
243
+ ```sh
244
+ blackpool bridge sol_usdc <solana-address> 10 --from base_usdc --no-wait --json
245
+ blackpool bridges --json | jq '.bridges[0].status' # poll every minute until SUCCESS / REFUNDED
246
+ ```
247
+
248
+ ### Move private funds to another chain (rebalance)
249
+ ```sh
250
+ blackpool bridge arb_usdc $(blackpool address --json | jq -r .address) 20 --from base_usdc --json
251
+ blackpool deposit 19 --pool usdc --chain arbitrum --json # re-shield on the destination chain
252
+ ```
253
+ The bridge delivers to a public address; shield it again on the destination chain.
254
+
255
+ ### Health check before a run
256
+ ```sh
257
+ curl -s https://api.blackpool.cash/api/health | jq '.ok, .relayerGas'
258
+ ```
259
+
260
+ ### Clean up after an interrupted swap or bridge
261
+ ```sh
262
+ blackpool recover --chain all --json
263
+ ```
264
+
265
+ ---
266
+
267
+ ## 8. Integration code
268
+
269
+ ### Node.js
270
+ ```js
271
+ import { execFile } from 'node:child_process'
272
+
273
+ export function blackpool(args, { timeoutMs = 300_000 } = {}) {
274
+ return new Promise((resolve) => {
275
+ execFile('blackpool', [...args, '--json'], { timeout: timeoutMs, env: process.env }, (err, stdout, stderr) => {
276
+ try { resolve(JSON.parse(stdout.trim().split('\n').pop())) }
277
+ catch { resolve({ ok: false, error: (stderr || err?.message || 'no output').trim(), exitCode: err?.code }) }
278
+ })
279
+ })
280
+ }
281
+
282
+ const r = await blackpool(['send', '0xRECIPIENT', '10', '--pool', 'usdc', '--chain', 'base'])
283
+ if (!r.ok) throw new Error(r.error)
284
+ console.log(r.txHash, r.received)
285
+ ```
286
+
287
+ ### Python
288
+ ```python
289
+ import json, subprocess
290
+
291
+ def blackpool(*args, timeout=300):
292
+ p = subprocess.run(["blackpool", *args, "--json"], capture_output=True, text=True, timeout=timeout)
293
+ try:
294
+ return json.loads(p.stdout.strip().splitlines()[-1])
295
+ except (ValueError, IndexError):
296
+ return {"ok": False, "error": (p.stderr or "no output").strip(), "exitCode": p.returncode}
297
+
298
+ r = blackpool("balance", "--chain", "base", "--pool", "usdc")
299
+ print(r["balance"] if r["ok"] else r["error"])
300
+ ```
301
+
302
+ ### Tool definition for function-calling models
303
+ Expose one tool and let the model choose the command:
304
+ ```json
305
+ {
306
+ "name": "blackpool",
307
+ "description": "Private crypto payments via the Blackpool CLI. Commands: address; balance [--chain base|robinhood|arbitrum|bsc|all] [--pool P] [--wait]; deposit <amount> [--pool P] [--chain C]; send <recipient> <amount> [--pool P] [--chain C]; swap <amount> --from P [--chain C]; bridge <dest-asset> <recipient> <amount> --from ORIGIN [--no-wait]; bridges; assets; recover [--chain C|all]. Returns one JSON object; ok=false carries an error. Never retry deposit/send/swap/bridge without checking balance first.",
308
+ "input_schema": {
309
+ "type": "object",
310
+ "properties": {
311
+ "args": { "type": "array", "items": { "type": "string" }, "description": "CLI arguments, e.g. [\"send\",\"0xabc…\",\"10\",\"--pool\",\"usdc\",\"--chain\",\"base\"]" }
312
+ },
313
+ "required": ["args"]
314
+ }
315
+ }
316
+ ```
317
+ Implement it with the Node or Python wrapper above. Allow-list the first argument to the commands you want the agent to use (for example only `balance`, `send` and `bridges`).
318
+
319
+ ---
320
+
321
+ ## 9. Output contract
322
+
323
+ - stdout: exactly one line of JSON when `--json` (or `BLACKPOOL_JSON=1`) is set. stderr: human progress, safe to log.
324
+ - Envelope: `{"ok": true, "command": "<name>", …fields}` or `{"ok": false, "command": "<name>", "error": "<message>"}`.
325
+ - Amounts are decimal strings in token units (some input echoes such as `amount` are numbers). Tx hashes are 0x-hex; `explorer` is a full URL.
326
+ - Exit codes: `0` success · `1` failed or refused (including amounts under a minimum) · `2` usage (unknown command, missing arguments).
327
+ - New fields may be added; existing fields are not renamed or removed without a major version bump.
328
+
329
+ ---
330
+
331
+ ## 10. Errors and what to do
332
+
333
+ | `error` contains | Meaning | Do |
334
+ |---|---|---|
335
+ | `no wallet configured` | `BLACKPOOL_KEY` missing | Set it, or run `blackpool login`. |
336
+ | `invalid BLACKPOOL_KEY` / `stored key invalid` | Not a 0x hex private key | Fix the key. |
337
+ | `minimum deposit is …` / `minimum send is …` | Below the floor in §5 | Use a larger amount. |
338
+ | `unknown pool` / `unknown chain` / `unknown --from` | Wrong flag value | Use the values in §5–6. |
339
+ | `invalid recipient address` | Wrong format for the destination | EVM `0x…` (40 hex) or Solana base58. |
340
+ | `insufficient` / `no unspent` | Private balance too low | `balance`, then `deposit` more. |
341
+ | `Withdrawal must be more than twice the fee` | Amount too close to the fee | Larger amount. |
342
+ | `Amount is too low for this route. Minimum is …` | Solver minimum for that bridge | Bridge at least that much. |
343
+ | `rate limited, slow down` | API limit (40 relay ops, 600 reads per minute per IP) | The CLI already waited and retried 4 times; pause a minute. |
344
+ | `simulation failed: …` | The relayer refused a transaction that would revert | Do not retry blindly; check `balance`; report the message. |
345
+ | `reshield would revert` / `swap output … too small` | Swap stopped after funds reached the burner | `recover`, then `deposit` again. |
346
+ | `solver refunded` / `bridge failed at solver` | Bridge did not complete | Refund goes to the agent wallet on the origin chain; `deposit` to re-shield. |
347
+ | `timeout` / `fetch failed` / `HTTP 5xx` | Network or service problem | Check https://blackpool.cash/status; for write commands check `balance --wait` before retrying. |
348
+
349
+ ---
350
+
351
+ ## 11. Retries, timeouts, concurrency
352
+
353
+ - Safe to retry any time: `address`, `balance`, `assets`, `bridges`, `recover`, `version`, `latest`.
354
+ - **Not** safe to retry blindly: `deposit`, `send`, `swap`, `bridge`. A timeout can mean the transaction landed. Check `balance --wait` (or `bridges`) and only retry if the balance is unchanged.
355
+ - Give every command a timeout of **at least 300 s** (`bridge` without `--no-wait` up to 420 s).
356
+ - One write command at a time per account. Reads can run in parallel.
357
+ - A private balance can read `0` for a few seconds after a deposit while the indexer catches up; use `--wait`.
358
+
359
+ ---
360
+
361
+ ## 12. Security
362
+
363
+ - **Dedicated agent wallet.** Fund it with only what the agent may spend. Never use a personal or treasury key.
364
+ - `BLACKPOOL_KEY` never leaves the machine: it signs the sign-in message, deposit approvals and deposit transactions locally.
365
+ - The **sign-in signature** is cached in `$BLACKPOOL_HOME/config.json` (mode 600) and sent to the API with each private operation. The API derives the private account keys from it in memory to build the proof and does not store or log it, but anyone holding that signature can spend the agent's private balance. Treat the config directory like a key: never print it, never put it in prompts, logs or tool output.
366
+ - Only sign the Blackpool sign-in message on blackpool.cash or with this CLI.
367
+ - The contracts and circuits have not been externally audited; pools are upgradeable by an admin key during launch. Size balances accordingly. Details: https://blackpool.cash/docs#security
368
+ - Report vulnerabilities privately to hello@blackpool.cash (subject `security`).
369
+
370
+ ---
371
+
372
+ ## 13. Direct API (advanced)
373
+
374
+ The CLI is the supported interface. If you cannot run Node.js, the same operations are HTTP calls on `https://api.blackpool.cash` (`/api/balance`, `/api/deposit/prepare`, `/api/withdraw`, `/api/swap-private`, `/api/bridge/quote`, `/api/swap`, `/api/intents`, `/api/burner/*`, `/api/health`), all JSON. They need the sign-in signature (EIP-191 signature of the exact message below by the agent wallet), and deposits need you to sign and broadcast the returned transaction yourself. Endpoint list: https://blackpool.cash/docs#api
375
+
376
+ ```text
377
+ Welcome to Blackpool
378
+
379
+ Sign this message to access your private account.
380
+
381
+ - Gas free: no transaction is sent
382
+ - Your private account keys are derived from this signature
383
+ - It cannot move funds from your wallet or approve spending
384
+ - Only sign it on blackpool.cash or the Blackpool CLI
385
+ ```
386
+
387
+ The message is part of the account: a different message, even by one character, is a different (empty) account.
388
+
389
+ ---
390
+
391
+ ## 14. Working on this repository
392
+
393
+ If you are a coding agent editing this repository rather than using the CLI: the CLI lives in `cli/`, contracts in `contracts/`, circuits in `circuits/`, client libraries in `src/`, the web app in `frontend/`. Run `npx hardhat test` before changing contracts or core libraries. Keep the sign-in message, note format and circuit byte-identical across `src/`, `cli/` and `frontend/` (changing any of them changes every account). Never commit keys, `.env` files or credentials. Keep this file in sync with `cli/bin/blackpool.js` when commands or output fields change.
package/README.md CHANGED
@@ -127,3 +127,7 @@ Run `blackpool assets` for the full live list. Currently: **Base, Ethereum, BSC,
127
127
  ## Support
128
128
 
129
129
  Docs: [docs.blackpool.cash/agents](https://docs.blackpool.cash/agents) · Site: [blackpool.cash](https://blackpool.cash)
130
+
131
+ ## Agents
132
+
133
+ Run any command with `--json` (or `BLACKPOOL_JSON=1`) to get one JSON object on stdout and progress on stderr; exit codes are `0` ok, `1` failed, `2` usage. [`AGENTS.md`](AGENTS.md) (also at https://blackpool.cash/AGENTS.md) documents every command's fields, minimums, retry rules and security notes.
package/bin/blackpool.js CHANGED
@@ -24,9 +24,9 @@ const SIGN_MESSAGE =
24
24
  'Welcome to Blackpool\n\n' +
25
25
  'Sign this message to access your private account.\n\n' +
26
26
  '- Gas free: no transaction is sent\n' +
27
- '- Your keys are derived locally and never leave this device\n' +
28
- '- This signature cannot move funds or approve spending'
29
- const CONFIG_DIR = path.join(os.homedir(), '.blackpool')
27
+ '- Your private account keys are derived from this signature\n' +
28
+ '- It cannot move funds from your wallet or approve spending\n- Only sign it on blackpool.cash or the Blackpool CLI'
29
+ const CONFIG_DIR = process.env.BLACKPOOL_HOME || path.join(os.homedir(), '.blackpool')
30
30
  const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json') // cached BLACKPOOL sig
31
31
  const WALLET_FILE = path.join(CONFIG_DIR, 'wallet.json') // stored priv key (chmod 600)
32
32
 
@@ -36,11 +36,21 @@ const c = {
36
36
  red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m',
37
37
  blue: '\x1b[34m', cyan: '\x1b[36m', lime: '\x1b[38;5;155m'
38
38
  }
39
+ // --json (or BLACKPOOL_JSON=1): stdout carries exactly one JSON object, human progress goes to stderr.
40
+ const JSON_MODE = process.argv.includes('--json') || process.env.BLACKPOOL_JSON === '1'
41
+ const RESULT = {}
42
+ let CMD = null
43
+ function result(obj) { Object.assign(RESULT, obj) }
44
+ function out(line) { if (JSON_MODE) process.stderr.write(line + '\n'); else console.log(line) }
39
45
  function color(text, col) {
40
- return process.stdout.isTTY ? `${col}${text}${c.reset}` : text
46
+ return !JSON_MODE && process.stdout.isTTY ? `${col}${text}${c.reset}` : text
47
+ }
48
+ function ok(msg) { out(color('✓ ', c.green) + msg) }
49
+ function fail(msg, code = 1) {
50
+ if (JSON_MODE) process.stdout.write(JSON.stringify({ ok: false, command: CMD, error: String(msg) }) + '\n')
51
+ else console.error(color('✗ ', c.red) + msg)
52
+ process.exit(code)
41
53
  }
42
- function ok(msg) { console.log(color('✓ ', c.green) + msg) }
43
- function fail(msg) { console.error(color('✗ ', c.red) + msg); process.exit(1) }
44
54
  // Extract a human revert reason from ethers' verbose errors (which otherwise dump the entire tx,
45
55
  // calldata, and JSON-RPC request body into .message — unreadable in a terminal).
46
56
  function cleanErr(e) {
@@ -59,8 +69,8 @@ function cleanErr(e) {
59
69
  const m = e.message || String(e)
60
70
  return m.split(/\s*[\(\[]\s*(?:See:|error=|reason=)/)[0].trim().slice(0, 200)
61
71
  }
62
- function info(msg) { console.log(color(' ', c.dim) + msg) }
63
- function log(msg) { console.log(msg) }
72
+ function info(msg) { out(color(' ', c.dim) + msg) }
73
+ function log(msg) { out(msg) }
64
74
 
65
75
  // === config ===
66
76
  function loadConfig() {
@@ -140,8 +150,11 @@ function getWallet() {
140
150
  }
141
151
  }
142
152
 
153
+ // Cached signatures are only valid for the message they signed: a new sign-in message means a new account.
154
+ const SIGN_MESSAGE_ID = ethers.utils.id(SIGN_MESSAGE)
143
155
  function getCachedSignature(address) {
144
156
  const cfg = loadConfig()
157
+ if (cfg.signMessageId !== SIGN_MESSAGE_ID) return undefined
145
158
  return cfg.signatures?.[address.toLowerCase()]
146
159
  }
147
160
 
@@ -153,6 +166,8 @@ async function ensureSignature(wallet, { forceNew = false } = {}) {
153
166
  }
154
167
  const sig = await wallet.signMessage(SIGN_MESSAGE)
155
168
  const cfg = loadConfig()
169
+ if (cfg.signMessageId !== SIGN_MESSAGE_ID) cfg.signatures = {}
170
+ cfg.signMessageId = SIGN_MESSAGE_ID
156
171
  cfg.signatures = cfg.signatures || {}
157
172
  cfg.signatures[address.toLowerCase()] = sig
158
173
  saveConfig(cfg)
@@ -160,7 +175,18 @@ async function ensureSignature(wallet, { forceNew = false } = {}) {
160
175
  }
161
176
 
162
177
  // === HTTP ===
163
- async function apiPost(endpoint, body, { timeoutMs = 90_000 } = {}) {
178
+ async function apiPost(endpoint, body, opts = {}) {
179
+ for (let attempt = 0; ; attempt++) {
180
+ try { return await apiPostOnce(endpoint, body, opts) }
181
+ catch (e) {
182
+ if (e.status !== 429 || attempt >= 4) throw e
183
+ const wait = Math.min(Math.max(Number(e.retryAfter) || 5, 1), 30)
184
+ info(`rate limited by the API, retrying in ${wait}s...`)
185
+ await new Promise(r => setTimeout(r, wait * 1000))
186
+ }
187
+ }
188
+ }
189
+ async function apiPostOnce(endpoint, body, { timeoutMs = 90_000 } = {}) {
164
190
  const controller = new AbortController()
165
191
  const t = setTimeout(() => controller.abort(), timeoutMs)
166
192
  try {
@@ -174,7 +200,7 @@ async function apiPost(endpoint, body, { timeoutMs = 90_000 } = {}) {
174
200
  try { json = await r.json() } catch {}
175
201
  if (!r.ok) {
176
202
  const msg = json?.error || `HTTP ${r.status}`
177
- throw new Error(msg)
203
+ throw Object.assign(new Error(msg), { status: r.status, retryAfter: json?.retryAfter })
178
204
  }
179
205
  return json
180
206
  } finally { clearTimeout(t) }
@@ -206,6 +232,13 @@ async function cmdLogin() {
206
232
  const sig = await ensureSignature(wallet, { forceNew: true })
207
233
  ok(`logged in as ${color(wallet.address, c.cyan)}`)
208
234
  info(`signature cached at ${CONFIG_FILE}`)
235
+ result({ address: wallet.address })
236
+ }
237
+
238
+ async function cmdAddress() {
239
+ const wallet = getWallet()
240
+ log(wallet.address)
241
+ result({ address: wallet.address })
209
242
  }
210
243
 
211
244
  async function cmdLogout() {
@@ -215,6 +248,7 @@ async function cmdLogout() {
215
248
  }
216
249
  if (removed.length) { removed.forEach(f => ok(`removed ${f}`)) }
217
250
  else info('nothing to remove (already logged out)')
251
+ result({ removed })
218
252
  }
219
253
 
220
254
  // === Multi-chain registry (mirrors frontend app/page.tsx: CHAIN_POOLS + UNISWAP + POOL_META) ===
@@ -513,6 +547,14 @@ async function showChainBalance(chain, wallet, sig, wait) {
513
547
  log(` ${color(stableSym.padEnd(5), c.dim)} ${color(pub.usdc, c.cyan)}`)
514
548
  if (pub.err) info(` (public balance unavailable: ${pub.err} — retry, or set BLACKPOOL_${chain.toUpperCase()}_RPC to a private RPC)`)
515
549
  log('')
550
+ ;(RESULT.balances ||= []).push({
551
+ chain, address: wallet.address,
552
+ private: {
553
+ [nativeSym]: { balance: ethPriv.balance, utxos: ethPriv.utxoCount, ...(ethPriv.err ? { error: ethPriv.err } : {}) },
554
+ [stableSym]: { balance: stablePriv.balance, utxos: stablePriv.utxoCount, ...(stablePriv.err ? { error: stablePriv.err } : {}) },
555
+ },
556
+ public: { [nativeSym]: pub.eth, [stableSym]: pub.usdc, ...(pub.err ? { error: pub.err } : {}) },
557
+ })
516
558
  }
517
559
 
518
560
  async function cmdBalance(args) {
@@ -554,6 +596,7 @@ async function cmdBalance(args) {
554
596
  log(` ${color('private balance', c.dim)} ${color(res.balance, c.lime)} ${color(res.symbol, c.cyan)} ${color(`on ${C.label}`, c.dim)}`)
555
597
  log(` ${color('utxos', c.dim)} ${res.utxoCount}`)
556
598
  log('')
599
+ result({ chain, pool, address: wallet.address, balance: res.balance, symbol: res.symbol, utxos: res.utxoCount })
557
600
  return
558
601
  }
559
602
 
@@ -569,7 +612,7 @@ async function cmdDeposit(args) {
569
612
  const amount = args[0]
570
613
  const validPools = poolsForChain(chain)
571
614
  const pool = getFlag(args, '--pool') || C.stablePool
572
- if (!amount || amount.startsWith('--')) fail(`usage: blackpool deposit <amount> [--pool ${validPools.join('|')}] [--chain ${CHAIN_LIST}]`)
615
+ if (!amount || amount.startsWith('--')) fail(`usage: blackpool deposit <amount> [--pool ${validPools.join('|')}] [--chain ${CHAIN_LIST}]`, 2)
573
616
  const n = Number(amount)
574
617
  if (isNaN(n) || n <= 0) fail(`invalid amount: ${amount}`)
575
618
  if (!validPools.includes(pool)) fail(`unknown pool "${pool}" on ${C.label} (supported: ${validPools.join(', ')})`)
@@ -642,6 +685,7 @@ async function cmdDeposit(args) {
642
685
  info(`new private balance: ${prep.newBalance} ${prep.symbol}`)
643
686
  info(`tx: ${explorerTx(chain, dep.hash)}`)
644
687
  log('')
688
+ result({ chain, pool, amount: n, symbol: prep.symbol, txHash: dep.hash, explorer: explorerTx(chain, dep.hash), privateBalance: prep.newBalance })
645
689
  }
646
690
 
647
691
  // === Uniswap V3 constants on Base (extracted from frontend bundle - do not change) ===
@@ -787,7 +831,7 @@ async function cmdBridge(args) {
787
831
  const recipient = args[1]
788
832
  const amount = args[2]
789
833
  if (!toAsset || toAsset.startsWith('--') || !recipient || !amount) {
790
- fail(`usage: blackpool bridge <dest-asset> <recipient> <amount> [--from <origin>] [--chain ...]\n origins: ${Object.keys(BRIDGE_ORIGINS).join('|')} (default base_usdc)\n run 'blackpool assets' for destinations`)
834
+ fail(`usage: blackpool bridge <dest-asset> <recipient> <amount> [--from <origin>] [--chain ...]\n origins: ${Object.keys(BRIDGE_ORIGINS).join('|')} (default base_usdc)\n run 'blackpool assets' for destinations`, 2)
791
835
  }
792
836
  const fromAsset = getFlag(args, '--from') || 'base_usdc'
793
837
  // Recipient format follows the destination: Solana = base58 pubkey, everything else EVM 0x…
@@ -837,21 +881,23 @@ async function cmdBridge(args) {
837
881
  info(`withdraw tx: ${explorerTx(origin.chain, res.txHash)}`)
838
882
  info(`tracking: ${res.statusUrl}`)
839
883
  log('')
884
+ result({ from: fromAsset, to: toAsset, recipient, amount: n, amountOutQuote: res.amountOut, withdrawTx: res.txHash, depositAddress: res.depositAddress || null, statusUrl: res.statusUrl, status: 'SUBMITTED' })
840
885
 
841
- if (res.depositAddress) {
886
+ if (res.depositAddress && !args.includes('--no-wait')) {
842
887
  info('polling solver status (Ctrl+C to stop; bridge continues regardless)...')
843
888
  const start = Date.now()
844
889
  while (Date.now() - start < 360_000) {
845
890
  await new Promise(r => setTimeout(r, 8000))
846
891
  try {
847
892
  const st = await apiGet(`/api/intent-status?depositAddress=${res.depositAddress}`)
848
- if (st.status === 'SUCCESS') { ok(color(`delivered ${st.amountOut || res.amountOut} to recipient`, c.green)); log(''); return }
893
+ if (st.status === 'SUCCESS') { ok(color(`delivered ${st.amountOut || res.amountOut} to recipient`, c.green)); log(''); result({ status: 'SUCCESS', amountOut: st.amountOut || res.amountOut }); return }
849
894
  if (st.status === 'REFUNDED') { fail(`solver refunded (${st.refundReason || 'route unavailable'}). Funds auto-return to your wallet.`) }
850
895
  if (st.status === 'FAILED') { fail('bridge failed at solver. Funds auto-refund.') }
851
896
  info(`status: ${st.status}...`)
852
897
  } catch (e) { info(`status check: ${e.message}`) }
853
898
  }
854
899
  info('still processing after 6 min - check the tracking URL above.')
900
+ result({ status: 'PENDING' })
855
901
  }
856
902
  log('')
857
903
  }
@@ -962,6 +1008,7 @@ async function recoverOneChain(chain, wallet, sig, dest) {
962
1008
  if (dust > 0) info(`(${dust} burner(s) hold only dust ETH below gas cost — not recoverable, safe to ignore)`)
963
1009
  if (errors > 0) info(`${errors} burner(s) had errors — re-run recover to retry`)
964
1010
  log('')
1011
+ ;(RESULT.recovered ||= []).push({ chain, to: dest, swept, dust, errors })
965
1012
  return { swept, dust, errors }
966
1013
  }
967
1014
 
@@ -993,7 +1040,7 @@ async function cmdSwap(args) {
993
1040
  const stablePool = C.stablePool // 'usdc' on base, 'usdg' on robinhood, 'usdt' on bsc
994
1041
  const nativePool = C.nativePool // 'eth' everywhere except bsc, where it's 'bnb'
995
1042
  const validFrom = [nativePool, stablePool]
996
- if (!amount || amount.startsWith('--') || !from) fail(`usage: blackpool swap <amount> --from ${validFrom.join('|')} [--chain ${CHAIN_LIST}] [--slippage 100]`)
1043
+ if (!amount || amount.startsWith('--') || !from) fail(`usage: blackpool swap <amount> --from ${validFrom.join('|')} [--chain ${CHAIN_LIST}] [--slippage 100]`, 2)
997
1044
  if (!validFrom.includes(from)) fail(`--from must be ${validFrom.join(' or ')} on ${C.label}`)
998
1045
  const n = Number(amount)
999
1046
  if (isNaN(n) || n <= 0) fail(`invalid amount: ${amount}`)
@@ -1198,15 +1245,17 @@ async function cmdSwap(args) {
1198
1245
  info(`new private balance: ${prep.newBalance} ${prep.symbol}`)
1199
1246
  info(`reshield tx: ${explorerTx(chain, rtx.hash)}`)
1200
1247
  log('')
1248
+ result({ chain, from, amount: n, received: reshieldFmt, symbol: toSym, txHash: rtx.hash, explorer: explorerTx(chain, rtx.hash), privateBalance: prep.newBalance })
1201
1249
  }
1202
1250
 
1203
1251
  // blackpool bridges — list this wallet's NEAR Intents bridges (server-side intent registry, any device).
1204
1252
  async function cmdBridges() {
1205
1253
  const wallet = getWallet()
1206
- const sig = await getSig(wallet)
1254
+ const sig = await ensureSignature(wallet)
1207
1255
  const authSig = await registryAuthSig(sig)
1208
1256
  const res = await apiPost('/api/intents', { authSig }, { timeoutMs: 60_000 })
1209
1257
  const list = res.intents || []
1258
+ result({ bridges: list })
1210
1259
  if (!list.length) { log(color(' no bridges recorded for this wallet', c.dim)); return }
1211
1260
  log('')
1212
1261
  for (const it of list) {
@@ -1226,6 +1275,7 @@ async function cmdBridges() {
1226
1275
  async function cmdAssets() {
1227
1276
  info('fetching supported assets...')
1228
1277
  const res = await apiGet('/api/assets')
1278
+ result({ assets: res.assets || {}, origins: Object.keys(BRIDGE_ORIGINS) })
1229
1279
  log('')
1230
1280
  log(color(' bridge destination assets:', c.bold))
1231
1281
  const byChain = {}
@@ -1251,7 +1301,7 @@ async function cmdSend(args) {
1251
1301
  const amount = args[1]
1252
1302
  const validPools = poolsForChain(chain)
1253
1303
  const pool = getFlag(args, '--pool') || C.stablePool
1254
- if (!recipient || !amount) fail(`usage: blackpool send <recipient> <amount> [--pool ${validPools.join('|')}] [--chain ${CHAIN_LIST}]`)
1304
+ if (!recipient || !amount) fail(`usage: blackpool send <recipient> <amount> [--pool ${validPools.join('|')}] [--chain ${CHAIN_LIST}]`, 2)
1255
1305
  if (!/^0x[a-fA-F0-9]{40}$/.test(recipient)) fail(`invalid recipient address: ${recipient}`)
1256
1306
  const n = Number(amount)
1257
1307
  if (isNaN(n) || n <= 0) fail(`invalid amount: ${amount}`)
@@ -1277,6 +1327,7 @@ async function cmdSend(args) {
1277
1327
  info(`fee: ${res.fee} ${res.symbol}`)
1278
1328
  info(`tx: ${explorerTx(chain, res.txHash)}`)
1279
1329
  log('')
1330
+ result({ chain, pool, recipient, amount: n, received: res.recipientReceives, fee: res.fee, symbol: res.symbol, txHash: res.txHash, explorer: explorerTx(chain, res.txHash) })
1280
1331
  }
1281
1332
 
1282
1333
  function getFlag(args, name) {
@@ -1295,13 +1346,14 @@ ${color('Usage:', c.bold)}
1295
1346
 
1296
1347
  ${color('Commands:', c.bold)}
1297
1348
  login Store key (hidden prompt) + sign BLACKPOOL message.
1349
+ address Print the wallet address (fund this to shield).
1298
1350
  logout Remove stored key and cached signature.
1299
1351
  balance [--pool] [--chain] Show private + public balances (or one pool).
1300
1352
  --chain all shows every chain in one run.
1301
1353
  deposit <amount> [--pool] [--chain] Shield public funds into private pool.
1302
1354
  send <to> <amount> [--pool] [--chain] Send privately to an address (no gas from you).
1303
1355
  swap <amount> --from <pool> [--chain] Private same-chain swap via Uniswap V3.
1304
- bridge <asset> <to> <amount> Bridge cross-chain privately via NEAR Intents
1356
+ bridge <asset> <to> <amount> Bridge cross-chain privately via NEAR Intents (--no-wait: return once submitted)
1305
1357
  (--from ${Object.keys(BRIDGE_ORIGINS).join('|')}).
1306
1358
  Run 'blackpool assets' for the destination list.
1307
1359
  assets List supported bridge destination assets.
@@ -1318,13 +1370,20 @@ ${Object.entries(CHAINS).map(([k, v]) => ` --chain ${k.padEnd(10)} ${k === 'bas
1318
1370
  ${color('Environment (optional, for agents/CI):', c.bold)}
1319
1371
  BLACKPOOL_KEY Agent private key (0x-hex). If set, skips interactive login.
1320
1372
  BLACKPOOL_API API base URL (default: ${API}).
1373
+ BLACKPOOL_HOME Config directory (default: ~/.blackpool).
1374
+ BLACKPOOL_JSON Set to 1 for JSON output (same as --json).
1375
+
1376
+ ${color('Output:', c.bold)}
1377
+ --json One JSON object on stdout: {"ok":true,"command":...,...} or {"ok":false,"error":...}.
1378
+ Progress goes to stderr. Exit codes: 0 ok, 1 error, 2 usage.
1321
1379
 
1322
1380
  ${color('Config:', c.bold)}
1323
1381
  ~/.blackpool/wallet.json Stored private key (chmod 600).
1324
1382
  ~/.blackpool/config.json Cached signatures per wallet (chmod 600).
1325
1383
 
1326
1384
  ${color('Docs:', c.bold)}
1327
- https://docs.blackpool.cash/agents
1385
+ https://blackpool.cash/docs#agents
1386
+ https://blackpool.cash/AGENTS.md
1328
1387
  `)
1329
1388
  }
1330
1389
 
@@ -1336,6 +1395,7 @@ async function cmdLatest() {
1336
1395
  const r = await fetch('https://registry.npmjs.org/@blackpool/cli/latest', { headers: { 'Accept': 'application/json' } })
1337
1396
  if (!r.ok) fail(`npm registry error (HTTP ${r.status})`)
1338
1397
  const remote = (await r.json()).version
1398
+ result({ installed: local, latest: remote, upToDate: local === remote })
1339
1399
  log('')
1340
1400
  log(` ${color('installed', c.dim)} ${color('v' + local, c.cyan)}`)
1341
1401
  log(` ${color('latest ', c.dim)} ${color('v' + remote, c.lime)}`)
@@ -1351,11 +1411,13 @@ async function cmdLatest() {
1351
1411
 
1352
1412
  // === entrypoint ===
1353
1413
  async function main() {
1354
- const [cmd, ...args] = process.argv.slice(2)
1414
+ const [cmd, ...args] = process.argv.slice(2).filter(a => a !== '--json')
1415
+ CMD = cmd || 'help'
1355
1416
  try {
1356
1417
  switch (cmd) {
1357
1418
  case 'login': await cmdLogin(); break
1358
1419
  case 'logout': await cmdLogout(); break
1420
+ case 'address': await cmdAddress(); break
1359
1421
  case 'balance': await cmdBalance(args); break
1360
1422
  case 'deposit': await cmdDeposit(args); break
1361
1423
  case 'send': await cmdSend(args); break
@@ -1369,6 +1431,7 @@ async function main() {
1369
1431
  case '--version': {
1370
1432
  const pkg = require('../package.json')
1371
1433
  log(pkg.version)
1434
+ result({ version: pkg.version })
1372
1435
  break
1373
1436
  }
1374
1437
  case 'latest':
@@ -1377,8 +1440,9 @@ async function main() {
1377
1440
  case '-h':
1378
1441
  case '--help':
1379
1442
  case undefined: help(); break
1380
- default: log(`unknown command: ${cmd}\n`); help(); process.exit(2)
1443
+ default: if (!JSON_MODE) { log(`unknown command: ${cmd}\n`); help() } fail(`unknown command: ${cmd} (run blackpool help)`, 2)
1381
1444
  }
1445
+ if (JSON_MODE) process.stdout.write(JSON.stringify({ ok: true, command: CMD, ...RESULT }) + '\n')
1382
1446
  } catch (e) {
1383
1447
  fail(cleanErr(e))
1384
1448
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blackpool/cli",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Private payments for onchain agents. CLI for BLACKPOOL - shield, send, swap, bridge with zero-knowledge privacy on Base, Robinhood Chain, Arbitrum, and BNB Smart Chain.",
5
5
  "bin": {
6
6
  "blackpool": "bin/blackpool.js"
@@ -11,7 +11,8 @@
11
11
  },
12
12
  "files": [
13
13
  "bin/",
14
- "README.md"
14
+ "README.md",
15
+ "AGENTS.md"
15
16
  ],
16
17
  "dependencies": {
17
18
  "ethers": "^5.7.2"