@blackpool/cli 0.4.4 → 0.4.5

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 CHANGED
@@ -175,9 +175,9 @@ Bridge destinations and valid origins, live from the API.
175
175
  ```
176
176
 
177
177
  ### `recover [--to ADDR] [--chain C|all]`
178
- 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.
178
+ 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. A burner that holds tokens but no gas gets a gas top-up from the relayer first; if that is refused, the result reports it in `needsGas` instead of claiming the chain is clean.
179
179
  ```json
180
- {"ok":true,"command":"recover","recovered":[{"chain":"base","to":"0x…","swept":1,"dust":0,"errors":0}]}
180
+ {"ok":true,"command":"recover","recovered":[{"chain":"base","to":"0x…","swept":1,"dust":0,"errors":0,"needsGas":0}]}
181
181
  ```
182
182
 
183
183
  ### `login`, `logout`, `version`, `latest`, `help`
@@ -333,6 +333,15 @@ Implement it with the Node or Python wrapper above. Allow-list the first argumen
333
333
 
334
334
  Blackpool speaks the [Model Context Protocol](https://modelcontextprotocol.io), so any MCP-capable agent app can call it as tools instead of running shell commands. There are two servers.
335
335
 
336
+ Both are listed publicly, so an agent app or directory can find them by name:
337
+
338
+ | Listing | Name | What is listed |
339
+ |---|---|---|
340
+ | [Official MCP Registry](https://registry.modelcontextprotocol.io/v0.1/servers/cash.blackpool%2Fblackpool/versions/latest) | `cash.blackpool/blackpool` | The local server (npm package) and the hosted endpoint |
341
+ | [Glama — server](https://glama.ai/mcp/servers/blackpooldev/blackpool) | `blackpooldev/blackpool` | The local server, built and tested from the open-source repository |
342
+ | [Glama — connector](https://glama.ai/mcp/connectors/cash.blackpool/blackpool) | `cash.blackpool/blackpool` | The hosted endpoint, ownership verified and health-checked |
343
+ | [npm](https://www.npmjs.com/package/@blackpool/cli) | `@blackpool/cli` | The package that runs the local server |
344
+
336
345
  ### 9.1 Local server — payments (recommended)
337
346
 
338
347
  Runs on your machine over stdio and exposes the full CLI as tools. The wallet key stays in the local environment; tools behave exactly like the commands in §4 and return the same JSON.
package/bin/blackpool.js CHANGED
@@ -1040,13 +1040,30 @@ async function sweepBurner(privateKey, chain, dest) {
1040
1040
  log(color(` burner ${burner.address}`, c.cyan))
1041
1041
  log(` ${C.pools[C.nativePool].symbol} : ${ethers.utils.formatEther(ethB)}`)
1042
1042
  log(` ${stableSym}: ${ethers.utils.formatUnits(uB, stableDecimals)}`)
1043
- let swept = false, error = false
1044
-
1045
- // stablecoin first (needs ETH for gas). Chain-realistic gas so the burner's small top-up isn't drained.
1043
+ let swept = false, error = false, needsGas = false
1044
+ const nativeSym = C.pools[C.nativePool].symbol
1045
+ const MIN_GAS = ethers.utils.parseEther('0.00001')
1046
+
1047
+ // stablecoin first (needs gas). A burner holding pool tokens qualifies for the relayer's gas top-up
1048
+ // (/api/burner-gas only funds addresses that hold a pool token), so ask for it instead of making the
1049
+ // user send gas by hand.
1050
+ if (uB.gt(0) && ethB.lt(MIN_GAS)) {
1051
+ info(` burner has no ${nativeSym} for gas — asking the relayer for a gas top-up...`)
1052
+ try {
1053
+ const g = await apiPost('/api/burner-gas', { burnerAddress: burner.address, chain, txCount: 1 }, { timeoutMs: 60_000 })
1054
+ if (g && g.success) {
1055
+ for (let i = 0; i < 30 && ethB.lt(MIN_GAS); i++) {
1056
+ await new Promise((res) => setTimeout(res, 2000))
1057
+ ethB = await provider.getBalance(burner.address)
1058
+ }
1059
+ } else info(` gas top-up declined: ${(g && (g.error || g.reason)) || 'no reason given'}`)
1060
+ } catch (e) { info(` gas top-up failed: ${(e.message || '').slice(0, 80)}`) }
1061
+ }
1046
1062
  if (uB.gt(0)) {
1047
1063
  try {
1048
- if (ethB.lt(ethers.utils.parseEther('0.00001'))) {
1049
- info(` burner has no ETH for gas — send it ~0.00002 ETH then re-run recover`)
1064
+ if (ethB.lt(MIN_GAS)) {
1065
+ needsGas = true
1066
+ info(` still no gas — send this burner ~0.00002 ${nativeSym}, then re-run recover`)
1050
1067
  } else {
1051
1068
  info(` sweeping ${ethers.utils.formatUnits(uB, stableDecimals)} ${stableSym} to ${dest}...`)
1052
1069
  const t = new ethers.Contract(C.uni.stable, ['function transfer(address,uint256) returns (bool)'], burner)
@@ -1082,7 +1099,7 @@ async function sweepBurner(privateKey, chain, dest) {
1082
1099
  }
1083
1100
  }
1084
1101
  } catch (e) { info(` ETH sweep failed: ${(e.message || '').slice(0, 80)}`); error = true }
1085
- return { swept, dust: false, error }
1102
+ return { swept, dust: false, error, needsGas }
1086
1103
  }
1087
1104
 
1088
1105
  // Sweep registry burners for ONE chain. Shared by single-chain and --chain all.
@@ -1093,7 +1110,7 @@ async function recoverOneChain(chain, wallet, sig, dest) {
1093
1110
  log(color(` sweeping to ${dest}`, c.dim))
1094
1111
  log('')
1095
1112
 
1096
- let swept = 0, dust = 0, errors = 0
1113
+ let swept = 0, dust = 0, errors = 0, needsGas = 0
1097
1114
  const seen = new Set()
1098
1115
 
1099
1116
  // 1) Registry burners (random ephemeral, created by frontend OR this CLI). Primary path — random
@@ -1109,17 +1126,19 @@ async function recoverOneChain(chain, wallet, sig, dest) {
1109
1126
  if (r.swept) { swept++; await markBurnerSwept(authSig, b.address, chain) }
1110
1127
  if (r.dust) dust++
1111
1128
  if (r.error) errors++
1129
+ if (r.needsGas) needsGas++
1112
1130
  }
1113
1131
  } catch (e) { info(`registry scan skipped: ${cleanErr(e)}`) }
1114
1132
 
1115
1133
  log('')
1116
1134
  if (swept > 0) ok(color(`recovered funds from ${swept} burner(s) on ${C.label}`, c.green))
1117
- else ok(`no stranded funds on ${C.label} — all burners clean`)
1135
+ else if (needsGas === 0 && errors === 0) ok(`no stranded funds on ${C.label} — all burners clean`)
1136
+ if (needsGas > 0) info(color(`${needsGas} burner(s) still hold funds but have no gas — see above, then re-run recover`, c.yellow))
1118
1137
  if (dust > 0) info(`(${dust} burner(s) hold only dust ETH below gas cost — not recoverable, safe to ignore)`)
1119
1138
  if (errors > 0) info(`${errors} burner(s) had errors — re-run recover to retry`)
1120
1139
  log('')
1121
- ;(RESULT.recovered ||= []).push({ chain, to: dest, swept, dust, errors })
1122
- return { swept, dust, errors }
1140
+ ;(RESULT.recovered ||= []).push({ chain, to: dest, swept, dust, errors, needsGas })
1141
+ return { swept, dust, errors, needsGas }
1123
1142
  }
1124
1143
 
1125
1144
  async function cmdRecover(args) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blackpool/cli",
3
- "version": "0.4.4",
3
+ "version": "0.4.5",
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
  "mcpName": "cash.blackpool/blackpool",
6
6
  "bin": {