Skip to content

Commit 030369f

Browse files
authored
Merge pull request #7 from Jolah1/main
updates
2 parents c7f13aa + 084b576 commit 030369f

12 files changed

Lines changed: 471 additions & 231 deletions

File tree

‎.github/workflows/rust.yml‎

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ "main" ]
6+
pull_request:
7+
branches: [ "main" ]
8+
9+
env:
10+
CARGO_TERM_COLOR: always
11+
12+
jobs:
13+
test:
14+
name: Test
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@v4
18+
- uses: dtolnay/rust-toolchain@stable
19+
- name: Build
20+
run: cargo build --verbose
21+
- name: Run tests
22+
run: cargo test --verbose
23+
24+
clippy:
25+
name: Clippy
26+
runs-on: ubuntu-latest
27+
steps:
28+
- uses: actions/checkout@v4
29+
- uses: dtolnay/rust-toolchain@stable
30+
with:
31+
components: clippy
32+
- name: Clippy check
33+
run: cargo clippy -- -D warnings
34+
35+
fmt:
36+
name: Format
37+
runs-on: ubuntu-latest
38+
steps:
39+
- uses: actions/checkout@v4
40+
- uses: dtolnay/rust-toolchain@stable
41+
with:
42+
components: rustfmt
43+
- name: Format check
44+
run: cargo fmt --check

‎Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ edition = "2021"
66
[dependencies]
77
bitcoincore-rpc = "0.19"
88
bitcoin = "0.32"
9-
clap = { version = "4", features = ["derive"] }
9+
clap = { version = "4", features = ["derive", "env"] }
1010
anyhow = "1"
1111
serde_json = "1.0.149"

‎README.md‎

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22

33
A Bitcoin CLI tool that detects dust attack UTXOs in your wallet and sweeps them safely using PSBTs ([BIP174](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki)).
44

5-
Built in Rust!
6-
---
5+
6+
## Demo
7+
8+
Example of scanning for dust UTXOs and performing a dry-run sweep:
9+
10+
[![asciicast](https://asciinema.org/a/911545.svg)](https://asciinema.org/a/911545)
711

812
## What is a Dust Attack?
913

@@ -146,6 +150,17 @@ cHNidP8BA...
146150
Send: bitcoin-cli sendrawtransaction <hex>
147151
```
148152

153+
### Sweep using OP_RETURN (burn to miner fees)
154+
```bash
155+
dust-cleaner --rpc-user <user> --rpc-pass <pass> sweep --method op-return
156+
```
157+
158+
Example output:
159+
```
160+
🔥 Method: op-return — dust burned to miner fees
161+
Output: OP_RETURN ("ash" — ashes to ashes, dust to dust)
162+
```
163+
149164
### Custom threshold
150165

151166
```bash

‎docs/demo.cast‎

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
{"version": 2, "width": 82, "height": 44, "timestamp": 1775798717, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}}
2+
[0.820534, "o", "\u001b[?2004h\u001b]0;jolah1@DESKTOP-N4M1LE0: ~/dev/dust-cleaner\u0007\u001b[01;32mjolah1@DESKTOP-N4M1LE0\u001b[00m:\u001b[01;34m~/dev/dust-cleaner\u001b[00m$ "]
3+
[2.795947, "o", "c"]
4+
[2.980767, "o", "a"]
5+
[3.139852, "o", "r"]
6+
[3.392408, "o", "g"]
7+
[3.835027, "o", "o"]
8+
[4.186488, "o", " "]
9+
[5.40972, "o", "r"]
10+
[5.701888, "o", "u"]
11+
[6.069876, "o", "n"]
12+
[6.946351, "o", " "]
13+
[7.654731, "o", "-"]
14+
[7.837404, "o", "-"]
15+
[8.257093, "o", " "]
16+
[9.03623, "o", "-"]
17+
[9.230662, "o", "-"]
18+
[9.907083, "o", "h"]
19+
[10.337625, "o", "e"]
20+
[10.51073, "o", "l"]
21+
[10.830549, "o", "p"]
22+
[12.792466, "o", "\r\n\u001b[?2004l\r"]
23+
[12.967165, "o", "\u001b[1m\u001b[92m Finished\u001b[0m `dev` profile [unoptimized + debuginfo] target(s) in 0.10s\r\n"]
24+
[12.973381, "o", "\u001b[1m\u001b[92m Running\u001b[0m `target/debug/dust-cleaner --help`\r\n"]
25+
[12.981323, "o", "Detect and sweep dust attack UTXOs from your Bitcoin wallet\r\n\r\n\u001b[1m\u001b[4mUsage:\u001b[0m \u001b[1mdust-cleaner\u001b[0m [OPTIONS] \u001b[1m--rpc-user\u001b[0m <RPC_USER> \u001b[1m--rpc-pass\u001b[0m <RPC_PASS> <COMMAND>\r\n\r\n\u001b[1m\u001b[4mCommands:\u001b[0m\r\n \u001b[1mscan\u001b[0m Scan wallet for dust UTXOs\r\n \u001b[1msweep\u001b[0m Create a PSBT sweeping all dust UTXOs\r\n \u001b[1mhelp\u001b[0m Print this message or the help of the given subcommand(s)\r\n\r\n\u001b[1m\u001b[4mOptions:\u001b[0m\r\n \u001b[1m--rpc-url\u001b[0m <RPC_URL> Bitcoin Core RPC URL [default: http://127.0.0.1:18443]\r\n \u001b[1m--rpc-user\u001b[0m <RPC_USER> \r\n \u001b[1m--rpc-pass\u001b[0m <RPC_PASS> \r\n \u001b[1m--threshold\u001b[0m <THRESHOLD> Custom dust threshold in sats. If omitted, uses per-script-type thresholds\r\n \u001b[1m-h\u001b[0m, \u001b[1m--help\u001b[0m Print help\r\n"]
26+
[12.982709, "o", "\u001b[?2004h\u001b]0;jolah1@DESKTOP-N4M1LE0: ~/dev/dust-cleaner\u0007\u001b[01;32mjolah1@DESKTOP-N4M1LE0\u001b[00m:\u001b[01;34m~/dev/dust-cleaner\u001b[00m$ "]
27+
[15.274456, "o", "c"]
28+
[15.510141, "o", "a"]
29+
[15.687383, "o", "r"]
30+
[15.980204, "o", "g"]
31+
[16.508834, "o", "o"]
32+
[17.879149, "o", " "]
33+
[18.482939, "o", "r"]
34+
[18.748675, "o", "u"]
35+
[19.060231, "o", "n"]
36+
[19.412957, "o", " "]
37+
[22.445408, "o", "-"]
38+
[22.642001, "o", "-"]
39+
[22.924427, "o", " "]
40+
[23.237142, "o", "-"]
41+
[23.458555, "o", "-"]
42+
[25.72683, "o", "p"]
43+
[26.257663, "o", "\b\u001b[K"]
44+
[26.766145, "o", "r"]
45+
[27.368655, "o", "p"]
46+
[27.735021, "o", "c"]
47+
[30.695653, "o", "-"]
48+
[31.182338, "o", "p"]
49+
[33.338241, "o", "\b\u001b[K"]
50+
[33.578218, "o", "\b\u001b[K"]
51+
[34.803893, "o", "-"]
52+
[35.324833, "o", "u"]
53+
[35.888364, "o", "s"]
54+
[36.083225, "o", "e"]
55+
[36.398795, "o", "r"]
56+
[37.953145, "o", " "]
57+
[38.34731, "o", "u"]
58+
[38.676875, "o", "s"]
59+
[38.937002, "o", "e"]
60+
[39.227316, "o", "r"]
61+
[39.696359, "o", " "]
62+
[40.23407, "o", "-"]
63+
[40.448878, "o", "-"]
64+
[41.333096, "o", "r"]
65+
[41.636531, "o", "p"]
66+
[42.040403, "o", "c"]
67+
[43.378474, "o", "-"]
68+
[43.876399, "o", "p"]
69+
[44.168749, "o", "a"]
70+
[44.48426, "o", "s"]
71+
[44.710614, "o", "s \r"]
72+
[44.947755, "o", " "]
73+
[45.496174, "o", "p"]
74+
[45.79453, "o", "a"]
75+
[46.116484, "o", "s"]
76+
[46.332291, "o", "s"]
77+
[46.501898, "o", " "]
78+
[47.26367, "o", "-"]
79+
[47.459406, "o", "-"]
80+
[48.382401, "o", "t"]
81+
[48.699388, "o", "h"]
82+
[49.072755, "o", "r"]
83+
[49.441081, "o", "e"]
84+
[50.802838, "o", "s"]
85+
[50.933947, "o", "h"]
86+
[51.624798, "o", "o"]
87+
[51.896608, "o", "l"]
88+
[52.192583, "o", "d"]
89+
[52.876343, "o", " "]
90+
[53.534242, "o", "1"]
91+
[53.955866, "o", "0"]
92+
[54.145991, "o", "0"]
93+
[54.384159, "o", "0"]
94+
[55.356373, "o", " "]
95+
[55.933721, "o", "s"]
96+
[56.281976, "o", "c"]
97+
[56.899056, "o", "a"]
98+
[57.043523, "o", "n"]
99+
[61.326621, "o", "\r\n\u001b[?2004l\r"]
100+
[61.51143, "o", "\u001b[1m\u001b[92m Finished\u001b[0m `dev` profile [unoptimized + debuginfo] target(s) in 0.09s\r\n"]
101+
[61.519195, "o", "\u001b[1m\u001b[92m Running\u001b[0m `target/debug/dust-cleaner --rpc-user user --rpc-pass pass --threshold 1000 scan`\r\n"]
102+
[61.575349, "o", "Found 9 total UTXOs (threshold: 1000 sats (custom))\r\n\r\n"]
103+
[61.576409, "o", "⚠️ DUST UTXOs (3 found):\r\n 500 sats | 84e32362d15b14d069934ef2fe7a5d7864323d59f6936ef361bce812c2a8c10e:0 | P2WPKH | bcrt1q4x2qz6a7xrau48hflygphv75vmwx60v6xg9dqz\r\n"]
104+
[61.576935, "o", " 300 sats | 1b6cceaba9b1555ad385f3273e3a726a7f3e4a7d1091128d8628caa38d8514cd:0 | P2WPKH | bcrt1q62sx9egww58us7up2f5w3lf2vspeyrjqwupr2q\r\n 800 sats | 1a51b81e315379f2b036f5bdaf4862edfe3b3527c7596c475918e7f8b97ba6a1:0 | P2WPKH | bcrt1qyk3sa24pnfrex8gneldjdwe2n8ed0yhj83uy8s\r\n\r\n✅ CLEAN UTXOs (6 found):\r\n 4999973630 sats | 84e32362d15b14d069934ef2fe7a5d7864323d59f6936ef361bce812c2a8c10e:1\r\n 5000000000 sats | f50406678677be7e407d55ad7eda96df662237555cc5da5b26cc740838eca7ec:0\r\n 4999999860 sats | 81452410ca2bb3de54916a51a0c1b00f1c723bcef470d812895a01f33abb6500:0\r\n 5000000000 sats | e16c27cbd32cbbeb7f1e3d03fd26c77ae4f2c42f8bb122b77dc9d61cdddc8bcd:0\r\n 4999994210 sats | 1a51b81e315379f2b036f5bdaf4862edfe3b3527c7596c475918e7f8b97ba6a1:1\r\n 5000000000 sats | 8789c76955d1107de5731d618424372b5c6467254ae960a214938c16a6852740:0\r\n\r\n─────────────────────────────────────────\r\n📊 Summary\r\n Total UTXOs: 9"]
105+
[61.577538, "o", "\r\n Dust UTXOs: 3 (1600 sats)\r\n Clean UTXOs: 6 (29999967700 sats)\r\n Threshold: 1000 sats (custom)\r\n\r\n 💡 Run 'sweep' to consolidate dust into a single UTXO\r\n─────────────────────────────────────────\r\n"]
106+
[61.578418, "o", "\u001b[?2004h\u001b]0;jolah1@DESKTOP-N4M1LE0: ~/dev/dust-cleaner\u0007\u001b[01;32mjolah1@DESKTOP-N4M1LE0\u001b[00m:\u001b[01;34m~/dev/dust-cleaner\u001b[00m$ "]
107+
[70.912916, "o", "c"]
108+
[71.007195, "o", "l"]
109+
[71.611786, "o", "e"]
110+
[73.418187, "o", "a"]
111+
[73.593535, "o", "r"]
112+
[75.662852, "o", "\r\n\u001b[?2004l\r"]
113+
[75.667244, "o", "\u001b[H\u001b[2J\u001b[3J"]
114+
[75.66789, "o", "\u001b[?2004h\u001b]0;jolah1@DESKTOP-N4M1LE0: ~/dev/dust-cleaner\u0007\u001b[01;32mjolah1@DESKTOP-N4M1LE0\u001b[00m:\u001b[01;34m~/dev/dust-cleaner\u001b[00m$ "]
115+
[87.016649, "o", "\u001b[7mcargo run -- --rpc-user user --rpc-pass\u001b[27m\u001b[7m \u001b[27m\u001b[7mpass --threshold 1000 sweep --dry-run\u001b[27m"]
116+
[87.973369, "o", "\u0007\u001b[A\r\u001b]0;jolah1@DESKTOP-N4M1LE0: ~/dev/dust-cleaner\u0007\u001b[01;32mjolah1@DESKTOP-N4M1LE0\u001b[00m:\u001b[01;34m~/dev/dust-cleaner\u001b[00m$ cargo run -- --rpc-user user --rpc-pass pass --threshold 1000 sweep --dry-run"]
117+
[91.891958, "o", "\r\n\u001b[?2004l\r"]
118+
[92.04665, "o", "\u001b[1m\u001b[92m Finished\u001b[0m `dev` profile [unoptimized + debuginfo] target(s) in 0.07s\r\n"]
119+
[92.053677, "o", "\u001b[1m\u001b[92m Running\u001b[0m `target/debug/dust-cleaner --rpc-user user --rpc-pass pass --threshold 1000 sweep --dry-run`\r\n"]
120+
[92.070096, "o", "Found 3 dust UTXOs to sweep:\r\n 500 sats | 84e32362d15b14d069934ef2fe7a5d7864323d59f6936ef361bce812c2a8c10e:0\r\n 300 sats | 1b6cceaba9b1555ad385f3273e3a726a7f3e4a7d1091128d8628caa38d8514cd:0\r\n 800 sats | 1a51b81e315379f2b036f5bdaf4862edfe3b3527c7596c475918e7f8b97ba6a1:0\r\n\r\n🔍 Dry Run — no PSBT created\r\n\r\n Method: Consolidate\r\n Dust inputs: 3\r\n Total dust: 1600 sats\r\n Funder UTXO: 5000000000 sats\r\n Estimated fee: 626 sats\r\n Estimated output: 5000000974 sats\r\n\r\n Run without --dry-run to create the PSBT.\r\n"]
121+
[92.071323, "o", "\u001b[?2004h\u001b]0;jolah1@DESKTOP-N4M1LE0: ~/dev/dust-cleaner\u0007\u001b[01;32mjolah1@DESKTOP-N4M1LE0\u001b[00m:\u001b[01;34m~/dev/dust-cleaner\u001b[00m$ "]
122+
[116.248237, "o", "\u001b[7mcargo run -- --rpc-user user --rpc-pass\u001b[27m\u001b[7m \u001b[27m\u001b[7mpass --threshold 1000 sweep --method op-return --dry-run\u001b[27m"]
123+
[118.796131, "o", "\u0007\u001b[A\b\b\b\b\b\b\b\b\b\b\b\b\b\bcargo run -- --rpc-user user --rpc-pass pass --threshold 1000 sweep --method op-return --dry-run"]
124+
[119.62632, "o", "\r\n"]
125+
[119.626848, "o", "\u001b[?2004l\r"]
126+
[119.78859, "o", "\u001b[1m\u001b[92m Finished\u001b[0m `dev` profile [unoptimized + debuginfo] target(s) in 0.07s\r\n"]
127+
[119.794165, "o", "\u001b[1m\u001b[92m Running\u001b[0m `target/debug/dust-cleaner --rpc-user user --rpc-pass pass --threshold 1000 sweep --method op-return --dry-run`\r\n"]
128+
[119.815471, "o", "Found 3 dust UTXOs to sweep:\r\n 500 sats | 84e32362d15b14d069934ef2fe7a5d7864323d59f6936ef361bce812c2a8c10e:0\r\n 300 sats | 1b6cceaba9b1555ad385f3273e3a726a7f3e4a7d1091128d8628caa38d8514cd:0\r\n 800 sats | 1a51b81e315379f2b036f5bdaf4862edfe3b3527c7596c475918e7f8b97ba6a1:0\r\n\r\n🔍 Dry Run — no PSBT created\r\n\r\n Method: OpReturn\r\n Dust inputs: 3\r\n Total dust: 1600 sats\r\n Funder UTXO: 5000000000 sats\r\n Estimated fee: 626 sats\r\n Estimated output: 5000000974 sats\r\n\r\n Run without --dry-run to create the PSBT.\r\n"]
129+
[119.81687, "o", "\u001b[?2004h\u001b]0;jolah1@DESKTOP-N4M1LE0: ~/dev/dust-cleaner\u0007\u001b[01;32mjolah1@DESKTOP-N4M1LE0\u001b[00m:\u001b[01;34m~/dev/dust-cleaner\u001b[00m$ "]
130+
[138.60205, "o", "\u001b[7mcargo run -- --rpc-user user --rpc-pass\u001b[27m\u001b[7m \u001b[27m\u001b[7mpass --threshold 1000 sweep --method op-return\u001b[27m"]
131+
[139.284238, "o", "\u0007\u001b[A\b\b\b\bcargo run -- --rpc-user user --rpc-pass pass --threshold 1000 sweep --method op-return"]
132+
[142.210555, "o", "\r\n"]
133+
[142.21145, "o", "\u001b[?2004l\r"]
134+
[142.36781, "o", "\u001b[1m\u001b[92m Finished\u001b[0m `dev` profile [unoptimized + debuginfo] target(s) in 0.07s\r\n"]
135+
[142.373133, "o", "\u001b[1m\u001b[92m Running\u001b[0m `target/debug/dust-cleaner --rpc-user user --rpc-pass pass --threshold 1000 sweep --method op-return`\r\n"]
136+
[142.406946, "o", "Found 3 dust UTXOs to sweep:\r\n 500 sats | 84e32362d15b14d069934ef2fe7a5d7864323d59f6936ef361bce812c2a8c10e:0\r\n 300 sats | 1b6cceaba9b1555ad385f3273e3a726a7f3e4a7d1091128d8628caa38d8514cd:0\r\n 800 sats | 1a51b81e315379f2b036f5bdaf4862edfe3b3527c7596c475918e7f8b97ba6a1:0\r\n\r\n🔥 Method: op-return — dust burned to miner fees\r\n Output: OP_RETURN (\"ash\" — ashes to ashes, dust to dust)\r\n\r\n ℹ️ Using clean UTXO to fund fees: 5000000000 sats\r\n"]
137+
[142.54227, "o", "\r\n📊 Sweep Summary:\r\n Dust inputs: 3\r\n Total dust: 1600 sats\r\n\r\n🧹 Sweep PSBT (base64):\r\ncHNidP8BAPoCAAAABEAnhaYWjJMUomDpSiVnZFwrNySEYR1z5X0Q0VVpx4mHAAAAAAD9////DsGowhLovGHzbpP2WT0yZHhdev7yTpNp0BRb0WIj44QAAAAAAP3////NFIWNo8ooho0SkRB9Sj5/anI6PifzhdNaVbGpq85sGwAAAAAA/f///6Gme7n45xhZR2xZxyc1O/7tYkivvfU2sPJ5UzEeuFEaAAAAAAD9////AwAAAAAAAAAABWoDYXNoQAYAAAAAAAAWABSttCLfgG4/NURqfAHEpBsd1fcygQTkBSoBAAAAFgAUQJeNT5jyl6LaNiWtzN2MWndlLLYAAAAAAAEAgwIAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////AlUA/////wIA8gUqAQAAABYAFF6XxDXy7piDz1AhPspCGSm9yBVQAAAAAAAAAAAmaiSqIant4vYcP3HR3v0/qZnfo2lTdVxpBol5mWK0i+vYNpdOjPkAAAAAAQEfAPIFKgEAAAAWABRel8Q18u6Yg89QIT7KQhkpvcgVUCIGAoENJdbT6mMTuD2hDkCwv1V4S5QlOyEdjjW5Ppn9OjIqGJi7BkpUAACAAQAAgAAAAIAAAAAAAAAAAAABAHECAAAAAe4a44e+jKVcfWdeXARqGank2Mtorlv5e/5x4ZMeK5Q4AAAAAAD9////AvQBAAAAAAAAFgAUqZQBa74w+8qe6fkQG7PUZtxtPZr+igUqAQAAABYAFDfwRlg6Y/RLC+tMJGvwLMmNTq8AaQAAAAEBH/QBAAAAAAAAFgAUqZQBa74w+8qe6fkQG7PUZtxtPZoiBgMJaNSmn50loDjR98nh/gMrTvFXaGo7NPI2Xl19VMnY6xiYuwZKVAAAgAEAAIAAAACAAAAAABkAAAAAAQBSAgAAAAHsI1uFz5DnI/y7jeQT9iTIKt0jaOpEnApHws6hSNGO8AIAAAAA/f///wEsAQAAAAAAABYAFNKgYuUOdQ/Ie4FSaOj9KmQDkg5AaQAAAAEBHywBAAAAAAAAFgAU0qBi5Q51D8h7gVJo6P0qZAOSDkAiBgLzk3PR1gOWaC0X7bvc/nHr3+5wc5+zPvgPYa1F6FzImhiYuwZKVAAAgAEAAIAAAACAAAAAABgAAAAAAQBxAgAAAAHsI1uFz5DnI/y7jeQT9iTIKt0jaOpEnApHws6hSNGO8AEAAAAA/f///wIgAwAAAAAAABYAFCWjDqqhmkeTHRPP2ya7Kpny15LyYtsFKgEAAAAWABTLA2JCoOsM139b+ft5JSupavi5H2kAAAABAR8gAwAAAAAAABYAFCWjDqqhmkeTHRPP2ya7Kpny15LyIgYDSYFWXz0zDtQ8nb819SBLfP+t/N6mCOsWq2X0ROrHHtkYmLsGSlQAAIABAACAAAAAgAAAAAAaAAAAAAAiAgNjLZZToK8U19OxlBXF1gJK6lJaPQ7gG6/izvhnnt4m6RiYuwZKVAAAgAEAAIAAAACAAQAAABIAAAAAIgICS/cUTihGMLrbjuES/wYIQR9uEn88RIEQ3tD9NdHVr08YmLsGSlQAAIABAACAAAAAgAAAAAAbAAAAAA==\r\n\r\n💡 Next steps:\r\n Inspect: bitcoin-cli -rpcport=18443 -rpcuser=user -rpcpassword=pass decodepsbt <psbt>\r\n Sign: bitcoin-cli -rpcport=18443 -rpcuser=user -rpcpassword=pass walletprocesspsbt <psbt>\r\n Send: bitcoin-cli -rpcport=18443 -rpcuser=user -rpcpassword=pass sendrawtransaction <hex>\r\n"]
138+
[142.54348, "o", "\u001b[?2004h\u001b]0;jolah1@DESKTOP-N4M1LE0: ~/dev/dust-cleaner\u0007\u001b[01;32mjolah1@DESKTOP-N4M1LE0\u001b[00m:\u001b[01;34m~/dev/dust-cleaner\u001b[00m$ "]
139+
[150.79263, "r", "170x44"]
140+
[150.79337, "o", "\r\u001b[K\r\u001b]0;jolah1@DESKTOP-N4M1LE0: ~/dev/dust-cleaner\u0007\u001b[01;32mjolah1@DESKTOP-N4M1LE0\u001b[00m:\u001b[01;34m~/dev/dust-cleaner\u001b[00m$ "]
141+
[171.879902, "o", "\u001b[?2004l\r\r\nexit\r\n"]

0 commit comments

Comments
 (0)