@kevinlilili/agent-wallet 0.1.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.
- package/index.js +282 -0
- package/package.json +22 -0
package/index.js
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Agent Wallet Scanner CLI
|
|
4
|
+
*
|
|
5
|
+
* Scans local MCP wallet configs, discovers all agent wallet addresses,
|
|
6
|
+
* and opens the dashboard with pre-populated wallets.
|
|
7
|
+
*
|
|
8
|
+
* Usage: npx agent-wallet
|
|
9
|
+
*
|
|
10
|
+
* Security: ONLY reads public addresses. Never reads private keys.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { readFileSync, existsSync, readdirSync } = require("fs");
|
|
14
|
+
const { homedir } = require("os");
|
|
15
|
+
const { join } = require("path");
|
|
16
|
+
const { execSync } = require("child_process");
|
|
17
|
+
|
|
18
|
+
const HOME = homedir();
|
|
19
|
+
|
|
20
|
+
function readJson(path) {
|
|
21
|
+
try {
|
|
22
|
+
if (!existsSync(path)) return null;
|
|
23
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
24
|
+
} catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// ── Wallet Discoverers ──────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
function scanAgentCash() {
|
|
32
|
+
const wallets = [];
|
|
33
|
+
|
|
34
|
+
const evmData = readJson(join(HOME, ".agentcash", "wallet.json"));
|
|
35
|
+
if (evmData && typeof evmData.address === "string") {
|
|
36
|
+
wallets.push({ provider: "AgentCash", address: evmData.address, chain: "evm" });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const solData = readJson(join(HOME, ".agentcash", "solana-wallet.json"));
|
|
40
|
+
if (solData && typeof solData.address === "string") {
|
|
41
|
+
wallets.push({ provider: "AgentCash (SOL)", address: solData.address, chain: "solana" });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return wallets;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function scanSponge() {
|
|
48
|
+
const wallets = [];
|
|
49
|
+
|
|
50
|
+
// Sponge stores wallet in ~/.sponge/ or via MCP config env vars
|
|
51
|
+
const spongeWallet = readJson(join(HOME, ".sponge", "wallet.json"));
|
|
52
|
+
if (spongeWallet && typeof spongeWallet.address === "string") {
|
|
53
|
+
wallets.push({ provider: "Sponge", address: spongeWallet.address, chain: "evm" });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Check MCP configs for Sponge server env vars
|
|
57
|
+
const mcpConfigs = [
|
|
58
|
+
join(HOME, "Library", "Application Support", "Claude", "claude_desktop_config.json"),
|
|
59
|
+
join(HOME, ".cursor", "mcp.json"),
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
for (const configPath of mcpConfigs) {
|
|
63
|
+
const data = readJson(configPath);
|
|
64
|
+
if (!data) continue;
|
|
65
|
+
const servers = data.mcpServers;
|
|
66
|
+
if (!servers) continue;
|
|
67
|
+
|
|
68
|
+
for (const [name, config] of Object.entries(servers)) {
|
|
69
|
+
if (!name.toLowerCase().includes("sponge")) continue;
|
|
70
|
+
const env = config.env;
|
|
71
|
+
if (!env) continue;
|
|
72
|
+
|
|
73
|
+
// Look for wallet address in env vars
|
|
74
|
+
for (const [key, val] of Object.entries(env)) {
|
|
75
|
+
if (typeof val === "string" && val.startsWith("0x") && val.length === 42 &&
|
|
76
|
+
(key.includes("ADDRESS") || key.includes("WALLET"))) {
|
|
77
|
+
wallets.push({ provider: `Sponge (${name})`, address: val, chain: "evm" });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return wallets;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function scanCoinbaseAgentKit() {
|
|
87
|
+
const wallets = [];
|
|
88
|
+
|
|
89
|
+
// CDP wallet data
|
|
90
|
+
const cdpPath = join(HOME, ".cdp", "wallet_data.json");
|
|
91
|
+
const cdpData = readJson(cdpPath);
|
|
92
|
+
if (cdpData && typeof cdpData.address === "string") {
|
|
93
|
+
wallets.push({ provider: "CDP", address: cdpData.address, chain: "evm" });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// wallet_data_*.txt files in common project dirs
|
|
97
|
+
const searchDirs = [
|
|
98
|
+
HOME,
|
|
99
|
+
join(HOME, "Projects"),
|
|
100
|
+
join(HOME, "projects"),
|
|
101
|
+
join(HOME, "code"),
|
|
102
|
+
join(HOME, "dev"),
|
|
103
|
+
];
|
|
104
|
+
|
|
105
|
+
for (const dir of searchDirs) {
|
|
106
|
+
if (!existsSync(dir)) continue;
|
|
107
|
+
try {
|
|
108
|
+
for (const file of readdirSync(dir)) {
|
|
109
|
+
if (!file.startsWith("wallet_data_") || !file.endsWith(".txt")) continue;
|
|
110
|
+
const data = readJson(join(dir, file));
|
|
111
|
+
if (!data) continue;
|
|
112
|
+
const address = data.smart_wallet_address || data.address;
|
|
113
|
+
if (address && typeof address === "string" && address.startsWith("0x")) {
|
|
114
|
+
const network = data.network_id || file.replace("wallet_data_", "").replace(".txt", "");
|
|
115
|
+
wallets.push({ provider: `AgentKit (${network})`, address, chain: "evm" });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
} catch { /* skip unreadable dirs */ }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return wallets;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function scanWalletAgent() {
|
|
125
|
+
const wallets = [];
|
|
126
|
+
const keysPath = join(HOME, ".wallet-agent", "auth", "encrypted-keys.json");
|
|
127
|
+
const data = readJson(keysPath);
|
|
128
|
+
if (!data || !data.keys || typeof data.keys !== "object") return wallets;
|
|
129
|
+
|
|
130
|
+
for (const address of Object.keys(data.keys)) {
|
|
131
|
+
if (address.startsWith("0x") && address.length === 42) {
|
|
132
|
+
wallets.push({ provider: "wallet-agent", address, chain: "evm" });
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return wallets;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function scanOpenClaw() {
|
|
139
|
+
const wallets = [];
|
|
140
|
+
const walletsDir = join(HOME, ".openclaw", "crossmint-wallets");
|
|
141
|
+
if (!existsSync(walletsDir)) return wallets;
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
for (const file of readdirSync(walletsDir)) {
|
|
145
|
+
const data = readJson(join(walletsDir, file));
|
|
146
|
+
if (data && typeof data.publicKey === "string") {
|
|
147
|
+
wallets.push({ provider: "OpenClaw", address: data.publicKey, chain: "solana" });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
} catch { /* skip */ }
|
|
151
|
+
return wallets;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function scanLatinum() {
|
|
155
|
+
const wallets = [];
|
|
156
|
+
const configPath = join(HOME, ".latinum", "config.json");
|
|
157
|
+
const data = readJson(configPath);
|
|
158
|
+
if (data && typeof data.address === "string") {
|
|
159
|
+
wallets.push({ provider: "Latinum", address: data.address, chain: "evm" });
|
|
160
|
+
}
|
|
161
|
+
return wallets;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function scanEnvFiles() {
|
|
165
|
+
// Scan .env files in CWD for wallet addresses
|
|
166
|
+
const wallets = [];
|
|
167
|
+
const envPath = join(process.cwd(), ".env");
|
|
168
|
+
if (!existsSync(envPath)) return wallets;
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
const lines = readFileSync(envPath, "utf-8").split("\n");
|
|
172
|
+
for (const line of lines) {
|
|
173
|
+
const match = line.match(/^([A-Z_]*(?:ADDRESS|WALLET)[A-Z_]*)=["']?(0x[a-fA-F0-9]{40})["']?/);
|
|
174
|
+
if (match) {
|
|
175
|
+
wallets.push({ provider: match[1], address: match[2], chain: "evm" });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
} catch { /* skip */ }
|
|
179
|
+
return wallets;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ── Output helpers ──────────────────────────────────────────────
|
|
183
|
+
|
|
184
|
+
const RESET = "\x1b[0m";
|
|
185
|
+
const BOLD = "\x1b[1m";
|
|
186
|
+
const DIM = "\x1b[2m";
|
|
187
|
+
const GREEN = "\x1b[32m";
|
|
188
|
+
const CYAN = "\x1b[36m";
|
|
189
|
+
const YELLOW = "\x1b[33m";
|
|
190
|
+
|
|
191
|
+
function shortAddr(addr) {
|
|
192
|
+
return addr.length > 16 ? addr.slice(0, 6) + "..." + addr.slice(-4) : addr;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ── Main ────────────────────────────────────────────────────────
|
|
196
|
+
|
|
197
|
+
function main() {
|
|
198
|
+
const args = process.argv.slice(2);
|
|
199
|
+
const dashboardUrl = args.find((a) => a.startsWith("--url="))?.split("=")[1]
|
|
200
|
+
|| process.env.DASHBOARD_URL
|
|
201
|
+
|| "https://agent-wallet-dashboard.vercel.app";
|
|
202
|
+
const noBrowser = args.includes("--no-open");
|
|
203
|
+
const jsonOutput = args.includes("--json");
|
|
204
|
+
|
|
205
|
+
console.log("");
|
|
206
|
+
console.log(` ${BOLD}Agent Wallet Scanner${RESET}`);
|
|
207
|
+
console.log(` ${DIM}Scanning local configs for agent wallet addresses...${RESET}`);
|
|
208
|
+
console.log("");
|
|
209
|
+
|
|
210
|
+
const allWallets = [];
|
|
211
|
+
const scanners = [
|
|
212
|
+
{ name: "AgentCash", fn: scanAgentCash },
|
|
213
|
+
{ name: "Sponge", fn: scanSponge },
|
|
214
|
+
{ name: "Coinbase AgentKit", fn: scanCoinbaseAgentKit },
|
|
215
|
+
{ name: "wallet-agent", fn: scanWalletAgent },
|
|
216
|
+
{ name: "OpenClaw", fn: scanOpenClaw },
|
|
217
|
+
{ name: "Latinum", fn: scanLatinum },
|
|
218
|
+
{ name: ".env file", fn: scanEnvFiles },
|
|
219
|
+
];
|
|
220
|
+
|
|
221
|
+
for (const scanner of scanners) {
|
|
222
|
+
const found = scanner.fn();
|
|
223
|
+
if (found.length > 0) {
|
|
224
|
+
console.log(` ${GREEN}✓${RESET} ${scanner.name}: ${found.length} wallet(s)`);
|
|
225
|
+
allWallets.push(...found);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Deduplicate by address
|
|
230
|
+
const unique = Array.from(new Map(allWallets.map((w) => [w.address, w])).values());
|
|
231
|
+
|
|
232
|
+
if (unique.length === 0) {
|
|
233
|
+
console.log(` ${DIM}No agent wallets found.${RESET}`);
|
|
234
|
+
console.log("");
|
|
235
|
+
console.log(` ${DIM}Tip: Install an agent wallet provider (AgentCash, Sponge, etc.)${RESET}`);
|
|
236
|
+
console.log(` ${DIM}or open the dashboard manually: ${dashboardUrl}${RESET}`);
|
|
237
|
+
console.log("");
|
|
238
|
+
process.exit(0);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
console.log("");
|
|
242
|
+
console.log(` ${BOLD}${unique.length} wallet(s) found${RESET}`);
|
|
243
|
+
console.log("");
|
|
244
|
+
|
|
245
|
+
for (const w of unique) {
|
|
246
|
+
const badge = w.chain === "solana" ? `${YELLOW}SOL${RESET}` : `${CYAN}EVM${RESET}`;
|
|
247
|
+
console.log(` ${w.provider.padEnd(20)} ${DIM}${shortAddr(w.address)}${RESET} ${badge}`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// JSON output mode (for piping)
|
|
251
|
+
if (jsonOutput) {
|
|
252
|
+
console.log("");
|
|
253
|
+
console.log(JSON.stringify(unique, null, 2));
|
|
254
|
+
process.exit(0);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Build dashboard URL
|
|
258
|
+
const params = unique
|
|
259
|
+
.map((w) => `w=${encodeURIComponent(`${w.provider}:${w.address}`)}`)
|
|
260
|
+
.join("&");
|
|
261
|
+
const url = `${dashboardUrl}?${params}`;
|
|
262
|
+
|
|
263
|
+
console.log("");
|
|
264
|
+
console.log(` ${DIM}Dashboard:${RESET} ${url}`);
|
|
265
|
+
console.log("");
|
|
266
|
+
|
|
267
|
+
if (noBrowser) {
|
|
268
|
+
process.exit(0);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Open in default browser
|
|
272
|
+
try {
|
|
273
|
+
const platform = process.platform;
|
|
274
|
+
if (platform === "darwin") execSync(`open "${url}"`);
|
|
275
|
+
else if (platform === "linux") execSync(`xdg-open "${url}"`);
|
|
276
|
+
else if (platform === "win32") execSync(`start "" "${url}"`);
|
|
277
|
+
} catch {
|
|
278
|
+
console.log(` ${DIM}Could not open browser. Copy the URL above.${RESET}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kevinlilili/agent-wallet",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Scan your machine for AI agent wallets and see them in one dashboard",
|
|
5
|
+
"bin": {
|
|
6
|
+
"agent-wallet": "index.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"index.js"
|
|
10
|
+
],
|
|
11
|
+
"keywords": [
|
|
12
|
+
"ai",
|
|
13
|
+
"agent",
|
|
14
|
+
"wallet",
|
|
15
|
+
"crypto",
|
|
16
|
+
"mcp",
|
|
17
|
+
"agentcash",
|
|
18
|
+
"sponge",
|
|
19
|
+
"coinbase"
|
|
20
|
+
],
|
|
21
|
+
"license": "MIT"
|
|
22
|
+
}
|