@hasna/wallets 0.1.0 → 0.1.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/package.json +1 -1
- package/dist/cli/index.d.ts +0 -3
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js +0 -1082
- package/dist/db/agents.d.ts +0 -8
- package/dist/db/agents.d.ts.map +0 -1
- package/dist/db/cards.d.ts +0 -25
- package/dist/db/cards.d.ts.map +0 -1
- package/dist/db/database.d.ts +0 -9
- package/dist/db/database.d.ts.map +0 -1
- package/dist/db/providers.d.ts +0 -13
- package/dist/db/providers.d.ts.map +0 -1
- package/dist/db/transactions.d.ts +0 -18
- package/dist/db/transactions.d.ts.map +0 -1
- package/dist/index.d.ts +0 -13
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -869
- package/dist/lib/config.d.ts +0 -13
- package/dist/lib/config.d.ts.map +0 -1
- package/dist/lib/doctor.d.ts +0 -3
- package/dist/lib/doctor.d.ts.map +0 -1
- package/dist/lib/format.d.ts +0 -7
- package/dist/lib/format.d.ts.map +0 -1
- package/dist/mcp/index.d.ts +0 -3
- package/dist/mcp/index.d.ts.map +0 -1
- package/dist/mcp/index.js +0 -5069
- package/dist/providers/agentcard.d.ts +0 -25
- package/dist/providers/agentcard.d.ts.map +0 -1
- package/dist/providers/index.d.ts +0 -11
- package/dist/providers/index.d.ts.map +0 -1
- package/dist/providers/registry.d.ts +0 -6
- package/dist/providers/registry.d.ts.map +0 -1
- package/dist/types/index.d.ts +0 -215
- package/dist/types/index.d.ts.map +0 -1
package/dist/cli/index.js
DELETED
|
@@ -1,1082 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bun
|
|
2
|
-
// @bun
|
|
3
|
-
|
|
4
|
-
// src/cli/index.ts
|
|
5
|
-
import { Command } from "commander";
|
|
6
|
-
import chalk from "chalk";
|
|
7
|
-
|
|
8
|
-
// src/db/database.ts
|
|
9
|
-
import { Database } from "bun:sqlite";
|
|
10
|
-
import { homedir } from "os";
|
|
11
|
-
import { join } from "path";
|
|
12
|
-
import { existsSync, mkdirSync } from "fs";
|
|
13
|
-
var _db = null;
|
|
14
|
-
var MIGRATIONS = [
|
|
15
|
-
{
|
|
16
|
-
id: 1,
|
|
17
|
-
sql: `
|
|
18
|
-
CREATE TABLE IF NOT EXISTS providers (
|
|
19
|
-
id TEXT PRIMARY KEY,
|
|
20
|
-
name TEXT NOT NULL UNIQUE,
|
|
21
|
-
type TEXT NOT NULL,
|
|
22
|
-
status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'inactive', 'error')),
|
|
23
|
-
config TEXT DEFAULT '{}',
|
|
24
|
-
metadata TEXT DEFAULT '{}',
|
|
25
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
26
|
-
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
CREATE TABLE IF NOT EXISTS cards (
|
|
30
|
-
id TEXT PRIMARY KEY,
|
|
31
|
-
provider_id TEXT NOT NULL REFERENCES providers(id) ON DELETE CASCADE,
|
|
32
|
-
external_id TEXT NOT NULL,
|
|
33
|
-
name TEXT NOT NULL,
|
|
34
|
-
last_four TEXT NOT NULL DEFAULT '',
|
|
35
|
-
brand TEXT NOT NULL DEFAULT 'visa',
|
|
36
|
-
status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('active', 'frozen', 'closed', 'pending')),
|
|
37
|
-
currency TEXT NOT NULL DEFAULT 'USD',
|
|
38
|
-
balance REAL NOT NULL DEFAULT 0,
|
|
39
|
-
funded_amount REAL NOT NULL DEFAULT 0,
|
|
40
|
-
spending_limit REAL,
|
|
41
|
-
agent_id TEXT,
|
|
42
|
-
metadata TEXT DEFAULT '{}',
|
|
43
|
-
expires_at TEXT,
|
|
44
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
45
|
-
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
46
|
-
);
|
|
47
|
-
|
|
48
|
-
CREATE TABLE IF NOT EXISTS transactions (
|
|
49
|
-
id TEXT PRIMARY KEY,
|
|
50
|
-
card_id TEXT NOT NULL REFERENCES cards(id) ON DELETE CASCADE,
|
|
51
|
-
provider_id TEXT NOT NULL REFERENCES providers(id) ON DELETE CASCADE,
|
|
52
|
-
external_id TEXT,
|
|
53
|
-
type TEXT NOT NULL CHECK(type IN ('purchase', 'refund', 'load', 'withdrawal', 'fee')),
|
|
54
|
-
status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending', 'completed', 'failed', 'reversed')),
|
|
55
|
-
amount REAL NOT NULL,
|
|
56
|
-
currency TEXT NOT NULL DEFAULT 'USD',
|
|
57
|
-
merchant TEXT,
|
|
58
|
-
description TEXT,
|
|
59
|
-
metadata TEXT DEFAULT '{}',
|
|
60
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
CREATE TABLE IF NOT EXISTS agents (
|
|
64
|
-
id TEXT PRIMARY KEY,
|
|
65
|
-
name TEXT NOT NULL UNIQUE,
|
|
66
|
-
description TEXT,
|
|
67
|
-
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
|
68
|
-
last_seen_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
CREATE INDEX IF NOT EXISTS idx_cards_provider ON cards(provider_id);
|
|
72
|
-
CREATE INDEX IF NOT EXISTS idx_cards_agent ON cards(agent_id);
|
|
73
|
-
CREATE INDEX IF NOT EXISTS idx_cards_status ON cards(status);
|
|
74
|
-
CREATE INDEX IF NOT EXISTS idx_transactions_card ON transactions(card_id);
|
|
75
|
-
CREATE INDEX IF NOT EXISTS idx_transactions_provider ON transactions(provider_id);
|
|
76
|
-
CREATE INDEX IF NOT EXISTS idx_transactions_type ON transactions(type);
|
|
77
|
-
|
|
78
|
-
CREATE TABLE IF NOT EXISTS _migrations (
|
|
79
|
-
id INTEGER PRIMARY KEY,
|
|
80
|
-
applied_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
81
|
-
);
|
|
82
|
-
`
|
|
83
|
-
}
|
|
84
|
-
];
|
|
85
|
-
function runMigrations(db) {
|
|
86
|
-
db.run(`CREATE TABLE IF NOT EXISTS _migrations (
|
|
87
|
-
id INTEGER PRIMARY KEY,
|
|
88
|
-
applied_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
89
|
-
)`);
|
|
90
|
-
for (const migration of MIGRATIONS) {
|
|
91
|
-
const applied = db.query("SELECT id FROM _migrations WHERE id = ?").get(migration.id);
|
|
92
|
-
if (!applied) {
|
|
93
|
-
db.run("BEGIN");
|
|
94
|
-
try {
|
|
95
|
-
db.run(migration.sql);
|
|
96
|
-
db.run("INSERT INTO _migrations (id) VALUES (?)", [migration.id]);
|
|
97
|
-
db.run("COMMIT");
|
|
98
|
-
} catch (e) {
|
|
99
|
-
db.run("ROLLBACK");
|
|
100
|
-
throw e;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
function resolveDbPath() {
|
|
106
|
-
const envPath = process.env["WALLETS_DB_PATH"];
|
|
107
|
-
if (envPath)
|
|
108
|
-
return envPath;
|
|
109
|
-
const dir = join(homedir(), ".wallets");
|
|
110
|
-
if (!existsSync(dir)) {
|
|
111
|
-
mkdirSync(dir, { recursive: true });
|
|
112
|
-
}
|
|
113
|
-
return join(dir, "wallets.db");
|
|
114
|
-
}
|
|
115
|
-
function getDatabase(dbPath) {
|
|
116
|
-
if (_db)
|
|
117
|
-
return _db;
|
|
118
|
-
const path = dbPath || resolveDbPath();
|
|
119
|
-
_db = new Database(path);
|
|
120
|
-
_db.run("PRAGMA journal_mode = WAL");
|
|
121
|
-
_db.run("PRAGMA foreign_keys = ON");
|
|
122
|
-
_db.run("PRAGMA busy_timeout = 5000");
|
|
123
|
-
runMigrations(_db);
|
|
124
|
-
return _db;
|
|
125
|
-
}
|
|
126
|
-
function now() {
|
|
127
|
-
return new Date().toISOString();
|
|
128
|
-
}
|
|
129
|
-
function uuid() {
|
|
130
|
-
return crypto.randomUUID();
|
|
131
|
-
}
|
|
132
|
-
function shortId() {
|
|
133
|
-
return crypto.randomUUID().slice(0, 8);
|
|
134
|
-
}
|
|
135
|
-
function resolvePartialId(db, table, partialId) {
|
|
136
|
-
const rows = db.query(`SELECT id FROM ${table} WHERE id LIKE ?`).all(`${partialId}%`);
|
|
137
|
-
if (rows.length === 1 && rows[0])
|
|
138
|
-
return rows[0].id;
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// src/db/providers.ts
|
|
143
|
-
function rowToProvider(row) {
|
|
144
|
-
return {
|
|
145
|
-
...row,
|
|
146
|
-
status: row.status,
|
|
147
|
-
config: JSON.parse(row.config || "{}"),
|
|
148
|
-
metadata: JSON.parse(row.metadata || "{}")
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
function createProvider(input, db) {
|
|
152
|
-
const d = db || getDatabase();
|
|
153
|
-
const id = shortId();
|
|
154
|
-
d.run(`INSERT INTO providers (id, name, type, config, metadata) VALUES (?, ?, ?, ?, ?)`, [
|
|
155
|
-
id,
|
|
156
|
-
input.name,
|
|
157
|
-
input.type,
|
|
158
|
-
JSON.stringify(input.config || {}),
|
|
159
|
-
JSON.stringify(input.metadata || {})
|
|
160
|
-
]);
|
|
161
|
-
return getProvider(id, d);
|
|
162
|
-
}
|
|
163
|
-
function getProvider(id, db) {
|
|
164
|
-
const d = db || getDatabase();
|
|
165
|
-
const row = d.query("SELECT * FROM providers WHERE id = ?").get(id);
|
|
166
|
-
return row ? rowToProvider(row) : null;
|
|
167
|
-
}
|
|
168
|
-
function getProviderByName(name, db) {
|
|
169
|
-
const d = db || getDatabase();
|
|
170
|
-
const row = d.query("SELECT * FROM providers WHERE name = ?").get(name);
|
|
171
|
-
return row ? rowToProvider(row) : null;
|
|
172
|
-
}
|
|
173
|
-
function listProviders(db) {
|
|
174
|
-
const d = db || getDatabase();
|
|
175
|
-
const rows = d.query("SELECT * FROM providers ORDER BY created_at DESC").all();
|
|
176
|
-
return rows.map(rowToProvider);
|
|
177
|
-
}
|
|
178
|
-
function updateProvider(id, input, db) {
|
|
179
|
-
const d = db || getDatabase();
|
|
180
|
-
const provider = getProvider(id, d);
|
|
181
|
-
if (!provider)
|
|
182
|
-
throw new Error(`Provider not found: ${id}`);
|
|
183
|
-
const sets = [];
|
|
184
|
-
const params = [];
|
|
185
|
-
if (input.name !== undefined) {
|
|
186
|
-
sets.push("name = ?");
|
|
187
|
-
params.push(input.name);
|
|
188
|
-
}
|
|
189
|
-
if (input.config !== undefined) {
|
|
190
|
-
sets.push("config = ?");
|
|
191
|
-
params.push(JSON.stringify(input.config));
|
|
192
|
-
}
|
|
193
|
-
if (input.metadata !== undefined) {
|
|
194
|
-
sets.push("metadata = ?");
|
|
195
|
-
params.push(JSON.stringify(input.metadata));
|
|
196
|
-
}
|
|
197
|
-
if (input.status !== undefined) {
|
|
198
|
-
sets.push("status = ?");
|
|
199
|
-
params.push(input.status);
|
|
200
|
-
}
|
|
201
|
-
if (sets.length > 0) {
|
|
202
|
-
sets.push("updated_at = ?");
|
|
203
|
-
params.push(now());
|
|
204
|
-
params.push(id);
|
|
205
|
-
d.run(`UPDATE providers SET ${sets.join(", ")} WHERE id = ?`, params);
|
|
206
|
-
}
|
|
207
|
-
return getProvider(id, d);
|
|
208
|
-
}
|
|
209
|
-
function deleteProvider(id, db) {
|
|
210
|
-
const d = db || getDatabase();
|
|
211
|
-
return d.run("DELETE FROM providers WHERE id = ?", [id]).changes > 0;
|
|
212
|
-
}
|
|
213
|
-
function ensureProvider(name, type, config, db) {
|
|
214
|
-
const d = db || getDatabase();
|
|
215
|
-
const existing = getProviderByName(name, d);
|
|
216
|
-
if (existing) {
|
|
217
|
-
if (config) {
|
|
218
|
-
return updateProvider(existing.id, { config }, d);
|
|
219
|
-
}
|
|
220
|
-
d.run("UPDATE providers SET updated_at = ? WHERE id = ?", [now(), existing.id]);
|
|
221
|
-
return getProvider(existing.id, d);
|
|
222
|
-
}
|
|
223
|
-
return createProvider({ name, type, config }, d);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// src/db/cards.ts
|
|
227
|
-
function rowToCard(row) {
|
|
228
|
-
return {
|
|
229
|
-
...row,
|
|
230
|
-
status: row.status,
|
|
231
|
-
currency: row.currency,
|
|
232
|
-
metadata: JSON.parse(row.metadata || "{}")
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
function createCardRecord(input, db) {
|
|
236
|
-
const d = db || getDatabase();
|
|
237
|
-
const id = uuid();
|
|
238
|
-
d.run(`INSERT INTO cards (id, provider_id, external_id, name, last_four, brand, status, currency, balance, funded_amount, spending_limit, agent_id, metadata, expires_at)
|
|
239
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
240
|
-
id,
|
|
241
|
-
input.provider_id,
|
|
242
|
-
input.external_id,
|
|
243
|
-
input.name,
|
|
244
|
-
input.last_four || "",
|
|
245
|
-
input.brand || "visa",
|
|
246
|
-
input.status || "pending",
|
|
247
|
-
input.currency || "USD",
|
|
248
|
-
input.balance ?? 0,
|
|
249
|
-
input.funded_amount ?? 0,
|
|
250
|
-
input.spending_limit ?? null,
|
|
251
|
-
input.agent_id ?? null,
|
|
252
|
-
JSON.stringify(input.metadata || {}),
|
|
253
|
-
input.expires_at ?? null
|
|
254
|
-
]);
|
|
255
|
-
return getCard(id, d);
|
|
256
|
-
}
|
|
257
|
-
function getCard(id, db) {
|
|
258
|
-
const d = db || getDatabase();
|
|
259
|
-
const row = d.query("SELECT * FROM cards WHERE id = ?").get(id);
|
|
260
|
-
return row ? rowToCard(row) : null;
|
|
261
|
-
}
|
|
262
|
-
function listCards(filter = {}, db) {
|
|
263
|
-
const d = db || getDatabase();
|
|
264
|
-
const conditions = [];
|
|
265
|
-
const params = [];
|
|
266
|
-
if (filter.provider_id) {
|
|
267
|
-
conditions.push("provider_id = ?");
|
|
268
|
-
params.push(filter.provider_id);
|
|
269
|
-
}
|
|
270
|
-
if (filter.status) {
|
|
271
|
-
conditions.push("status = ?");
|
|
272
|
-
params.push(filter.status);
|
|
273
|
-
}
|
|
274
|
-
if (filter.agent_id) {
|
|
275
|
-
conditions.push("agent_id = ?");
|
|
276
|
-
params.push(filter.agent_id);
|
|
277
|
-
}
|
|
278
|
-
if (filter.currency) {
|
|
279
|
-
conditions.push("currency = ?");
|
|
280
|
-
params.push(filter.currency);
|
|
281
|
-
}
|
|
282
|
-
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
283
|
-
const limit = filter.limit ? `LIMIT ${filter.limit}` : "";
|
|
284
|
-
const offset = filter.offset ? `OFFSET ${filter.offset}` : "";
|
|
285
|
-
const rows = d.query(`SELECT * FROM cards ${where} ORDER BY created_at DESC ${limit} ${offset}`).all(...params);
|
|
286
|
-
return rows.map(rowToCard);
|
|
287
|
-
}
|
|
288
|
-
function updateCard(id, input, db) {
|
|
289
|
-
const d = db || getDatabase();
|
|
290
|
-
const card = getCard(id, d);
|
|
291
|
-
if (!card)
|
|
292
|
-
throw new Error(`Card not found: ${id}`);
|
|
293
|
-
const sets = [];
|
|
294
|
-
const params = [];
|
|
295
|
-
if (input.name !== undefined) {
|
|
296
|
-
sets.push("name = ?");
|
|
297
|
-
params.push(input.name);
|
|
298
|
-
}
|
|
299
|
-
if (input.status !== undefined) {
|
|
300
|
-
sets.push("status = ?");
|
|
301
|
-
params.push(input.status);
|
|
302
|
-
}
|
|
303
|
-
if (input.spending_limit !== undefined) {
|
|
304
|
-
sets.push("spending_limit = ?");
|
|
305
|
-
params.push(input.spending_limit);
|
|
306
|
-
}
|
|
307
|
-
if (input.metadata !== undefined) {
|
|
308
|
-
sets.push("metadata = ?");
|
|
309
|
-
params.push(JSON.stringify(input.metadata));
|
|
310
|
-
}
|
|
311
|
-
if (sets.length > 0) {
|
|
312
|
-
sets.push("updated_at = ?");
|
|
313
|
-
params.push(now());
|
|
314
|
-
params.push(id);
|
|
315
|
-
d.run(`UPDATE cards SET ${sets.join(", ")} WHERE id = ?`, params);
|
|
316
|
-
}
|
|
317
|
-
return getCard(id, d);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// src/db/transactions.ts
|
|
321
|
-
function rowToTransaction(row) {
|
|
322
|
-
return {
|
|
323
|
-
...row,
|
|
324
|
-
type: row.type,
|
|
325
|
-
status: row.status,
|
|
326
|
-
currency: row.currency,
|
|
327
|
-
metadata: JSON.parse(row.metadata || "{}")
|
|
328
|
-
};
|
|
329
|
-
}
|
|
330
|
-
function listTransactions(filter = {}, db) {
|
|
331
|
-
const d = db || getDatabase();
|
|
332
|
-
const conditions = [];
|
|
333
|
-
const params = [];
|
|
334
|
-
if (filter.card_id) {
|
|
335
|
-
conditions.push("card_id = ?");
|
|
336
|
-
params.push(filter.card_id);
|
|
337
|
-
}
|
|
338
|
-
if (filter.provider_id) {
|
|
339
|
-
conditions.push("provider_id = ?");
|
|
340
|
-
params.push(filter.provider_id);
|
|
341
|
-
}
|
|
342
|
-
if (filter.type) {
|
|
343
|
-
conditions.push("type = ?");
|
|
344
|
-
params.push(filter.type);
|
|
345
|
-
}
|
|
346
|
-
if (filter.status) {
|
|
347
|
-
conditions.push("status = ?");
|
|
348
|
-
params.push(filter.status);
|
|
349
|
-
}
|
|
350
|
-
const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
|
|
351
|
-
const limit = filter.limit ? `LIMIT ${filter.limit}` : "";
|
|
352
|
-
const offset = filter.offset ? `OFFSET ${filter.offset}` : "";
|
|
353
|
-
const rows = d.query(`SELECT * FROM transactions ${where} ORDER BY created_at DESC ${limit} ${offset}`).all(...params);
|
|
354
|
-
return rows.map(rowToTransaction);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// src/db/agents.ts
|
|
358
|
-
function rowToAgent(row) {
|
|
359
|
-
return { ...row };
|
|
360
|
-
}
|
|
361
|
-
function registerAgent(input, db) {
|
|
362
|
-
const d = db || getDatabase();
|
|
363
|
-
const normalizedName = input.name.trim().toLowerCase();
|
|
364
|
-
const existing = getAgentByName(normalizedName, d);
|
|
365
|
-
if (existing) {
|
|
366
|
-
d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), existing.id]);
|
|
367
|
-
return getAgent(existing.id, d);
|
|
368
|
-
}
|
|
369
|
-
const id = shortId();
|
|
370
|
-
d.run("INSERT INTO agents (id, name, description) VALUES (?, ?, ?)", [id, normalizedName, input.description ?? null]);
|
|
371
|
-
return getAgent(id, d);
|
|
372
|
-
}
|
|
373
|
-
function getAgent(id, db) {
|
|
374
|
-
const d = db || getDatabase();
|
|
375
|
-
const row = d.query("SELECT * FROM agents WHERE id = ?").get(id);
|
|
376
|
-
return row ? rowToAgent(row) : null;
|
|
377
|
-
}
|
|
378
|
-
function getAgentByName(name, db) {
|
|
379
|
-
const d = db || getDatabase();
|
|
380
|
-
const normalizedName = name.trim().toLowerCase();
|
|
381
|
-
const row = d.query("SELECT * FROM agents WHERE name = ?").get(normalizedName);
|
|
382
|
-
return row ? rowToAgent(row) : null;
|
|
383
|
-
}
|
|
384
|
-
function listAgents(db) {
|
|
385
|
-
const d = db || getDatabase();
|
|
386
|
-
const rows = d.query("SELECT * FROM agents ORDER BY last_seen_at DESC").all();
|
|
387
|
-
return rows.map(rowToAgent);
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
// src/types/index.ts
|
|
391
|
-
class ProviderError extends Error {
|
|
392
|
-
static code = "PROVIDER_ERROR";
|
|
393
|
-
static suggestion = "Check the provider configuration and API credentials. Run 'wallets doctor' for diagnostics.";
|
|
394
|
-
constructor(provider, message) {
|
|
395
|
-
super(`Provider '${provider}' error: ${message}`);
|
|
396
|
-
this.name = "ProviderError";
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
// src/providers/agentcard.ts
|
|
401
|
-
class AgentCardProvider {
|
|
402
|
-
name = "agentcard";
|
|
403
|
-
type = "agentcard";
|
|
404
|
-
jwt;
|
|
405
|
-
baseUrl;
|
|
406
|
-
constructor(config) {
|
|
407
|
-
this.jwt = config.jwt;
|
|
408
|
-
this.baseUrl = config.baseUrl || "https://api.agentcard.sh";
|
|
409
|
-
}
|
|
410
|
-
async request(path, options = {}) {
|
|
411
|
-
const url = `${this.baseUrl}${path}`;
|
|
412
|
-
const res = await fetch(url, {
|
|
413
|
-
...options,
|
|
414
|
-
headers: {
|
|
415
|
-
"Content-Type": "application/json",
|
|
416
|
-
Authorization: `Bearer ${this.jwt}`,
|
|
417
|
-
...options.headers || {}
|
|
418
|
-
}
|
|
419
|
-
});
|
|
420
|
-
if (!res.ok) {
|
|
421
|
-
const body = await res.text();
|
|
422
|
-
throw new ProviderError("agentcard", `HTTP ${res.status}: ${body}`);
|
|
423
|
-
}
|
|
424
|
-
return res.json();
|
|
425
|
-
}
|
|
426
|
-
async createCard(input) {
|
|
427
|
-
const data = await this.request("/cards", {
|
|
428
|
-
method: "POST",
|
|
429
|
-
body: JSON.stringify({ amount: input.amount })
|
|
430
|
-
});
|
|
431
|
-
return {
|
|
432
|
-
id: data.id,
|
|
433
|
-
provider_id: "",
|
|
434
|
-
external_id: data.id,
|
|
435
|
-
name: input.name || `AgentCard $${input.amount}`,
|
|
436
|
-
last_four: "",
|
|
437
|
-
brand: "visa",
|
|
438
|
-
status: data.status || "pending",
|
|
439
|
-
currency: input.currency || "USD",
|
|
440
|
-
balance: 0,
|
|
441
|
-
funded_amount: data.amount || input.amount,
|
|
442
|
-
spending_limit: input.spending_limit ?? null,
|
|
443
|
-
agent_id: input.agent_id ?? null,
|
|
444
|
-
metadata: input.metadata || {},
|
|
445
|
-
expires_at: null,
|
|
446
|
-
created_at: new Date().toISOString(),
|
|
447
|
-
updated_at: new Date().toISOString(),
|
|
448
|
-
funding_url: data.funding_url
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
async listCards() {
|
|
452
|
-
const data = await this.request("/cards");
|
|
453
|
-
return data.map((c) => ({
|
|
454
|
-
id: c.id,
|
|
455
|
-
provider_id: "",
|
|
456
|
-
external_id: c.id,
|
|
457
|
-
name: `AgentCard ${c.last_four ? `*${c.last_four}` : c.id.slice(0, 8)}`,
|
|
458
|
-
last_four: c.last_four || "",
|
|
459
|
-
brand: "visa",
|
|
460
|
-
status: c.status || "active",
|
|
461
|
-
currency: "USD",
|
|
462
|
-
balance: c.balance ?? 0,
|
|
463
|
-
funded_amount: c.amount ?? 0,
|
|
464
|
-
spending_limit: null,
|
|
465
|
-
agent_id: null,
|
|
466
|
-
metadata: {},
|
|
467
|
-
expires_at: null,
|
|
468
|
-
created_at: c.created_at || new Date().toISOString(),
|
|
469
|
-
updated_at: new Date().toISOString()
|
|
470
|
-
}));
|
|
471
|
-
}
|
|
472
|
-
async getCardDetails(externalId) {
|
|
473
|
-
const data = await this.request(`/cards/${externalId}/details`);
|
|
474
|
-
return {
|
|
475
|
-
id: data.id,
|
|
476
|
-
provider_id: "",
|
|
477
|
-
external_id: data.id,
|
|
478
|
-
name: `AgentCard *${data.last_four || data.pan.slice(-4)}`,
|
|
479
|
-
last_four: data.last_four || data.pan.slice(-4),
|
|
480
|
-
brand: "visa",
|
|
481
|
-
status: data.status || "active",
|
|
482
|
-
currency: "USD",
|
|
483
|
-
balance: data.balance ?? 0,
|
|
484
|
-
funded_amount: data.amount ?? 0,
|
|
485
|
-
spending_limit: null,
|
|
486
|
-
agent_id: null,
|
|
487
|
-
metadata: {},
|
|
488
|
-
expires_at: null,
|
|
489
|
-
created_at: new Date().toISOString(),
|
|
490
|
-
updated_at: new Date().toISOString(),
|
|
491
|
-
pan: data.pan,
|
|
492
|
-
cvv: data.cvv,
|
|
493
|
-
exp_month: data.exp_month,
|
|
494
|
-
exp_year: data.exp_year
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
async getBalance(externalId) {
|
|
498
|
-
const data = await this.request(`/cards/${externalId}/balance`);
|
|
499
|
-
return {
|
|
500
|
-
balance: data.balance,
|
|
501
|
-
currency: data.currency || "USD"
|
|
502
|
-
};
|
|
503
|
-
}
|
|
504
|
-
async closeCard(externalId) {
|
|
505
|
-
await this.request(`/cards/${externalId}/close`, { method: "POST" });
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
// src/providers/registry.ts
|
|
510
|
-
var providers = new Map;
|
|
511
|
-
function registerProviderFactory(type, factory) {
|
|
512
|
-
providers.set(type, factory);
|
|
513
|
-
}
|
|
514
|
-
function listProviderTypes() {
|
|
515
|
-
return Array.from(providers.keys());
|
|
516
|
-
}
|
|
517
|
-
function createProviderInstance(type) {
|
|
518
|
-
const factory = providers.get(type);
|
|
519
|
-
if (!factory) {
|
|
520
|
-
throw new Error(`Unknown provider type: ${type}. Available: ${listProviderTypes().join(", ")}`);
|
|
521
|
-
}
|
|
522
|
-
return factory();
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
// src/providers/index.ts
|
|
526
|
-
registerProviderFactory("agentcard", () => {
|
|
527
|
-
throw new Error("AgentCard requires config. Use createAgentCardProvider() instead.");
|
|
528
|
-
});
|
|
529
|
-
function getProviderInstance(type, config) {
|
|
530
|
-
switch (type) {
|
|
531
|
-
case "agentcard":
|
|
532
|
-
return new AgentCardProvider({ jwt: config["jwt"], baseUrl: config["baseUrl"] });
|
|
533
|
-
default:
|
|
534
|
-
return createProviderInstance(type);
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
// src/lib/config.ts
|
|
539
|
-
import { homedir as homedir2 } from "os";
|
|
540
|
-
import { join as join2 } from "path";
|
|
541
|
-
import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync as mkdirSync2 } from "fs";
|
|
542
|
-
function getConfigDir() {
|
|
543
|
-
return process.env["WALLETS_CONFIG_DIR"] || join2(homedir2(), ".wallets");
|
|
544
|
-
}
|
|
545
|
-
function getConfigPath() {
|
|
546
|
-
return join2(getConfigDir(), "config.json");
|
|
547
|
-
}
|
|
548
|
-
function loadConfig() {
|
|
549
|
-
const configPath = getConfigPath();
|
|
550
|
-
if (!existsSync2(configPath)) {
|
|
551
|
-
return {};
|
|
552
|
-
}
|
|
553
|
-
try {
|
|
554
|
-
return JSON.parse(readFileSync(configPath, "utf-8"));
|
|
555
|
-
} catch {
|
|
556
|
-
return {};
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
function saveConfig(config) {
|
|
560
|
-
const configDir = getConfigDir();
|
|
561
|
-
if (!existsSync2(configDir)) {
|
|
562
|
-
mkdirSync2(configDir, { recursive: true });
|
|
563
|
-
}
|
|
564
|
-
writeFileSync(join2(configDir, "config.json"), JSON.stringify(config, null, 2) + `
|
|
565
|
-
`);
|
|
566
|
-
}
|
|
567
|
-
function getProviderConfig(providerName) {
|
|
568
|
-
const config = loadConfig();
|
|
569
|
-
return config.providers?.[providerName];
|
|
570
|
-
}
|
|
571
|
-
function setProviderConfig(providerName, providerConfig) {
|
|
572
|
-
const config = loadConfig();
|
|
573
|
-
if (!config.providers)
|
|
574
|
-
config.providers = {};
|
|
575
|
-
config.providers[providerName] = providerConfig;
|
|
576
|
-
saveConfig(config);
|
|
577
|
-
}
|
|
578
|
-
function removeProviderConfig(providerName) {
|
|
579
|
-
const config = loadConfig();
|
|
580
|
-
if (config.providers) {
|
|
581
|
-
delete config.providers[providerName];
|
|
582
|
-
saveConfig(config);
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
// src/lib/doctor.ts
|
|
587
|
-
import { existsSync as existsSync3 } from "fs";
|
|
588
|
-
import { join as join3 } from "path";
|
|
589
|
-
function runDoctor() {
|
|
590
|
-
const checks = [];
|
|
591
|
-
const configDir = getConfigDir();
|
|
592
|
-
checks.push({
|
|
593
|
-
name: "Config directory",
|
|
594
|
-
status: existsSync3(configDir) ? "ok" : "warn",
|
|
595
|
-
message: existsSync3(configDir) ? `Found at ${configDir}` : `Not found at ${configDir}. Run 'wallets provider add' to create it.`
|
|
596
|
-
});
|
|
597
|
-
const configPath = getConfigPath();
|
|
598
|
-
if (existsSync3(configPath)) {
|
|
599
|
-
try {
|
|
600
|
-
const config = loadConfig();
|
|
601
|
-
checks.push({
|
|
602
|
-
name: "Config file",
|
|
603
|
-
status: "ok",
|
|
604
|
-
message: `Valid config at ${configPath}`
|
|
605
|
-
});
|
|
606
|
-
if (config.default_provider) {
|
|
607
|
-
checks.push({
|
|
608
|
-
name: "Default provider",
|
|
609
|
-
status: "ok",
|
|
610
|
-
message: `Set to '${config.default_provider}'`
|
|
611
|
-
});
|
|
612
|
-
} else {
|
|
613
|
-
checks.push({
|
|
614
|
-
name: "Default provider",
|
|
615
|
-
status: "warn",
|
|
616
|
-
message: "No default provider set. Use 'wallets provider add' to register one."
|
|
617
|
-
});
|
|
618
|
-
}
|
|
619
|
-
} catch {
|
|
620
|
-
checks.push({
|
|
621
|
-
name: "Config file",
|
|
622
|
-
status: "error",
|
|
623
|
-
message: `Invalid JSON at ${configPath}`
|
|
624
|
-
});
|
|
625
|
-
}
|
|
626
|
-
} else {
|
|
627
|
-
checks.push({
|
|
628
|
-
name: "Config file",
|
|
629
|
-
status: "warn",
|
|
630
|
-
message: `Not found at ${configPath}. Run 'wallets provider add' to create it.`
|
|
631
|
-
});
|
|
632
|
-
}
|
|
633
|
-
try {
|
|
634
|
-
const db = getDatabase();
|
|
635
|
-
const providers2 = listProviders(db);
|
|
636
|
-
checks.push({
|
|
637
|
-
name: "Database",
|
|
638
|
-
status: "ok",
|
|
639
|
-
message: `Connected. ${providers2.length} provider(s) registered.`
|
|
640
|
-
});
|
|
641
|
-
for (const provider of providers2) {
|
|
642
|
-
const hasConfig = provider.config && Object.keys(provider.config).length > 0;
|
|
643
|
-
if (provider.type === "agentcard") {
|
|
644
|
-
const hasJwt = provider.config && "jwt" in provider.config;
|
|
645
|
-
checks.push({
|
|
646
|
-
name: `Provider: ${provider.name}`,
|
|
647
|
-
status: hasJwt ? "ok" : "error",
|
|
648
|
-
message: hasJwt ? `AgentCard configured (status: ${provider.status})` : "Missing JWT token. Run 'wallets provider add agentcard' to configure."
|
|
649
|
-
});
|
|
650
|
-
} else {
|
|
651
|
-
checks.push({
|
|
652
|
-
name: `Provider: ${provider.name}`,
|
|
653
|
-
status: hasConfig ? "ok" : "warn",
|
|
654
|
-
message: `Type: ${provider.type}, Status: ${provider.status}${hasConfig ? "" : " (no config)"}`
|
|
655
|
-
});
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
if (providers2.length === 0) {
|
|
659
|
-
checks.push({
|
|
660
|
-
name: "Providers",
|
|
661
|
-
status: "warn",
|
|
662
|
-
message: "No providers registered. Use 'wallets provider add' to add one."
|
|
663
|
-
});
|
|
664
|
-
}
|
|
665
|
-
} catch (e) {
|
|
666
|
-
checks.push({
|
|
667
|
-
name: "Database",
|
|
668
|
-
status: "error",
|
|
669
|
-
message: `Failed to connect: ${e instanceof Error ? e.message : String(e)}`
|
|
670
|
-
});
|
|
671
|
-
}
|
|
672
|
-
try {
|
|
673
|
-
const binPath = join3(import.meta.dir, "../../dist/mcp/index.js");
|
|
674
|
-
const srcPath = join3(import.meta.dir, "../mcp/index.ts");
|
|
675
|
-
const mcpAvailable = existsSync3(binPath) || existsSync3(srcPath);
|
|
676
|
-
checks.push({
|
|
677
|
-
name: "MCP server",
|
|
678
|
-
status: mcpAvailable ? "ok" : "warn",
|
|
679
|
-
message: mcpAvailable ? "MCP server binary available" : "MCP server not found. Run 'bun run build' to compile."
|
|
680
|
-
});
|
|
681
|
-
} catch {
|
|
682
|
-
checks.push({
|
|
683
|
-
name: "MCP server",
|
|
684
|
-
status: "warn",
|
|
685
|
-
message: "Could not check MCP server status."
|
|
686
|
-
});
|
|
687
|
-
}
|
|
688
|
-
return {
|
|
689
|
-
checks,
|
|
690
|
-
healthy: checks.every((c) => c.status !== "error")
|
|
691
|
-
};
|
|
692
|
-
}
|
|
693
|
-
|
|
694
|
-
// src/lib/format.ts
|
|
695
|
-
function formatCard(card) {
|
|
696
|
-
const id = card.id.slice(0, 8);
|
|
697
|
-
const last4 = card.last_four ? `*${card.last_four}` : "----";
|
|
698
|
-
return `${id} ${card.status.padEnd(8)} ${last4.padEnd(6)} $${card.balance.toFixed(2).padStart(10)} ${card.name}`;
|
|
699
|
-
}
|
|
700
|
-
function formatProvider(provider) {
|
|
701
|
-
return `${provider.id.slice(0, 8)} ${provider.status.padEnd(8)} ${provider.type.padEnd(12)} ${provider.name}`;
|
|
702
|
-
}
|
|
703
|
-
function formatTransaction(tx) {
|
|
704
|
-
const id = tx.id.slice(0, 8);
|
|
705
|
-
const sign = tx.type === "refund" || tx.type === "load" ? "+" : "-";
|
|
706
|
-
return `${id} ${tx.type.padEnd(10)} ${tx.status.padEnd(10)} ${sign}$${tx.amount.toFixed(2).padStart(9)} ${tx.merchant || tx.description || ""}`;
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
// src/cli/index.ts
|
|
710
|
-
import { readFileSync as readFileSync2 } from "fs";
|
|
711
|
-
import { join as join4 } from "path";
|
|
712
|
-
import { execSync } from "child_process";
|
|
713
|
-
function getVersion() {
|
|
714
|
-
try {
|
|
715
|
-
const pkg = JSON.parse(readFileSync2(join4(import.meta.dir, "../../package.json"), "utf-8"));
|
|
716
|
-
return pkg.version || "0.1.0";
|
|
717
|
-
} catch {
|
|
718
|
-
return "0.1.0";
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
function resolveId(partialId, table = "cards") {
|
|
722
|
-
const db = getDatabase();
|
|
723
|
-
const id = resolvePartialId(db, table, partialId);
|
|
724
|
-
if (!id) {
|
|
725
|
-
console.error(chalk.red(`Could not resolve ID: ${partialId}`));
|
|
726
|
-
process.exit(1);
|
|
727
|
-
}
|
|
728
|
-
return id;
|
|
729
|
-
}
|
|
730
|
-
var statusColors = {
|
|
731
|
-
active: chalk.green,
|
|
732
|
-
pending: chalk.yellow,
|
|
733
|
-
frozen: chalk.blue,
|
|
734
|
-
closed: chalk.gray,
|
|
735
|
-
inactive: chalk.gray,
|
|
736
|
-
error: chalk.red
|
|
737
|
-
};
|
|
738
|
-
function colorStatus(status) {
|
|
739
|
-
const fn = statusColors[status] || chalk.white;
|
|
740
|
-
return fn(status);
|
|
741
|
-
}
|
|
742
|
-
var program = new Command;
|
|
743
|
-
program.name("wallets").description("Universal wallet management for AI agents - multi-provider virtual cards").version(getVersion()).option("--json", "Output as JSON");
|
|
744
|
-
var providerCmd = program.command("provider").description("Manage wallet providers");
|
|
745
|
-
providerCmd.command("add <type>").description("Register a wallet provider (e.g., agentcard)").option("-n, --name <name>", "Provider display name").option("--jwt <token>", "JWT/API token for authentication").option("--api-key <key>", "API key for authentication").option("--base-url <url>", "Custom API base URL").option("--default", "Set as default provider").action((type, opts) => {
|
|
746
|
-
const name = opts.name || type;
|
|
747
|
-
const config = {};
|
|
748
|
-
if (opts.jwt)
|
|
749
|
-
config["jwt"] = opts.jwt;
|
|
750
|
-
if (opts.apiKey)
|
|
751
|
-
config["api_key"] = opts.apiKey;
|
|
752
|
-
if (opts.baseUrl)
|
|
753
|
-
config["baseUrl"] = opts.baseUrl;
|
|
754
|
-
const provider = ensureProvider(name, type, config);
|
|
755
|
-
setProviderConfig(name, config);
|
|
756
|
-
if (opts.default) {
|
|
757
|
-
const cfg = loadConfig();
|
|
758
|
-
cfg.default_provider = name;
|
|
759
|
-
saveConfig(cfg);
|
|
760
|
-
}
|
|
761
|
-
const globalOpts = program.opts();
|
|
762
|
-
if (globalOpts["json"]) {
|
|
763
|
-
console.log(JSON.stringify(provider, null, 2));
|
|
764
|
-
} else {
|
|
765
|
-
console.log(chalk.green(`Provider registered: ${formatProvider(provider)}`));
|
|
766
|
-
if (opts.default)
|
|
767
|
-
console.log(chalk.dim(` Set as default provider`));
|
|
768
|
-
}
|
|
769
|
-
});
|
|
770
|
-
providerCmd.command("list").description("List registered providers").action(() => {
|
|
771
|
-
const providers2 = listProviders();
|
|
772
|
-
const globalOpts = program.opts();
|
|
773
|
-
if (globalOpts["json"]) {
|
|
774
|
-
console.log(JSON.stringify(providers2, null, 2));
|
|
775
|
-
return;
|
|
776
|
-
}
|
|
777
|
-
if (providers2.length === 0) {
|
|
778
|
-
console.log(chalk.yellow("No providers registered. Use 'wallets provider add <type>' to add one."));
|
|
779
|
-
return;
|
|
780
|
-
}
|
|
781
|
-
const config = loadConfig();
|
|
782
|
-
console.log(chalk.bold("Providers:"));
|
|
783
|
-
for (const p of providers2) {
|
|
784
|
-
const isDefault = config.default_provider === p.name;
|
|
785
|
-
const defaultTag = isDefault ? chalk.cyan(" (default)") : "";
|
|
786
|
-
console.log(` ${chalk.dim(p.id.slice(0, 8))} ${colorStatus(p.status).padEnd(18)} ${p.type.padEnd(12)} ${p.name}${defaultTag}`);
|
|
787
|
-
}
|
|
788
|
-
});
|
|
789
|
-
providerCmd.command("remove <name>").description("Remove a provider").action((name) => {
|
|
790
|
-
const provider = getProviderByName(name);
|
|
791
|
-
if (!provider) {
|
|
792
|
-
console.error(chalk.red(`Provider not found: ${name}`));
|
|
793
|
-
process.exit(1);
|
|
794
|
-
}
|
|
795
|
-
deleteProvider(provider.id);
|
|
796
|
-
removeProviderConfig(name);
|
|
797
|
-
const config = loadConfig();
|
|
798
|
-
if (config.default_provider === name) {
|
|
799
|
-
delete config.default_provider;
|
|
800
|
-
saveConfig(config);
|
|
801
|
-
}
|
|
802
|
-
console.log(chalk.green(`Provider removed: ${name}`));
|
|
803
|
-
});
|
|
804
|
-
var cardCmd = program.command("card").description("Manage virtual cards");
|
|
805
|
-
cardCmd.command("create").description("Create a new funded virtual card").requiredOption("-a, --amount <amount>", "Funding amount in dollars").option("-n, --name <name>", "Card display name").option("-p, --provider <name>", "Provider to use (default from config)").option("-c, --currency <code>", "Currency code (default: USD)").option("--agent <name>", "Assign card to an agent").action(async (opts) => {
|
|
806
|
-
const config = loadConfig();
|
|
807
|
-
const providerName = opts.provider || config.default_provider;
|
|
808
|
-
if (!providerName) {
|
|
809
|
-
console.error(chalk.red("No provider specified and no default set. Use --provider or 'wallets provider add --default'."));
|
|
810
|
-
process.exit(1);
|
|
811
|
-
}
|
|
812
|
-
const providerRecord = getProviderByName(providerName);
|
|
813
|
-
if (!providerRecord) {
|
|
814
|
-
console.error(chalk.red(`Provider not found: ${providerName}`));
|
|
815
|
-
process.exit(1);
|
|
816
|
-
}
|
|
817
|
-
const providerConfig = { ...providerRecord.config, ...getProviderConfig(providerName) || {} };
|
|
818
|
-
const instance = getProviderInstance(providerRecord.type, providerConfig);
|
|
819
|
-
let agentId = null;
|
|
820
|
-
if (opts.agent) {
|
|
821
|
-
const agent = registerAgent({ name: opts.agent });
|
|
822
|
-
agentId = agent.id;
|
|
823
|
-
}
|
|
824
|
-
try {
|
|
825
|
-
const result = await instance.createCard({
|
|
826
|
-
amount: parseFloat(opts.amount),
|
|
827
|
-
name: opts.name,
|
|
828
|
-
currency: opts.currency || "USD",
|
|
829
|
-
agent_id: agentId ?? undefined
|
|
830
|
-
});
|
|
831
|
-
const card = createCardRecord({
|
|
832
|
-
provider_id: providerRecord.id,
|
|
833
|
-
external_id: result.external_id || result.id,
|
|
834
|
-
name: result.name,
|
|
835
|
-
last_four: result.last_four,
|
|
836
|
-
brand: result.brand,
|
|
837
|
-
status: result.status,
|
|
838
|
-
currency: result.currency,
|
|
839
|
-
balance: result.balance,
|
|
840
|
-
funded_amount: result.funded_amount,
|
|
841
|
-
spending_limit: result.spending_limit,
|
|
842
|
-
agent_id: agentId,
|
|
843
|
-
metadata: result.funding_url ? { funding_url: result.funding_url } : {}
|
|
844
|
-
});
|
|
845
|
-
const globalOpts = program.opts();
|
|
846
|
-
if (globalOpts["json"]) {
|
|
847
|
-
console.log(JSON.stringify({ ...card, funding_url: result.funding_url }, null, 2));
|
|
848
|
-
} else {
|
|
849
|
-
console.log(chalk.green(`Card created: ${formatCard(card)}`));
|
|
850
|
-
if (result.funding_url) {
|
|
851
|
-
console.log(chalk.cyan(` Fund at: ${result.funding_url}`));
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
} catch (e) {
|
|
855
|
-
console.error(chalk.red(`Failed to create card: ${e instanceof Error ? e.message : String(e)}`));
|
|
856
|
-
process.exit(1);
|
|
857
|
-
}
|
|
858
|
-
});
|
|
859
|
-
cardCmd.command("list").description("List all cards").option("-s, --status <status>", "Filter by status").option("-p, --provider <name>", "Filter by provider").option("--agent <name>", "Filter by agent").action((opts) => {
|
|
860
|
-
let providerId;
|
|
861
|
-
if (opts.provider) {
|
|
862
|
-
const p = getProviderByName(opts.provider);
|
|
863
|
-
if (p)
|
|
864
|
-
providerId = p.id;
|
|
865
|
-
}
|
|
866
|
-
const cards = listCards({
|
|
867
|
-
status: opts.status,
|
|
868
|
-
provider_id: providerId
|
|
869
|
-
});
|
|
870
|
-
const globalOpts = program.opts();
|
|
871
|
-
if (globalOpts["json"]) {
|
|
872
|
-
console.log(JSON.stringify(cards, null, 2));
|
|
873
|
-
return;
|
|
874
|
-
}
|
|
875
|
-
if (cards.length === 0) {
|
|
876
|
-
console.log(chalk.yellow("No cards found. Use 'wallets card create' to create one."));
|
|
877
|
-
return;
|
|
878
|
-
}
|
|
879
|
-
console.log(chalk.bold("Cards:"));
|
|
880
|
-
for (const card of cards) {
|
|
881
|
-
const last4 = card.last_four ? `*${card.last_four}` : "----";
|
|
882
|
-
console.log(` ${chalk.dim(card.id.slice(0, 8))} ${colorStatus(card.status).padEnd(18)} ${last4.padEnd(6)} $${card.balance.toFixed(2).padStart(10)} ${card.name}`);
|
|
883
|
-
}
|
|
884
|
-
});
|
|
885
|
-
cardCmd.command("details <id>").description("Get full card details including PAN and CVV").action(async (id) => {
|
|
886
|
-
const cardId = resolveId(id, "cards");
|
|
887
|
-
const card = getCard(cardId);
|
|
888
|
-
if (!card) {
|
|
889
|
-
console.error(chalk.red(`Card not found: ${id}`));
|
|
890
|
-
process.exit(1);
|
|
891
|
-
}
|
|
892
|
-
const provider = getProvider(card.provider_id);
|
|
893
|
-
if (!provider) {
|
|
894
|
-
console.error(chalk.red(`Provider not found for card`));
|
|
895
|
-
process.exit(1);
|
|
896
|
-
}
|
|
897
|
-
const providerConfig = { ...provider.config, ...getProviderConfig(provider.name) || {} };
|
|
898
|
-
const instance = getProviderInstance(provider.type, providerConfig);
|
|
899
|
-
try {
|
|
900
|
-
const details = await instance.getCardDetails(card.external_id);
|
|
901
|
-
const globalOpts = program.opts();
|
|
902
|
-
if (globalOpts["json"]) {
|
|
903
|
-
console.log(JSON.stringify(details, null, 2));
|
|
904
|
-
} else {
|
|
905
|
-
console.log(chalk.bold("Card Details:"));
|
|
906
|
-
console.log(` ID: ${card.id}`);
|
|
907
|
-
console.log(` Name: ${card.name}`);
|
|
908
|
-
console.log(` PAN: ${details.pan}`);
|
|
909
|
-
console.log(` CVV: ${details.cvv}`);
|
|
910
|
-
console.log(` Exp: ${details.exp_month}/${details.exp_year}`);
|
|
911
|
-
console.log(` Status: ${colorStatus(card.status)}`);
|
|
912
|
-
console.log(` Balance: $${details.balance.toFixed(2)}`);
|
|
913
|
-
}
|
|
914
|
-
} catch (e) {
|
|
915
|
-
console.error(chalk.red(`Failed to get details: ${e instanceof Error ? e.message : String(e)}`));
|
|
916
|
-
process.exit(1);
|
|
917
|
-
}
|
|
918
|
-
});
|
|
919
|
-
cardCmd.command("close <id>").description("Close a card permanently").action(async (id) => {
|
|
920
|
-
const cardId = resolveId(id, "cards");
|
|
921
|
-
const card = getCard(cardId);
|
|
922
|
-
if (!card) {
|
|
923
|
-
console.error(chalk.red(`Card not found: ${id}`));
|
|
924
|
-
process.exit(1);
|
|
925
|
-
}
|
|
926
|
-
const provider = getProvider(card.provider_id);
|
|
927
|
-
if (!provider) {
|
|
928
|
-
console.error(chalk.red(`Provider not found for card`));
|
|
929
|
-
process.exit(1);
|
|
930
|
-
}
|
|
931
|
-
const providerConfig = { ...provider.config, ...getProviderConfig(provider.name) || {} };
|
|
932
|
-
const instance = getProviderInstance(provider.type, providerConfig);
|
|
933
|
-
try {
|
|
934
|
-
await instance.closeCard(card.external_id);
|
|
935
|
-
updateCard(cardId, { status: "closed" });
|
|
936
|
-
console.log(chalk.green(`Card closed: ${card.id.slice(0, 8)} ${card.name}`));
|
|
937
|
-
} catch (e) {
|
|
938
|
-
console.error(chalk.red(`Failed to close card: ${e instanceof Error ? e.message : String(e)}`));
|
|
939
|
-
process.exit(1);
|
|
940
|
-
}
|
|
941
|
-
});
|
|
942
|
-
program.command("balance [card-id]").description("Check balance of a card or all cards").action(async (cardId) => {
|
|
943
|
-
if (cardId) {
|
|
944
|
-
const resolved = resolveId(cardId, "cards");
|
|
945
|
-
const card = getCard(resolved);
|
|
946
|
-
if (!card) {
|
|
947
|
-
console.error(chalk.red(`Card not found: ${cardId}`));
|
|
948
|
-
process.exit(1);
|
|
949
|
-
}
|
|
950
|
-
const provider = getProvider(card.provider_id);
|
|
951
|
-
if (!provider) {
|
|
952
|
-
console.error(chalk.red(`Provider not found for card`));
|
|
953
|
-
process.exit(1);
|
|
954
|
-
}
|
|
955
|
-
const providerConfig = { ...provider.config, ...getProviderConfig(provider.name) || {} };
|
|
956
|
-
const instance = getProviderInstance(provider.type, providerConfig);
|
|
957
|
-
try {
|
|
958
|
-
const { balance, currency } = await instance.getBalance(card.external_id);
|
|
959
|
-
const globalOpts = program.opts();
|
|
960
|
-
if (globalOpts["json"]) {
|
|
961
|
-
console.log(JSON.stringify({ card_id: card.id, balance, currency }, null, 2));
|
|
962
|
-
} else {
|
|
963
|
-
console.log(`${chalk.dim(card.id.slice(0, 8))} ${card.name}: ${chalk.green(`$${balance.toFixed(2)}`)} ${currency}`);
|
|
964
|
-
}
|
|
965
|
-
} catch (e) {
|
|
966
|
-
console.error(chalk.red(`Failed to get balance: ${e instanceof Error ? e.message : String(e)}`));
|
|
967
|
-
process.exit(1);
|
|
968
|
-
}
|
|
969
|
-
} else {
|
|
970
|
-
const cards = listCards({ status: "active" });
|
|
971
|
-
if (cards.length === 0) {
|
|
972
|
-
console.log(chalk.yellow("No active cards."));
|
|
973
|
-
return;
|
|
974
|
-
}
|
|
975
|
-
for (const card of cards) {
|
|
976
|
-
console.log(`${chalk.dim(card.id.slice(0, 8))} ${card.name}: ${chalk.green(`$${card.balance.toFixed(2)}`)} ${card.currency}`);
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
});
|
|
980
|
-
program.command("transactions [card-id]").description("List transactions").option("-n, --limit <n>", "Limit results", "20").action((cardId, opts) => {
|
|
981
|
-
let resolvedCardId;
|
|
982
|
-
if (cardId) {
|
|
983
|
-
resolvedCardId = resolveId(cardId, "cards");
|
|
984
|
-
}
|
|
985
|
-
const txns = listTransactions({
|
|
986
|
-
card_id: resolvedCardId,
|
|
987
|
-
limit: parseInt(opts.limit, 10)
|
|
988
|
-
});
|
|
989
|
-
const globalOpts = program.opts();
|
|
990
|
-
if (globalOpts["json"]) {
|
|
991
|
-
console.log(JSON.stringify(txns, null, 2));
|
|
992
|
-
return;
|
|
993
|
-
}
|
|
994
|
-
if (txns.length === 0) {
|
|
995
|
-
console.log(chalk.yellow("No transactions found."));
|
|
996
|
-
return;
|
|
997
|
-
}
|
|
998
|
-
console.log(chalk.bold("Transactions:"));
|
|
999
|
-
for (const tx of txns) {
|
|
1000
|
-
console.log(` ${formatTransaction(tx)}`);
|
|
1001
|
-
}
|
|
1002
|
-
});
|
|
1003
|
-
program.command("doctor").description("Run diagnostics and check wallet health").action(() => {
|
|
1004
|
-
const result = runDoctor();
|
|
1005
|
-
const globalOpts = program.opts();
|
|
1006
|
-
if (globalOpts["json"]) {
|
|
1007
|
-
console.log(JSON.stringify(result, null, 2));
|
|
1008
|
-
return;
|
|
1009
|
-
}
|
|
1010
|
-
console.log(chalk.bold("Wallet Health Check:"));
|
|
1011
|
-
console.log();
|
|
1012
|
-
for (const check of result.checks) {
|
|
1013
|
-
const icon = check.status === "ok" ? chalk.green("[ok]") : check.status === "warn" ? chalk.yellow("[!!]") : chalk.red("[ERR]");
|
|
1014
|
-
console.log(` ${icon} ${chalk.bold(check.name)}: ${check.message}`);
|
|
1015
|
-
}
|
|
1016
|
-
console.log();
|
|
1017
|
-
if (result.healthy) {
|
|
1018
|
-
console.log(chalk.green("All checks passed."));
|
|
1019
|
-
} else {
|
|
1020
|
-
console.log(chalk.red("Some checks failed. Run 'wallets doctor' with --json for details."));
|
|
1021
|
-
}
|
|
1022
|
-
});
|
|
1023
|
-
program.command("mcp").description("Install/configure MCP server for AI agents").option("--claude", "Install for Claude Code").option("--codex", "Install for Codex").option("--gemini", "Install for Gemini").option("--all", "Install for all agents").option("--uninstall", "Uninstall MCP server").action((opts) => {
|
|
1024
|
-
const targets = [];
|
|
1025
|
-
if (opts.all) {
|
|
1026
|
-
targets.push("claude", "codex", "gemini");
|
|
1027
|
-
} else {
|
|
1028
|
-
if (opts.claude)
|
|
1029
|
-
targets.push("claude");
|
|
1030
|
-
if (opts.codex)
|
|
1031
|
-
targets.push("codex");
|
|
1032
|
-
if (opts.gemini)
|
|
1033
|
-
targets.push("gemini");
|
|
1034
|
-
}
|
|
1035
|
-
if (targets.length === 0) {
|
|
1036
|
-
targets.push("claude");
|
|
1037
|
-
}
|
|
1038
|
-
for (const target of targets) {
|
|
1039
|
-
try {
|
|
1040
|
-
if (target === "claude") {
|
|
1041
|
-
if (opts.uninstall) {
|
|
1042
|
-
execSync("claude mcp remove wallets", { encoding: "utf-8" });
|
|
1043
|
-
console.log(chalk.green("Removed wallets MCP from Claude Code"));
|
|
1044
|
-
} else {
|
|
1045
|
-
execSync("claude mcp add --transport stdio --scope user wallets -- wallets-mcp", { encoding: "utf-8" });
|
|
1046
|
-
console.log(chalk.green("Installed wallets MCP for Claude Code"));
|
|
1047
|
-
}
|
|
1048
|
-
} else if (target === "codex") {
|
|
1049
|
-
console.log(chalk.yellow(`Codex MCP setup: Add to ~/.codex/config.toml:`));
|
|
1050
|
-
console.log(chalk.dim(` [mcp_servers.wallets]`));
|
|
1051
|
-
console.log(chalk.dim(` command = "wallets-mcp"`));
|
|
1052
|
-
console.log(chalk.dim(` args = []`));
|
|
1053
|
-
} else if (target === "gemini") {
|
|
1054
|
-
console.log(chalk.yellow(`Gemini MCP setup: Add to ~/.gemini/settings.json:`));
|
|
1055
|
-
console.log(chalk.dim(` "wallets": { "command": "wallets-mcp", "args": [] }`));
|
|
1056
|
-
}
|
|
1057
|
-
} catch (e) {
|
|
1058
|
-
console.error(chalk.red(`Failed to setup MCP for ${target}: ${e instanceof Error ? e.message : String(e)}`));
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
});
|
|
1062
|
-
var agentCmd = program.command("agent").description("Manage agent registrations");
|
|
1063
|
-
agentCmd.command("list").description("List registered agents").action(() => {
|
|
1064
|
-
const agents = listAgents();
|
|
1065
|
-
const globalOpts = program.opts();
|
|
1066
|
-
if (globalOpts["json"]) {
|
|
1067
|
-
console.log(JSON.stringify(agents, null, 2));
|
|
1068
|
-
return;
|
|
1069
|
-
}
|
|
1070
|
-
if (agents.length === 0) {
|
|
1071
|
-
console.log(chalk.yellow("No agents registered."));
|
|
1072
|
-
return;
|
|
1073
|
-
}
|
|
1074
|
-
for (const agent of agents) {
|
|
1075
|
-
console.log(` ${chalk.dim(agent.id)} ${agent.name}${agent.description ? chalk.dim(` \u2014 ${agent.description}`) : ""}`);
|
|
1076
|
-
}
|
|
1077
|
-
});
|
|
1078
|
-
agentCmd.command("register <name>").description("Register an agent").option("-d, --description <text>", "Agent description").action((name, opts) => {
|
|
1079
|
-
const agent = registerAgent({ name, description: opts.description });
|
|
1080
|
-
console.log(chalk.green(`Agent registered: ${agent.id} ${agent.name}`));
|
|
1081
|
-
});
|
|
1082
|
-
program.parse();
|