@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 +11 -2
- package/bin/blackpool.js +29 -10
- package/package.json +1 -1
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
|
-
|
|
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(
|
|
1049
|
-
|
|
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.
|
|
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": {
|