@hasna/wallets 0.1.6 → 0.1.8
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/README.md +19 -0
- package/dist/cli/index.js +15353 -8679
- package/dist/db/agents.d.ts +2 -2
- package/dist/db/agents.d.ts.map +1 -1
- package/dist/db/audit.d.ts +31 -0
- package/dist/db/audit.d.ts.map +1 -0
- package/dist/db/cards.d.ts +4 -2
- package/dist/db/cards.d.ts.map +1 -1
- package/dist/db/database.d.ts +2 -1
- package/dist/db/database.d.ts.map +1 -1
- package/dist/db/providers.d.ts +3 -3
- package/dist/db/providers.d.ts.map +1 -1
- package/dist/db/transactions.d.ts +2 -2
- package/dist/db/transactions.d.ts.map +1 -1
- package/dist/index.d.ts +12 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4934 -216
- package/dist/lib/cache.d.ts +3 -0
- package/dist/lib/cache.d.ts.map +1 -0
- package/dist/lib/config.d.ts +12 -5
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/doctor.d.ts +2 -2
- package/dist/lib/doctor.d.ts.map +1 -1
- package/dist/lib/format.d.ts +11 -2
- package/dist/lib/format.d.ts.map +1 -1
- package/dist/lib/logger.d.ts +25 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/mcp/http.d.ts +14 -0
- package/dist/mcp/http.d.ts.map +1 -0
- package/dist/mcp/index.d.ts +2 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +1754 -695
- package/dist/providers/agentcard.d.ts +8 -1
- package/dist/providers/agentcard.d.ts.map +1 -1
- package/dist/providers/index.d.ts +6 -5
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/registry.d.ts +1 -1
- package/dist/providers/registry.d.ts.map +1 -1
- package/dist/types/index.d.ts +16 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +82 -71
package/dist/mcp/index.js
CHANGED
|
@@ -20,7 +20,7 @@ var __export = (target, all) => {
|
|
|
20
20
|
var require_package = __commonJS((exports, module) => {
|
|
21
21
|
module.exports = {
|
|
22
22
|
name: "@hasna/wallets",
|
|
23
|
-
version: "0.1.
|
|
23
|
+
version: "0.1.7",
|
|
24
24
|
description: "Universal wallet management for AI agents - CLI + MCP server with multi-provider support",
|
|
25
25
|
type: "module",
|
|
26
26
|
main: "dist/index.js",
|
|
@@ -35,17 +35,21 @@ var require_package = __commonJS((exports, module) => {
|
|
|
35
35
|
import: "./dist/index.js"
|
|
36
36
|
}
|
|
37
37
|
},
|
|
38
|
-
files: [
|
|
39
|
-
"dist",
|
|
40
|
-
"LICENSE",
|
|
41
|
-
"README.md"
|
|
42
|
-
],
|
|
38
|
+
files: ["dist", "LICENSE", "README.md"],
|
|
43
39
|
scripts: {
|
|
44
40
|
build: "bun build src/cli/index.ts --outdir dist/cli --target bun --external chalk --external commander --external @modelcontextprotocol/sdk && bun build src/mcp/index.ts --outdir dist/mcp --target bun --external @modelcontextprotocol/sdk && bun build src/index.ts --outdir dist --target bun && tsc --emitDeclarationOnly --outDir dist",
|
|
45
41
|
typecheck: "tsc --noEmit",
|
|
46
42
|
test: "bun test",
|
|
43
|
+
"test:coverage": "bun test --coverage",
|
|
44
|
+
dev: "bun run dev:cli",
|
|
47
45
|
"dev:cli": "bun run src/cli/index.ts",
|
|
48
|
-
"dev:mcp": "bun run src/mcp/index.ts"
|
|
46
|
+
"dev:mcp": "bun run src/mcp/index.ts",
|
|
47
|
+
"dev:watch": "bun --watch run src/cli/index.ts",
|
|
48
|
+
"dev:mcp:watch": "bun --watch run src/mcp/index.ts",
|
|
49
|
+
lint: "biome check src/",
|
|
50
|
+
"lint:fix": "biome check --write .",
|
|
51
|
+
prepack: "bun run build",
|
|
52
|
+
postinstall: "mkdir -p $HOME/.hasna/wallets 2>/dev/null || true && lefthook install"
|
|
49
53
|
},
|
|
50
54
|
keywords: [
|
|
51
55
|
"wallets",
|
|
@@ -84,10 +88,13 @@ var require_package = __commonJS((exports, module) => {
|
|
|
84
88
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
85
89
|
chalk: "^5.4.1",
|
|
86
90
|
commander: "^13.1.0",
|
|
91
|
+
jmespath: "^0.16.0",
|
|
87
92
|
zod: "^3.24.2"
|
|
88
93
|
},
|
|
89
94
|
devDependencies: {
|
|
95
|
+
"@biomejs/biome": "^1.9.4",
|
|
90
96
|
"@types/bun": "^1.2.4",
|
|
97
|
+
lefthook: "^2.1.4",
|
|
91
98
|
typescript: "^5.7.3"
|
|
92
99
|
}
|
|
93
100
|
};
|
|
@@ -4070,6 +4077,33 @@ var coerce = {
|
|
|
4070
4077
|
date: (arg) => ZodDate.create({ ...arg, coerce: true })
|
|
4071
4078
|
};
|
|
4072
4079
|
var NEVER = INVALID;
|
|
4080
|
+
// src/lib/cache.ts
|
|
4081
|
+
var cache = new Map;
|
|
4082
|
+
function cached(key, ttlMs, fn) {
|
|
4083
|
+
const entry = cache.get(key);
|
|
4084
|
+
if (entry && entry.expiresAt > Date.now()) {
|
|
4085
|
+
return entry.value;
|
|
4086
|
+
}
|
|
4087
|
+
const value = fn();
|
|
4088
|
+
cache.set(key, { value, expiresAt: Date.now() + ttlMs });
|
|
4089
|
+
return value;
|
|
4090
|
+
}
|
|
4091
|
+
function cacheClear(prefix) {
|
|
4092
|
+
if (!prefix) {
|
|
4093
|
+
cache.clear();
|
|
4094
|
+
return;
|
|
4095
|
+
}
|
|
4096
|
+
for (const key of cache.keys()) {
|
|
4097
|
+
if (key.startsWith(prefix))
|
|
4098
|
+
cache.delete(key);
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
|
|
4102
|
+
// src/db/database.ts
|
|
4103
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
4104
|
+
import { homedir as homedir3 } from "os";
|
|
4105
|
+
import { join as join3 } from "path";
|
|
4106
|
+
|
|
4073
4107
|
// node_modules/@hasna/cloud/dist/index.js
|
|
4074
4108
|
import { createRequire } from "module";
|
|
4075
4109
|
import { Database } from "bun:sqlite";
|
|
@@ -4096,10 +4130,10 @@ var __toESMCache_esm;
|
|
|
4096
4130
|
var __toESM = (mod, isNodeMode, target) => {
|
|
4097
4131
|
var canCache = mod != null && typeof mod === "object";
|
|
4098
4132
|
if (canCache) {
|
|
4099
|
-
var
|
|
4100
|
-
var
|
|
4101
|
-
if (
|
|
4102
|
-
return
|
|
4133
|
+
var cache2 = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
4134
|
+
var cached2 = cache2.get(mod);
|
|
4135
|
+
if (cached2)
|
|
4136
|
+
return cached2;
|
|
4103
4137
|
}
|
|
4104
4138
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
4105
4139
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp2(target, "default", { value: mod, enumerable: true }) : target;
|
|
@@ -4110,7 +4144,7 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
4110
4144
|
enumerable: true
|
|
4111
4145
|
});
|
|
4112
4146
|
if (canCache)
|
|
4113
|
-
|
|
4147
|
+
cache2.set(mod, to);
|
|
4114
4148
|
return to;
|
|
4115
4149
|
};
|
|
4116
4150
|
var __commonJS2 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
@@ -13006,9 +13040,6 @@ function ensureFeedbackTable(db) {
|
|
|
13006
13040
|
}
|
|
13007
13041
|
|
|
13008
13042
|
// src/db/database.ts
|
|
13009
|
-
import { homedir as homedir3 } from "os";
|
|
13010
|
-
import { join as join3 } from "path";
|
|
13011
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
13012
13043
|
var _db = null;
|
|
13013
13044
|
var _adapter = null;
|
|
13014
13045
|
var MIGRATIONS = [
|
|
@@ -13083,7 +13114,30 @@ var MIGRATIONS = [
|
|
|
13083
13114
|
},
|
|
13084
13115
|
{
|
|
13085
13116
|
id: 2,
|
|
13086
|
-
sql:
|
|
13117
|
+
sql: "ALTER TABLE agents ADD COLUMN active_project_id TEXT;"
|
|
13118
|
+
},
|
|
13119
|
+
{
|
|
13120
|
+
id: 3,
|
|
13121
|
+
sql: "ALTER TABLE cards ADD COLUMN idempotency_key TEXT; CREATE UNIQUE INDEX IF NOT EXISTS idx_cards_idempotency ON cards(idempotency_key) WHERE idempotency_key IS NOT NULL;"
|
|
13122
|
+
},
|
|
13123
|
+
{
|
|
13124
|
+
id: 4,
|
|
13125
|
+
sql: `
|
|
13126
|
+
CREATE TABLE IF NOT EXISTS audit_log (
|
|
13127
|
+
id TEXT PRIMARY KEY,
|
|
13128
|
+
action TEXT NOT NULL CHECK(action IN ('create', 'update', 'delete')),
|
|
13129
|
+
entity_type TEXT NOT NULL CHECK(entity_type IN ('card', 'provider', 'transaction', 'agent')),
|
|
13130
|
+
entity_id TEXT NOT NULL,
|
|
13131
|
+
actor_id TEXT,
|
|
13132
|
+
actor_name TEXT,
|
|
13133
|
+
changes TEXT DEFAULT '{}',
|
|
13134
|
+
metadata TEXT DEFAULT '{}',
|
|
13135
|
+
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
|
13136
|
+
);
|
|
13137
|
+
CREATE INDEX IF NOT EXISTS idx_audit_entity ON audit_log(entity_type, entity_id);
|
|
13138
|
+
CREATE INDEX IF NOT EXISTS idx_audit_actor ON audit_log(actor_id);
|
|
13139
|
+
CREATE INDEX IF NOT EXISTS idx_audit_action ON audit_log(action);
|
|
13140
|
+
`
|
|
13087
13141
|
}
|
|
13088
13142
|
];
|
|
13089
13143
|
function runMigrations(db) {
|
|
@@ -13107,10 +13161,10 @@ function runMigrations(db) {
|
|
|
13107
13161
|
}
|
|
13108
13162
|
}
|
|
13109
13163
|
function resolveDbPath() {
|
|
13110
|
-
if (process.env
|
|
13111
|
-
return process.env
|
|
13112
|
-
if (process.env
|
|
13113
|
-
return process.env
|
|
13164
|
+
if (process.env.HASNA_WALLETS_DB_PATH)
|
|
13165
|
+
return process.env.HASNA_WALLETS_DB_PATH;
|
|
13166
|
+
if (process.env.WALLETS_DB_PATH)
|
|
13167
|
+
return process.env.WALLETS_DB_PATH;
|
|
13114
13168
|
const home = homedir3();
|
|
13115
13169
|
migrateDotfile("wallets");
|
|
13116
13170
|
const newDir = join3(home, ".hasna", "wallets");
|
|
@@ -13147,84 +13201,66 @@ function resolvePartialId(db, table, partialId) {
|
|
|
13147
13201
|
return null;
|
|
13148
13202
|
}
|
|
13149
13203
|
|
|
13150
|
-
// src/db/
|
|
13151
|
-
|
|
13152
|
-
|
|
13153
|
-
|
|
13154
|
-
status: row.status,
|
|
13155
|
-
config: JSON.parse(row.config || "{}"),
|
|
13156
|
-
metadata: JSON.parse(row.metadata || "{}")
|
|
13157
|
-
};
|
|
13204
|
+
// src/db/agents.ts
|
|
13205
|
+
var AGENT_LIST_TTL = 30000;
|
|
13206
|
+
function rowToAgent(row) {
|
|
13207
|
+
return { ...row };
|
|
13158
13208
|
}
|
|
13159
|
-
function
|
|
13209
|
+
function registerAgent(input, db) {
|
|
13160
13210
|
const d = db || getDatabase();
|
|
13211
|
+
const normalizedName = input.name.trim().toLowerCase();
|
|
13212
|
+
const existing = getAgentByName(normalizedName, d);
|
|
13213
|
+
if (existing) {
|
|
13214
|
+
d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [
|
|
13215
|
+
now(),
|
|
13216
|
+
existing.id
|
|
13217
|
+
]);
|
|
13218
|
+
cacheClear("agents:");
|
|
13219
|
+
return getAgent(existing.id, d);
|
|
13220
|
+
}
|
|
13161
13221
|
const id = shortId();
|
|
13162
|
-
d.run(
|
|
13222
|
+
d.run("INSERT INTO agents (id, name, description) VALUES (?, ?, ?)", [
|
|
13163
13223
|
id,
|
|
13164
|
-
|
|
13165
|
-
input.
|
|
13166
|
-
JSON.stringify(input.config || {}),
|
|
13167
|
-
JSON.stringify(input.metadata || {})
|
|
13224
|
+
normalizedName,
|
|
13225
|
+
input.description ?? null
|
|
13168
13226
|
]);
|
|
13169
|
-
|
|
13227
|
+
cacheClear("agents:");
|
|
13228
|
+
return getAgent(id, d);
|
|
13170
13229
|
}
|
|
13171
|
-
function
|
|
13230
|
+
function getAgent(id, db) {
|
|
13172
13231
|
const d = db || getDatabase();
|
|
13173
|
-
const row = d.query("SELECT * FROM
|
|
13174
|
-
return row ?
|
|
13232
|
+
const row = d.query("SELECT * FROM agents WHERE id = ?").get(id);
|
|
13233
|
+
return row ? rowToAgent(row) : null;
|
|
13175
13234
|
}
|
|
13176
|
-
function
|
|
13235
|
+
function getAgentByName(name, db) {
|
|
13177
13236
|
const d = db || getDatabase();
|
|
13178
|
-
const
|
|
13179
|
-
|
|
13237
|
+
const normalizedName = name.trim().toLowerCase();
|
|
13238
|
+
const row = d.query("SELECT * FROM agents WHERE name = ?").get(normalizedName);
|
|
13239
|
+
return row ? rowToAgent(row) : null;
|
|
13180
13240
|
}
|
|
13181
|
-
function
|
|
13182
|
-
|
|
13183
|
-
|
|
13184
|
-
|
|
13241
|
+
function listAgents(db) {
|
|
13242
|
+
return cached("agents:list", AGENT_LIST_TTL, () => {
|
|
13243
|
+
const d = db || getDatabase();
|
|
13244
|
+
const rows = d.query("SELECT * FROM agents ORDER BY last_seen_at DESC").all();
|
|
13245
|
+
return rows.map(rowToAgent);
|
|
13246
|
+
});
|
|
13185
13247
|
}
|
|
13186
|
-
function
|
|
13248
|
+
function heartbeatAgent(idOrName, db) {
|
|
13187
13249
|
const d = db || getDatabase();
|
|
13188
|
-
const
|
|
13189
|
-
if (!
|
|
13190
|
-
|
|
13191
|
-
|
|
13192
|
-
|
|
13193
|
-
if (input.name !== undefined) {
|
|
13194
|
-
sets.push("name = ?");
|
|
13195
|
-
params.push(input.name);
|
|
13196
|
-
}
|
|
13197
|
-
if (input.config !== undefined) {
|
|
13198
|
-
sets.push("config = ?");
|
|
13199
|
-
params.push(JSON.stringify(input.config));
|
|
13200
|
-
}
|
|
13201
|
-
if (input.metadata !== undefined) {
|
|
13202
|
-
sets.push("metadata = ?");
|
|
13203
|
-
params.push(JSON.stringify(input.metadata));
|
|
13204
|
-
}
|
|
13205
|
-
if (input.status !== undefined) {
|
|
13206
|
-
sets.push("status = ?");
|
|
13207
|
-
params.push(input.status);
|
|
13208
|
-
}
|
|
13209
|
-
if (sets.length > 0) {
|
|
13210
|
-
sets.push("updated_at = ?");
|
|
13211
|
-
params.push(now());
|
|
13212
|
-
params.push(id);
|
|
13213
|
-
d.run(`UPDATE providers SET ${sets.join(", ")} WHERE id = ?`, params);
|
|
13214
|
-
}
|
|
13215
|
-
return getProvider(id, d);
|
|
13250
|
+
const agent = getAgent(idOrName, d) ?? getAgentByName(idOrName, d);
|
|
13251
|
+
if (!agent)
|
|
13252
|
+
return null;
|
|
13253
|
+
d.run("UPDATE agents SET last_seen_at = ? WHERE id = ?", [now(), agent.id]);
|
|
13254
|
+
return getAgent(agent.id, d);
|
|
13216
13255
|
}
|
|
13217
|
-
function
|
|
13256
|
+
function setAgentFocus(idOrName, projectId, db) {
|
|
13218
13257
|
const d = db || getDatabase();
|
|
13219
|
-
const
|
|
13220
|
-
if (
|
|
13221
|
-
|
|
13222
|
-
|
|
13223
|
-
|
|
13224
|
-
|
|
13225
|
-
return getProvider(existing.id, d);
|
|
13226
|
-
}
|
|
13227
|
-
return createProvider({ name, type, config }, d);
|
|
13258
|
+
const agent = getAgent(idOrName, d) ?? getAgentByName(idOrName, d);
|
|
13259
|
+
if (!agent)
|
|
13260
|
+
return null;
|
|
13261
|
+
d.run("UPDATE agents SET active_project_id = ?, last_seen_at = ? WHERE id = ?", [projectId, now(), agent.id]);
|
|
13262
|
+
cacheClear("agents:");
|
|
13263
|
+
return getAgent(agent.id, d);
|
|
13228
13264
|
}
|
|
13229
13265
|
|
|
13230
13266
|
// src/db/cards.ts
|
|
@@ -13239,8 +13275,8 @@ function rowToCard(row) {
|
|
|
13239
13275
|
function createCardRecord(input, db) {
|
|
13240
13276
|
const d = db || getDatabase();
|
|
13241
13277
|
const id = uuid();
|
|
13242
|
-
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)
|
|
13243
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
13278
|
+
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, idempotency_key)
|
|
13279
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
13244
13280
|
id,
|
|
13245
13281
|
input.provider_id,
|
|
13246
13282
|
input.external_id,
|
|
@@ -13254,7 +13290,8 @@ function createCardRecord(input, db) {
|
|
|
13254
13290
|
input.spending_limit ?? null,
|
|
13255
13291
|
input.agent_id ?? null,
|
|
13256
13292
|
JSON.stringify(input.metadata || {}),
|
|
13257
|
-
input.expires_at ?? null
|
|
13293
|
+
input.expires_at ?? null,
|
|
13294
|
+
input.idempotency_key ?? null
|
|
13258
13295
|
]);
|
|
13259
13296
|
return getCard(id, d);
|
|
13260
13297
|
}
|
|
@@ -13263,6 +13300,11 @@ function getCard(id, db) {
|
|
|
13263
13300
|
const row = d.query("SELECT * FROM cards WHERE id = ?").get(id);
|
|
13264
13301
|
return row ? rowToCard(row) : null;
|
|
13265
13302
|
}
|
|
13303
|
+
function getCardByIdempotencyKey(idempotencyKey, db) {
|
|
13304
|
+
const d = db || getDatabase();
|
|
13305
|
+
const row = d.query("SELECT * FROM cards WHERE idempotency_key = ?").get(idempotencyKey);
|
|
13306
|
+
return row ? rowToCard(row) : null;
|
|
13307
|
+
}
|
|
13266
13308
|
function listCards(filter = {}, db) {
|
|
13267
13309
|
const d = db || getDatabase();
|
|
13268
13310
|
const conditions = [];
|
|
@@ -13322,10 +13364,102 @@ function updateCard(id, input, db) {
|
|
|
13322
13364
|
}
|
|
13323
13365
|
function updateCardBalance(id, balance, db) {
|
|
13324
13366
|
const d = db || getDatabase();
|
|
13325
|
-
d.run("UPDATE cards SET balance = ?, updated_at = ? WHERE id = ?", [
|
|
13367
|
+
d.run("UPDATE cards SET balance = ?, updated_at = ? WHERE id = ?", [
|
|
13368
|
+
balance,
|
|
13369
|
+
now(),
|
|
13370
|
+
id
|
|
13371
|
+
]);
|
|
13326
13372
|
return getCard(id, d);
|
|
13327
13373
|
}
|
|
13328
13374
|
|
|
13375
|
+
// src/db/providers.ts
|
|
13376
|
+
var PROVIDER_LIST_TTL = 30000;
|
|
13377
|
+
function rowToProvider(row) {
|
|
13378
|
+
return {
|
|
13379
|
+
...row,
|
|
13380
|
+
status: row.status,
|
|
13381
|
+
config: JSON.parse(row.config || "{}"),
|
|
13382
|
+
metadata: JSON.parse(row.metadata || "{}")
|
|
13383
|
+
};
|
|
13384
|
+
}
|
|
13385
|
+
function createProvider(input, db) {
|
|
13386
|
+
const d = db || getDatabase();
|
|
13387
|
+
const id = shortId();
|
|
13388
|
+
d.run("INSERT INTO providers (id, name, type, config, metadata) VALUES (?, ?, ?, ?, ?)", [
|
|
13389
|
+
id,
|
|
13390
|
+
input.name,
|
|
13391
|
+
input.type,
|
|
13392
|
+
JSON.stringify(input.config || {}),
|
|
13393
|
+
JSON.stringify(input.metadata || {})
|
|
13394
|
+
]);
|
|
13395
|
+
cacheClear("providers:");
|
|
13396
|
+
return getProvider(id, d);
|
|
13397
|
+
}
|
|
13398
|
+
function getProvider(id, db) {
|
|
13399
|
+
const d = db || getDatabase();
|
|
13400
|
+
const row = d.query("SELECT * FROM providers WHERE id = ?").get(id);
|
|
13401
|
+
return row ? rowToProvider(row) : null;
|
|
13402
|
+
}
|
|
13403
|
+
function getProviderByName(name, db) {
|
|
13404
|
+
const d = db || getDatabase();
|
|
13405
|
+
const row = d.query("SELECT * FROM providers WHERE name = ?").get(name);
|
|
13406
|
+
return row ? rowToProvider(row) : null;
|
|
13407
|
+
}
|
|
13408
|
+
function listProviders(db) {
|
|
13409
|
+
return cached("providers:list", PROVIDER_LIST_TTL, () => {
|
|
13410
|
+
const d = db || getDatabase();
|
|
13411
|
+
const rows = d.query("SELECT * FROM providers ORDER BY created_at DESC").all();
|
|
13412
|
+
return rows.map(rowToProvider);
|
|
13413
|
+
});
|
|
13414
|
+
}
|
|
13415
|
+
function updateProvider(id, input, db) {
|
|
13416
|
+
const d = db || getDatabase();
|
|
13417
|
+
const provider = getProvider(id, d);
|
|
13418
|
+
if (!provider)
|
|
13419
|
+
throw new Error(`Provider not found: ${id}`);
|
|
13420
|
+
const sets = [];
|
|
13421
|
+
const params = [];
|
|
13422
|
+
if (input.name !== undefined) {
|
|
13423
|
+
sets.push("name = ?");
|
|
13424
|
+
params.push(input.name);
|
|
13425
|
+
}
|
|
13426
|
+
if (input.config !== undefined) {
|
|
13427
|
+
sets.push("config = ?");
|
|
13428
|
+
params.push(JSON.stringify(input.config));
|
|
13429
|
+
}
|
|
13430
|
+
if (input.metadata !== undefined) {
|
|
13431
|
+
sets.push("metadata = ?");
|
|
13432
|
+
params.push(JSON.stringify(input.metadata));
|
|
13433
|
+
}
|
|
13434
|
+
if (input.status !== undefined) {
|
|
13435
|
+
sets.push("status = ?");
|
|
13436
|
+
params.push(input.status);
|
|
13437
|
+
}
|
|
13438
|
+
if (sets.length > 0) {
|
|
13439
|
+
sets.push("updated_at = ?");
|
|
13440
|
+
params.push(now());
|
|
13441
|
+
params.push(id);
|
|
13442
|
+
d.run(`UPDATE providers SET ${sets.join(", ")} WHERE id = ?`, params);
|
|
13443
|
+
cacheClear("providers:");
|
|
13444
|
+
}
|
|
13445
|
+
return getProvider(id, d);
|
|
13446
|
+
}
|
|
13447
|
+
function ensureProvider(name, type, config, db) {
|
|
13448
|
+
const d = db || getDatabase();
|
|
13449
|
+
const existing = getProviderByName(name, d);
|
|
13450
|
+
if (existing) {
|
|
13451
|
+
if (config) {
|
|
13452
|
+
return updateProvider(existing.id, { config }, d);
|
|
13453
|
+
}
|
|
13454
|
+
d.run("UPDATE providers SET updated_at = ? WHERE id = ?", [
|
|
13455
|
+
now(),
|
|
13456
|
+
existing.id
|
|
13457
|
+
]);
|
|
13458
|
+
return getProvider(existing.id, d);
|
|
13459
|
+
}
|
|
13460
|
+
return createProvider({ name, type, config }, d);
|
|
13461
|
+
}
|
|
13462
|
+
|
|
13329
13463
|
// src/db/transactions.ts
|
|
13330
13464
|
function rowToTransaction(row) {
|
|
13331
13465
|
return {
|
|
@@ -13363,53 +13497,688 @@ function listTransactions(filter = {}, db) {
|
|
|
13363
13497
|
return rows.map(rowToTransaction);
|
|
13364
13498
|
}
|
|
13365
13499
|
|
|
13366
|
-
// src/
|
|
13367
|
-
|
|
13368
|
-
|
|
13369
|
-
}
|
|
13370
|
-
|
|
13371
|
-
|
|
13372
|
-
|
|
13373
|
-
|
|
13374
|
-
|
|
13375
|
-
|
|
13376
|
-
|
|
13500
|
+
// src/lib/config.ts
|
|
13501
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync, writeFileSync } from "fs";
|
|
13502
|
+
import { homedir as homedir5 } from "os";
|
|
13503
|
+
import { join as join5 } from "path";
|
|
13504
|
+
|
|
13505
|
+
// node_modules/chalk/source/vendor/ansi-styles/index.js
|
|
13506
|
+
var ANSI_BACKGROUND_OFFSET = 10;
|
|
13507
|
+
var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
|
|
13508
|
+
var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`;
|
|
13509
|
+
var wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`;
|
|
13510
|
+
var styles = {
|
|
13511
|
+
modifier: {
|
|
13512
|
+
reset: [0, 0],
|
|
13513
|
+
bold: [1, 22],
|
|
13514
|
+
dim: [2, 22],
|
|
13515
|
+
italic: [3, 23],
|
|
13516
|
+
underline: [4, 24],
|
|
13517
|
+
overline: [53, 55],
|
|
13518
|
+
inverse: [7, 27],
|
|
13519
|
+
hidden: [8, 28],
|
|
13520
|
+
strikethrough: [9, 29]
|
|
13521
|
+
},
|
|
13522
|
+
color: {
|
|
13523
|
+
black: [30, 39],
|
|
13524
|
+
red: [31, 39],
|
|
13525
|
+
green: [32, 39],
|
|
13526
|
+
yellow: [33, 39],
|
|
13527
|
+
blue: [34, 39],
|
|
13528
|
+
magenta: [35, 39],
|
|
13529
|
+
cyan: [36, 39],
|
|
13530
|
+
white: [37, 39],
|
|
13531
|
+
blackBright: [90, 39],
|
|
13532
|
+
gray: [90, 39],
|
|
13533
|
+
grey: [90, 39],
|
|
13534
|
+
redBright: [91, 39],
|
|
13535
|
+
greenBright: [92, 39],
|
|
13536
|
+
yellowBright: [93, 39],
|
|
13537
|
+
blueBright: [94, 39],
|
|
13538
|
+
magentaBright: [95, 39],
|
|
13539
|
+
cyanBright: [96, 39],
|
|
13540
|
+
whiteBright: [97, 39]
|
|
13541
|
+
},
|
|
13542
|
+
bgColor: {
|
|
13543
|
+
bgBlack: [40, 49],
|
|
13544
|
+
bgRed: [41, 49],
|
|
13545
|
+
bgGreen: [42, 49],
|
|
13546
|
+
bgYellow: [43, 49],
|
|
13547
|
+
bgBlue: [44, 49],
|
|
13548
|
+
bgMagenta: [45, 49],
|
|
13549
|
+
bgCyan: [46, 49],
|
|
13550
|
+
bgWhite: [47, 49],
|
|
13551
|
+
bgBlackBright: [100, 49],
|
|
13552
|
+
bgGray: [100, 49],
|
|
13553
|
+
bgGrey: [100, 49],
|
|
13554
|
+
bgRedBright: [101, 49],
|
|
13555
|
+
bgGreenBright: [102, 49],
|
|
13556
|
+
bgYellowBright: [103, 49],
|
|
13557
|
+
bgBlueBright: [104, 49],
|
|
13558
|
+
bgMagentaBright: [105, 49],
|
|
13559
|
+
bgCyanBright: [106, 49],
|
|
13560
|
+
bgWhiteBright: [107, 49]
|
|
13561
|
+
}
|
|
13562
|
+
};
|
|
13563
|
+
var modifierNames = Object.keys(styles.modifier);
|
|
13564
|
+
var foregroundColorNames = Object.keys(styles.color);
|
|
13565
|
+
var backgroundColorNames = Object.keys(styles.bgColor);
|
|
13566
|
+
var colorNames = [...foregroundColorNames, ...backgroundColorNames];
|
|
13567
|
+
function assembleStyles() {
|
|
13568
|
+
const codes = new Map;
|
|
13569
|
+
for (const [groupName, group] of Object.entries(styles)) {
|
|
13570
|
+
for (const [styleName, style] of Object.entries(group)) {
|
|
13571
|
+
styles[styleName] = {
|
|
13572
|
+
open: `\x1B[${style[0]}m`,
|
|
13573
|
+
close: `\x1B[${style[1]}m`
|
|
13574
|
+
};
|
|
13575
|
+
group[styleName] = styles[styleName];
|
|
13576
|
+
codes.set(style[0], style[1]);
|
|
13577
|
+
}
|
|
13578
|
+
Object.defineProperty(styles, groupName, {
|
|
13579
|
+
value: group,
|
|
13580
|
+
enumerable: false
|
|
13581
|
+
});
|
|
13582
|
+
}
|
|
13583
|
+
Object.defineProperty(styles, "codes", {
|
|
13584
|
+
value: codes,
|
|
13585
|
+
enumerable: false
|
|
13586
|
+
});
|
|
13587
|
+
styles.color.close = "\x1B[39m";
|
|
13588
|
+
styles.bgColor.close = "\x1B[49m";
|
|
13589
|
+
styles.color.ansi = wrapAnsi16();
|
|
13590
|
+
styles.color.ansi256 = wrapAnsi256();
|
|
13591
|
+
styles.color.ansi16m = wrapAnsi16m();
|
|
13592
|
+
styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
|
|
13593
|
+
styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
|
|
13594
|
+
styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
|
|
13595
|
+
Object.defineProperties(styles, {
|
|
13596
|
+
rgbToAnsi256: {
|
|
13597
|
+
value(red, green, blue) {
|
|
13598
|
+
if (red === green && green === blue) {
|
|
13599
|
+
if (red < 8) {
|
|
13600
|
+
return 16;
|
|
13601
|
+
}
|
|
13602
|
+
if (red > 248) {
|
|
13603
|
+
return 231;
|
|
13604
|
+
}
|
|
13605
|
+
return Math.round((red - 8) / 247 * 24) + 232;
|
|
13606
|
+
}
|
|
13607
|
+
return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
|
|
13608
|
+
},
|
|
13609
|
+
enumerable: false
|
|
13610
|
+
},
|
|
13611
|
+
hexToRgb: {
|
|
13612
|
+
value(hex) {
|
|
13613
|
+
const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
|
|
13614
|
+
if (!matches) {
|
|
13615
|
+
return [0, 0, 0];
|
|
13616
|
+
}
|
|
13617
|
+
let [colorString] = matches;
|
|
13618
|
+
if (colorString.length === 3) {
|
|
13619
|
+
colorString = [...colorString].map((character) => character + character).join("");
|
|
13620
|
+
}
|
|
13621
|
+
const integer = Number.parseInt(colorString, 16);
|
|
13622
|
+
return [
|
|
13623
|
+
integer >> 16 & 255,
|
|
13624
|
+
integer >> 8 & 255,
|
|
13625
|
+
integer & 255
|
|
13626
|
+
];
|
|
13627
|
+
},
|
|
13628
|
+
enumerable: false
|
|
13629
|
+
},
|
|
13630
|
+
hexToAnsi256: {
|
|
13631
|
+
value: (hex) => styles.rgbToAnsi256(...styles.hexToRgb(hex)),
|
|
13632
|
+
enumerable: false
|
|
13633
|
+
},
|
|
13634
|
+
ansi256ToAnsi: {
|
|
13635
|
+
value(code) {
|
|
13636
|
+
if (code < 8) {
|
|
13637
|
+
return 30 + code;
|
|
13638
|
+
}
|
|
13639
|
+
if (code < 16) {
|
|
13640
|
+
return 90 + (code - 8);
|
|
13641
|
+
}
|
|
13642
|
+
let red;
|
|
13643
|
+
let green;
|
|
13644
|
+
let blue;
|
|
13645
|
+
if (code >= 232) {
|
|
13646
|
+
red = ((code - 232) * 10 + 8) / 255;
|
|
13647
|
+
green = red;
|
|
13648
|
+
blue = red;
|
|
13649
|
+
} else {
|
|
13650
|
+
code -= 16;
|
|
13651
|
+
const remainder = code % 36;
|
|
13652
|
+
red = Math.floor(code / 36) / 5;
|
|
13653
|
+
green = Math.floor(remainder / 6) / 5;
|
|
13654
|
+
blue = remainder % 6 / 5;
|
|
13655
|
+
}
|
|
13656
|
+
const value = Math.max(red, green, blue) * 2;
|
|
13657
|
+
if (value === 0) {
|
|
13658
|
+
return 30;
|
|
13659
|
+
}
|
|
13660
|
+
let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
|
|
13661
|
+
if (value === 2) {
|
|
13662
|
+
result += 60;
|
|
13663
|
+
}
|
|
13664
|
+
return result;
|
|
13665
|
+
},
|
|
13666
|
+
enumerable: false
|
|
13667
|
+
},
|
|
13668
|
+
rgbToAnsi: {
|
|
13669
|
+
value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),
|
|
13670
|
+
enumerable: false
|
|
13671
|
+
},
|
|
13672
|
+
hexToAnsi: {
|
|
13673
|
+
value: (hex) => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)),
|
|
13674
|
+
enumerable: false
|
|
13675
|
+
}
|
|
13676
|
+
});
|
|
13677
|
+
return styles;
|
|
13678
|
+
}
|
|
13679
|
+
var ansiStyles = assembleStyles();
|
|
13680
|
+
var ansi_styles_default = ansiStyles;
|
|
13681
|
+
|
|
13682
|
+
// node_modules/chalk/source/vendor/supports-color/index.js
|
|
13683
|
+
import process2 from "process";
|
|
13684
|
+
import os from "os";
|
|
13685
|
+
import tty from "tty";
|
|
13686
|
+
function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process2.argv) {
|
|
13687
|
+
const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
|
|
13688
|
+
const position = argv.indexOf(prefix + flag);
|
|
13689
|
+
const terminatorPosition = argv.indexOf("--");
|
|
13690
|
+
return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
|
|
13691
|
+
}
|
|
13692
|
+
var { env } = process2;
|
|
13693
|
+
var flagForceColor;
|
|
13694
|
+
if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
|
|
13695
|
+
flagForceColor = 0;
|
|
13696
|
+
} else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
|
|
13697
|
+
flagForceColor = 1;
|
|
13698
|
+
}
|
|
13699
|
+
function envForceColor() {
|
|
13700
|
+
if ("FORCE_COLOR" in env) {
|
|
13701
|
+
if (env.FORCE_COLOR === "true") {
|
|
13702
|
+
return 1;
|
|
13703
|
+
}
|
|
13704
|
+
if (env.FORCE_COLOR === "false") {
|
|
13705
|
+
return 0;
|
|
13706
|
+
}
|
|
13707
|
+
return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
|
|
13377
13708
|
}
|
|
13378
|
-
const id = shortId();
|
|
13379
|
-
d.run("INSERT INTO agents (id, name, description) VALUES (?, ?, ?)", [id, normalizedName, input.description ?? null]);
|
|
13380
|
-
return getAgent(id, d);
|
|
13381
|
-
}
|
|
13382
|
-
function getAgent(id, db) {
|
|
13383
|
-
const d = db || getDatabase();
|
|
13384
|
-
const row = d.query("SELECT * FROM agents WHERE id = ?").get(id);
|
|
13385
|
-
return row ? rowToAgent(row) : null;
|
|
13386
13709
|
}
|
|
13387
|
-
function
|
|
13388
|
-
|
|
13389
|
-
|
|
13390
|
-
|
|
13391
|
-
return
|
|
13710
|
+
function translateLevel(level) {
|
|
13711
|
+
if (level === 0) {
|
|
13712
|
+
return false;
|
|
13713
|
+
}
|
|
13714
|
+
return {
|
|
13715
|
+
level,
|
|
13716
|
+
hasBasic: true,
|
|
13717
|
+
has256: level >= 2,
|
|
13718
|
+
has16m: level >= 3
|
|
13719
|
+
};
|
|
13392
13720
|
}
|
|
13393
|
-
function
|
|
13394
|
-
const
|
|
13395
|
-
|
|
13396
|
-
|
|
13721
|
+
function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
|
|
13722
|
+
const noFlagForceColor = envForceColor();
|
|
13723
|
+
if (noFlagForceColor !== undefined) {
|
|
13724
|
+
flagForceColor = noFlagForceColor;
|
|
13725
|
+
}
|
|
13726
|
+
const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
|
|
13727
|
+
if (forceColor === 0) {
|
|
13728
|
+
return 0;
|
|
13729
|
+
}
|
|
13730
|
+
if (sniffFlags) {
|
|
13731
|
+
if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
|
|
13732
|
+
return 3;
|
|
13733
|
+
}
|
|
13734
|
+
if (hasFlag("color=256")) {
|
|
13735
|
+
return 2;
|
|
13736
|
+
}
|
|
13737
|
+
}
|
|
13738
|
+
if ("TF_BUILD" in env && "AGENT_NAME" in env) {
|
|
13739
|
+
return 1;
|
|
13740
|
+
}
|
|
13741
|
+
if (haveStream && !streamIsTTY && forceColor === undefined) {
|
|
13742
|
+
return 0;
|
|
13743
|
+
}
|
|
13744
|
+
const min = forceColor || 0;
|
|
13745
|
+
if (env.TERM === "dumb") {
|
|
13746
|
+
return min;
|
|
13747
|
+
}
|
|
13748
|
+
if (process2.platform === "win32") {
|
|
13749
|
+
const osRelease = os.release().split(".");
|
|
13750
|
+
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
13751
|
+
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
13752
|
+
}
|
|
13753
|
+
return 1;
|
|
13754
|
+
}
|
|
13755
|
+
if ("CI" in env) {
|
|
13756
|
+
if (["GITHUB_ACTIONS", "GITEA_ACTIONS", "CIRCLECI"].some((key) => (key in env))) {
|
|
13757
|
+
return 3;
|
|
13758
|
+
}
|
|
13759
|
+
if (["TRAVIS", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => (sign in env)) || env.CI_NAME === "codeship") {
|
|
13760
|
+
return 1;
|
|
13761
|
+
}
|
|
13762
|
+
return min;
|
|
13763
|
+
}
|
|
13764
|
+
if ("TEAMCITY_VERSION" in env) {
|
|
13765
|
+
return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
|
|
13766
|
+
}
|
|
13767
|
+
if (env.COLORTERM === "truecolor") {
|
|
13768
|
+
return 3;
|
|
13769
|
+
}
|
|
13770
|
+
if (env.TERM === "xterm-kitty") {
|
|
13771
|
+
return 3;
|
|
13772
|
+
}
|
|
13773
|
+
if (env.TERM === "xterm-ghostty") {
|
|
13774
|
+
return 3;
|
|
13775
|
+
}
|
|
13776
|
+
if (env.TERM === "wezterm") {
|
|
13777
|
+
return 3;
|
|
13778
|
+
}
|
|
13779
|
+
if ("TERM_PROGRAM" in env) {
|
|
13780
|
+
const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
|
|
13781
|
+
switch (env.TERM_PROGRAM) {
|
|
13782
|
+
case "iTerm.app": {
|
|
13783
|
+
return version >= 3 ? 3 : 2;
|
|
13784
|
+
}
|
|
13785
|
+
case "Apple_Terminal": {
|
|
13786
|
+
return 2;
|
|
13787
|
+
}
|
|
13788
|
+
}
|
|
13789
|
+
}
|
|
13790
|
+
if (/-256(color)?$/i.test(env.TERM)) {
|
|
13791
|
+
return 2;
|
|
13792
|
+
}
|
|
13793
|
+
if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
|
|
13794
|
+
return 1;
|
|
13795
|
+
}
|
|
13796
|
+
if ("COLORTERM" in env) {
|
|
13797
|
+
return 1;
|
|
13798
|
+
}
|
|
13799
|
+
return min;
|
|
13397
13800
|
}
|
|
13398
|
-
function
|
|
13399
|
-
const
|
|
13400
|
-
|
|
13401
|
-
|
|
13402
|
-
|
|
13403
|
-
|
|
13404
|
-
return getAgent(agent.id, d);
|
|
13801
|
+
function createSupportsColor(stream, options = {}) {
|
|
13802
|
+
const level = _supportsColor(stream, {
|
|
13803
|
+
streamIsTTY: stream && stream.isTTY,
|
|
13804
|
+
...options
|
|
13805
|
+
});
|
|
13806
|
+
return translateLevel(level);
|
|
13405
13807
|
}
|
|
13406
|
-
|
|
13407
|
-
|
|
13408
|
-
|
|
13409
|
-
|
|
13410
|
-
|
|
13411
|
-
|
|
13412
|
-
|
|
13808
|
+
var supportsColor = {
|
|
13809
|
+
stdout: createSupportsColor({ isTTY: tty.isatty(1) }),
|
|
13810
|
+
stderr: createSupportsColor({ isTTY: tty.isatty(2) })
|
|
13811
|
+
};
|
|
13812
|
+
var supports_color_default = supportsColor;
|
|
13813
|
+
|
|
13814
|
+
// node_modules/chalk/source/utilities.js
|
|
13815
|
+
function stringReplaceAll(string, substring, replacer) {
|
|
13816
|
+
let index = string.indexOf(substring);
|
|
13817
|
+
if (index === -1) {
|
|
13818
|
+
return string;
|
|
13819
|
+
}
|
|
13820
|
+
const substringLength = substring.length;
|
|
13821
|
+
let endIndex = 0;
|
|
13822
|
+
let returnValue = "";
|
|
13823
|
+
do {
|
|
13824
|
+
returnValue += string.slice(endIndex, index) + substring + replacer;
|
|
13825
|
+
endIndex = index + substringLength;
|
|
13826
|
+
index = string.indexOf(substring, endIndex);
|
|
13827
|
+
} while (index !== -1);
|
|
13828
|
+
returnValue += string.slice(endIndex);
|
|
13829
|
+
return returnValue;
|
|
13830
|
+
}
|
|
13831
|
+
function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) {
|
|
13832
|
+
let endIndex = 0;
|
|
13833
|
+
let returnValue = "";
|
|
13834
|
+
do {
|
|
13835
|
+
const gotCR = string[index - 1] === "\r";
|
|
13836
|
+
returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? `\r
|
|
13837
|
+
` : `
|
|
13838
|
+
`) + postfix;
|
|
13839
|
+
endIndex = index + 1;
|
|
13840
|
+
index = string.indexOf(`
|
|
13841
|
+
`, endIndex);
|
|
13842
|
+
} while (index !== -1);
|
|
13843
|
+
returnValue += string.slice(endIndex);
|
|
13844
|
+
return returnValue;
|
|
13845
|
+
}
|
|
13846
|
+
|
|
13847
|
+
// node_modules/chalk/source/index.js
|
|
13848
|
+
var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default;
|
|
13849
|
+
var GENERATOR = Symbol("GENERATOR");
|
|
13850
|
+
var STYLER = Symbol("STYLER");
|
|
13851
|
+
var IS_EMPTY = Symbol("IS_EMPTY");
|
|
13852
|
+
var levelMapping = [
|
|
13853
|
+
"ansi",
|
|
13854
|
+
"ansi",
|
|
13855
|
+
"ansi256",
|
|
13856
|
+
"ansi16m"
|
|
13857
|
+
];
|
|
13858
|
+
var styles2 = Object.create(null);
|
|
13859
|
+
var applyOptions = (object, options = {}) => {
|
|
13860
|
+
if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) {
|
|
13861
|
+
throw new Error("The `level` option should be an integer from 0 to 3");
|
|
13862
|
+
}
|
|
13863
|
+
const colorLevel = stdoutColor ? stdoutColor.level : 0;
|
|
13864
|
+
object.level = options.level === undefined ? colorLevel : options.level;
|
|
13865
|
+
};
|
|
13866
|
+
var chalkFactory = (options) => {
|
|
13867
|
+
const chalk = (...strings) => strings.join(" ");
|
|
13868
|
+
applyOptions(chalk, options);
|
|
13869
|
+
Object.setPrototypeOf(chalk, createChalk.prototype);
|
|
13870
|
+
return chalk;
|
|
13871
|
+
};
|
|
13872
|
+
function createChalk(options) {
|
|
13873
|
+
return chalkFactory(options);
|
|
13874
|
+
}
|
|
13875
|
+
Object.setPrototypeOf(createChalk.prototype, Function.prototype);
|
|
13876
|
+
for (const [styleName, style] of Object.entries(ansi_styles_default)) {
|
|
13877
|
+
styles2[styleName] = {
|
|
13878
|
+
get() {
|
|
13879
|
+
const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]);
|
|
13880
|
+
Object.defineProperty(this, styleName, { value: builder });
|
|
13881
|
+
return builder;
|
|
13882
|
+
}
|
|
13883
|
+
};
|
|
13884
|
+
}
|
|
13885
|
+
styles2.visible = {
|
|
13886
|
+
get() {
|
|
13887
|
+
const builder = createBuilder(this, this[STYLER], true);
|
|
13888
|
+
Object.defineProperty(this, "visible", { value: builder });
|
|
13889
|
+
return builder;
|
|
13890
|
+
}
|
|
13891
|
+
};
|
|
13892
|
+
var getModelAnsi = (model, level, type, ...arguments_) => {
|
|
13893
|
+
if (model === "rgb") {
|
|
13894
|
+
if (level === "ansi16m") {
|
|
13895
|
+
return ansi_styles_default[type].ansi16m(...arguments_);
|
|
13896
|
+
}
|
|
13897
|
+
if (level === "ansi256") {
|
|
13898
|
+
return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_));
|
|
13899
|
+
}
|
|
13900
|
+
return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_));
|
|
13901
|
+
}
|
|
13902
|
+
if (model === "hex") {
|
|
13903
|
+
return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_));
|
|
13904
|
+
}
|
|
13905
|
+
return ansi_styles_default[type][model](...arguments_);
|
|
13906
|
+
};
|
|
13907
|
+
var usedModels = ["rgb", "hex", "ansi256"];
|
|
13908
|
+
for (const model of usedModels) {
|
|
13909
|
+
styles2[model] = {
|
|
13910
|
+
get() {
|
|
13911
|
+
const { level } = this;
|
|
13912
|
+
return function(...arguments_) {
|
|
13913
|
+
const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansi_styles_default.color.close, this[STYLER]);
|
|
13914
|
+
return createBuilder(this, styler, this[IS_EMPTY]);
|
|
13915
|
+
};
|
|
13916
|
+
}
|
|
13917
|
+
};
|
|
13918
|
+
const bgModel = "bg" + model[0].toUpperCase() + model.slice(1);
|
|
13919
|
+
styles2[bgModel] = {
|
|
13920
|
+
get() {
|
|
13921
|
+
const { level } = this;
|
|
13922
|
+
return function(...arguments_) {
|
|
13923
|
+
const styler = createStyler(getModelAnsi(model, levelMapping[level], "bgColor", ...arguments_), ansi_styles_default.bgColor.close, this[STYLER]);
|
|
13924
|
+
return createBuilder(this, styler, this[IS_EMPTY]);
|
|
13925
|
+
};
|
|
13926
|
+
}
|
|
13927
|
+
};
|
|
13928
|
+
}
|
|
13929
|
+
var proto = Object.defineProperties(() => {}, {
|
|
13930
|
+
...styles2,
|
|
13931
|
+
level: {
|
|
13932
|
+
enumerable: true,
|
|
13933
|
+
get() {
|
|
13934
|
+
return this[GENERATOR].level;
|
|
13935
|
+
},
|
|
13936
|
+
set(level) {
|
|
13937
|
+
this[GENERATOR].level = level;
|
|
13938
|
+
}
|
|
13939
|
+
}
|
|
13940
|
+
});
|
|
13941
|
+
var createStyler = (open, close, parent) => {
|
|
13942
|
+
let openAll;
|
|
13943
|
+
let closeAll;
|
|
13944
|
+
if (parent === undefined) {
|
|
13945
|
+
openAll = open;
|
|
13946
|
+
closeAll = close;
|
|
13947
|
+
} else {
|
|
13948
|
+
openAll = parent.openAll + open;
|
|
13949
|
+
closeAll = close + parent.closeAll;
|
|
13950
|
+
}
|
|
13951
|
+
return {
|
|
13952
|
+
open,
|
|
13953
|
+
close,
|
|
13954
|
+
openAll,
|
|
13955
|
+
closeAll,
|
|
13956
|
+
parent
|
|
13957
|
+
};
|
|
13958
|
+
};
|
|
13959
|
+
var createBuilder = (self, _styler, _isEmpty) => {
|
|
13960
|
+
const builder = (...arguments_) => applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" "));
|
|
13961
|
+
Object.setPrototypeOf(builder, proto);
|
|
13962
|
+
builder[GENERATOR] = self;
|
|
13963
|
+
builder[STYLER] = _styler;
|
|
13964
|
+
builder[IS_EMPTY] = _isEmpty;
|
|
13965
|
+
return builder;
|
|
13966
|
+
};
|
|
13967
|
+
var applyStyle = (self, string) => {
|
|
13968
|
+
if (self.level <= 0 || !string) {
|
|
13969
|
+
return self[IS_EMPTY] ? "" : string;
|
|
13970
|
+
}
|
|
13971
|
+
let styler = self[STYLER];
|
|
13972
|
+
if (styler === undefined) {
|
|
13973
|
+
return string;
|
|
13974
|
+
}
|
|
13975
|
+
const { openAll, closeAll } = styler;
|
|
13976
|
+
if (string.includes("\x1B")) {
|
|
13977
|
+
while (styler !== undefined) {
|
|
13978
|
+
string = stringReplaceAll(string, styler.close, styler.open);
|
|
13979
|
+
styler = styler.parent;
|
|
13980
|
+
}
|
|
13981
|
+
}
|
|
13982
|
+
const lfIndex = string.indexOf(`
|
|
13983
|
+
`);
|
|
13984
|
+
if (lfIndex !== -1) {
|
|
13985
|
+
string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex);
|
|
13986
|
+
}
|
|
13987
|
+
return openAll + string + closeAll;
|
|
13988
|
+
};
|
|
13989
|
+
Object.defineProperties(createChalk.prototype, styles2);
|
|
13990
|
+
var chalk = createChalk();
|
|
13991
|
+
var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
13992
|
+
var source_default = chalk;
|
|
13993
|
+
|
|
13994
|
+
// src/lib/config.ts
|
|
13995
|
+
var WalletsConfigSchema = exports_external.object({
|
|
13996
|
+
default_provider: exports_external.string().optional(),
|
|
13997
|
+
default_currency: exports_external.string().optional()
|
|
13998
|
+
});
|
|
13999
|
+
var _configCache = null;
|
|
14000
|
+
var _configCachePath = null;
|
|
14001
|
+
function _invalidateCache() {
|
|
14002
|
+
_configCache = null;
|
|
14003
|
+
_configCachePath = null;
|
|
14004
|
+
}
|
|
14005
|
+
function _loadConfigUncached() {
|
|
14006
|
+
const configPath = getConfigPath();
|
|
14007
|
+
if (!existsSync3(configPath)) {
|
|
14008
|
+
return {};
|
|
14009
|
+
}
|
|
14010
|
+
try {
|
|
14011
|
+
const raw = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
14012
|
+
return WalletsConfigSchema.parse(raw);
|
|
14013
|
+
} catch (e) {
|
|
14014
|
+
if (e instanceof exports_external.ZodError) {
|
|
14015
|
+
console.error(source_default.yellow(`Config validation errors: ${e.errors.map((err) => `${err.path.join(".")}: ${err.message}`).join(", ")}`));
|
|
14016
|
+
}
|
|
14017
|
+
return {};
|
|
14018
|
+
}
|
|
14019
|
+
}
|
|
14020
|
+
function getConfigDir() {
|
|
14021
|
+
if (process.env.WALLETS_CONFIG_DIR)
|
|
14022
|
+
return process.env.WALLETS_CONFIG_DIR;
|
|
14023
|
+
const home = homedir5();
|
|
14024
|
+
const newDir = join5(home, ".hasna", "wallets");
|
|
14025
|
+
const legacyDir = join5(home, ".wallets");
|
|
14026
|
+
if (!existsSync3(newDir) && existsSync3(legacyDir)) {
|
|
14027
|
+
return legacyDir;
|
|
14028
|
+
}
|
|
14029
|
+
return newDir;
|
|
14030
|
+
}
|
|
14031
|
+
function getConfigPath() {
|
|
14032
|
+
return join5(getConfigDir(), "config.json");
|
|
14033
|
+
}
|
|
14034
|
+
function loadConfig() {
|
|
14035
|
+
const configPath = getConfigPath();
|
|
14036
|
+
if (_configCache !== null && _configCachePath === configPath) {
|
|
14037
|
+
return _configCache;
|
|
14038
|
+
}
|
|
14039
|
+
_configCache = _loadConfigUncached();
|
|
14040
|
+
_configCachePath = configPath;
|
|
14041
|
+
return _configCache;
|
|
14042
|
+
}
|
|
14043
|
+
function saveConfig(config) {
|
|
14044
|
+
const configDir = getConfigDir();
|
|
14045
|
+
if (!existsSync3(configDir)) {
|
|
14046
|
+
mkdirSync3(configDir, { recursive: true });
|
|
14047
|
+
}
|
|
14048
|
+
writeFileSync(join5(configDir, "config.json"), `${JSON.stringify(config, null, 2)}
|
|
14049
|
+
`);
|
|
14050
|
+
_invalidateCache();
|
|
14051
|
+
}
|
|
14052
|
+
function getProviderConfig(providerName) {
|
|
14053
|
+
const provider = getProviderByName(providerName);
|
|
14054
|
+
return provider?.config;
|
|
14055
|
+
}
|
|
14056
|
+
function setProviderConfig(providerName, providerConfig) {
|
|
14057
|
+
const provider = getProviderByName(providerName);
|
|
14058
|
+
if (!provider)
|
|
14059
|
+
return;
|
|
14060
|
+
updateProvider(provider.id, { config: providerConfig });
|
|
14061
|
+
}
|
|
14062
|
+
|
|
14063
|
+
// src/lib/doctor.ts
|
|
14064
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync4 } from "fs";
|
|
14065
|
+
import { join as join6 } from "path";
|
|
14066
|
+
function runDoctor(fix = false) {
|
|
14067
|
+
const checks = [];
|
|
14068
|
+
const fixed = [];
|
|
14069
|
+
const configDir = getConfigDir();
|
|
14070
|
+
if (!existsSync4(configDir)) {
|
|
14071
|
+
if (fix) {
|
|
14072
|
+
mkdirSync4(configDir, { recursive: true });
|
|
14073
|
+
fixed.push(`Created config directory at ${configDir}`);
|
|
14074
|
+
}
|
|
14075
|
+
}
|
|
14076
|
+
checks.push({
|
|
14077
|
+
name: "Config directory",
|
|
14078
|
+
status: existsSync4(configDir) ? "ok" : fix ? "ok" : "warn",
|
|
14079
|
+
message: existsSync4(configDir) ? `Found at ${configDir}` : fix ? `Created at ${configDir}` : `Not found at ${configDir}. Run 'wallets provider add' to create it.`
|
|
14080
|
+
});
|
|
14081
|
+
const configPath = getConfigPath();
|
|
14082
|
+
if (!existsSync4(configPath)) {
|
|
14083
|
+
if (fix) {
|
|
14084
|
+
saveConfig({});
|
|
14085
|
+
fixed.push(`Created empty config at ${configPath}`);
|
|
14086
|
+
}
|
|
14087
|
+
}
|
|
14088
|
+
if (existsSync4(configPath)) {
|
|
14089
|
+
try {
|
|
14090
|
+
const config = loadConfig();
|
|
14091
|
+
checks.push({
|
|
14092
|
+
name: "Config file",
|
|
14093
|
+
status: "ok",
|
|
14094
|
+
message: `Valid config at ${configPath}`
|
|
14095
|
+
});
|
|
14096
|
+
if (config.default_provider) {
|
|
14097
|
+
checks.push({
|
|
14098
|
+
name: "Default provider",
|
|
14099
|
+
status: "ok",
|
|
14100
|
+
message: `Set to '${config.default_provider}'`
|
|
14101
|
+
});
|
|
14102
|
+
} else {
|
|
14103
|
+
checks.push({
|
|
14104
|
+
name: "Default provider",
|
|
14105
|
+
status: "warn",
|
|
14106
|
+
message: "No default provider set. Use 'wallets provider add' to register one."
|
|
14107
|
+
});
|
|
14108
|
+
}
|
|
14109
|
+
} catch {
|
|
14110
|
+
checks.push({
|
|
14111
|
+
name: "Config file",
|
|
14112
|
+
status: "error",
|
|
14113
|
+
message: `Invalid JSON at ${configPath}`
|
|
14114
|
+
});
|
|
14115
|
+
}
|
|
14116
|
+
} else {
|
|
14117
|
+
checks.push({
|
|
14118
|
+
name: "Config file",
|
|
14119
|
+
status: "warn",
|
|
14120
|
+
message: `Not found at ${configPath}. Run 'wallets provider add' to create it.`
|
|
14121
|
+
});
|
|
14122
|
+
}
|
|
14123
|
+
try {
|
|
14124
|
+
const db = getDatabase();
|
|
14125
|
+
const providers = listProviders(db);
|
|
14126
|
+
checks.push({
|
|
14127
|
+
name: "Database",
|
|
14128
|
+
status: "ok",
|
|
14129
|
+
message: `Connected. ${providers.length} provider(s) registered.`
|
|
14130
|
+
});
|
|
14131
|
+
for (const provider of providers) {
|
|
14132
|
+
const hasConfig = provider.config && Object.keys(provider.config).length > 0;
|
|
14133
|
+
if (provider.type === "agentcard") {
|
|
14134
|
+
const hasJwt = provider.config && "jwt" in provider.config;
|
|
14135
|
+
checks.push({
|
|
14136
|
+
name: `Provider: ${provider.name}`,
|
|
14137
|
+
status: hasJwt ? "ok" : "error",
|
|
14138
|
+
message: hasJwt ? `AgentCard configured (status: ${provider.status})` : "Missing JWT token. Run 'wallets provider add agentcard' to configure."
|
|
14139
|
+
});
|
|
14140
|
+
} else {
|
|
14141
|
+
checks.push({
|
|
14142
|
+
name: `Provider: ${provider.name}`,
|
|
14143
|
+
status: hasConfig ? "ok" : "warn",
|
|
14144
|
+
message: `Type: ${provider.type}, Status: ${provider.status}${hasConfig ? "" : " (no config)"}`
|
|
14145
|
+
});
|
|
14146
|
+
}
|
|
14147
|
+
}
|
|
14148
|
+
if (providers.length === 0) {
|
|
14149
|
+
checks.push({
|
|
14150
|
+
name: "Providers",
|
|
14151
|
+
status: "warn",
|
|
14152
|
+
message: "No providers registered. Use 'wallets provider add' to add one."
|
|
14153
|
+
});
|
|
14154
|
+
}
|
|
14155
|
+
} catch (e) {
|
|
14156
|
+
checks.push({
|
|
14157
|
+
name: "Database",
|
|
14158
|
+
status: "error",
|
|
14159
|
+
message: `Failed to connect: ${e instanceof Error ? e.message : String(e)}`
|
|
14160
|
+
});
|
|
14161
|
+
}
|
|
14162
|
+
try {
|
|
14163
|
+
const binPath = join6(import.meta.dir, "../../dist/mcp/index.js");
|
|
14164
|
+
const srcPath = join6(import.meta.dir, "../mcp/index.ts");
|
|
14165
|
+
const mcpAvailable = existsSync4(binPath) || existsSync4(srcPath);
|
|
14166
|
+
checks.push({
|
|
14167
|
+
name: "MCP server",
|
|
14168
|
+
status: mcpAvailable ? "ok" : "warn",
|
|
14169
|
+
message: mcpAvailable ? "MCP server binary available" : "MCP server not found. Run 'bun run build' to compile."
|
|
14170
|
+
});
|
|
14171
|
+
} catch {
|
|
14172
|
+
checks.push({
|
|
14173
|
+
name: "MCP server",
|
|
14174
|
+
status: "warn",
|
|
14175
|
+
message: "Could not check MCP server status."
|
|
14176
|
+
});
|
|
14177
|
+
}
|
|
14178
|
+
return {
|
|
14179
|
+
checks,
|
|
14180
|
+
healthy: checks.every((c) => c.status !== "error")
|
|
14181
|
+
};
|
|
13413
14182
|
}
|
|
13414
14183
|
|
|
13415
14184
|
// src/types/index.ts
|
|
@@ -13476,29 +14245,140 @@ class AgentNotFoundError extends Error {
|
|
|
13476
14245
|
}
|
|
13477
14246
|
}
|
|
13478
14247
|
|
|
13479
|
-
// src/
|
|
13480
|
-
|
|
13481
|
-
|
|
13482
|
-
|
|
13483
|
-
|
|
13484
|
-
|
|
13485
|
-
|
|
13486
|
-
|
|
13487
|
-
|
|
13488
|
-
|
|
13489
|
-
|
|
13490
|
-
|
|
13491
|
-
|
|
13492
|
-
|
|
13493
|
-
|
|
13494
|
-
|
|
13495
|
-
|
|
13496
|
-
|
|
13497
|
-
|
|
13498
|
-
|
|
13499
|
-
|
|
13500
|
-
|
|
13501
|
-
|
|
14248
|
+
// src/lib/format.ts
|
|
14249
|
+
function formatCard(card) {
|
|
14250
|
+
const id = card.id.slice(0, 8);
|
|
14251
|
+
const last4 = card.last_four ? `*${card.last_four}` : "----";
|
|
14252
|
+
const category = card.metadata?.category ? `[${card.metadata.category}] ` : "";
|
|
14253
|
+
return `${id} ${card.status.padEnd(8)} ${last4.padEnd(6)} $${card.balance.toFixed(2).padStart(10)} ${category}${card.name}`;
|
|
14254
|
+
}
|
|
14255
|
+
function formatProvider(provider) {
|
|
14256
|
+
return `${provider.id.slice(0, 8)} ${provider.status.padEnd(8)} ${provider.type.padEnd(12)} ${provider.name}`;
|
|
14257
|
+
}
|
|
14258
|
+
function formatTransaction(tx) {
|
|
14259
|
+
const id = tx.id.slice(0, 8);
|
|
14260
|
+
const sign = tx.type === "refund" || tx.type === "load" ? "+" : "-";
|
|
14261
|
+
return `${id} ${tx.type.padEnd(10)} ${tx.status.padEnd(10)} ${sign}$${tx.amount.toFixed(2).padStart(9)} ${tx.merchant || tx.description || ""}`;
|
|
14262
|
+
}
|
|
14263
|
+
function formatDoctorCheck(check) {
|
|
14264
|
+
const icon = check.status === "ok" ? "[ok]" : check.status === "warn" ? "[!!]" : "[ERR]";
|
|
14265
|
+
return `${icon} ${check.name}: ${check.message}`;
|
|
14266
|
+
}
|
|
14267
|
+
function formatError(error, includeStack = false) {
|
|
14268
|
+
return JSON.stringify(formatErrorStructured(error, includeStack));
|
|
14269
|
+
}
|
|
14270
|
+
function formatErrorStructured(error, includeStack = false) {
|
|
14271
|
+
const base = { timestamp: new Date().toISOString() };
|
|
14272
|
+
if (error instanceof ProviderNotFoundError) {
|
|
14273
|
+
return {
|
|
14274
|
+
...base,
|
|
14275
|
+
type: "ProviderNotFoundError",
|
|
14276
|
+
code: ProviderNotFoundError.code,
|
|
14277
|
+
message: error.message,
|
|
14278
|
+
suggestion: ProviderNotFoundError.suggestion,
|
|
14279
|
+
...includeStack && error.stack ? { stack: error.stack } : {}
|
|
14280
|
+
};
|
|
14281
|
+
}
|
|
14282
|
+
if (error instanceof CardNotFoundError) {
|
|
14283
|
+
return {
|
|
14284
|
+
...base,
|
|
14285
|
+
type: "CardNotFoundError",
|
|
14286
|
+
code: CardNotFoundError.code,
|
|
14287
|
+
message: error.message,
|
|
14288
|
+
suggestion: CardNotFoundError.suggestion,
|
|
14289
|
+
...includeStack && error.stack ? { stack: error.stack } : {}
|
|
14290
|
+
};
|
|
14291
|
+
}
|
|
14292
|
+
if (error instanceof ProviderError) {
|
|
14293
|
+
return {
|
|
14294
|
+
...base,
|
|
14295
|
+
type: "ProviderError",
|
|
14296
|
+
code: ProviderError.code,
|
|
14297
|
+
message: error.message,
|
|
14298
|
+
suggestion: ProviderError.suggestion,
|
|
14299
|
+
...includeStack && error.stack ? { stack: error.stack } : {}
|
|
14300
|
+
};
|
|
14301
|
+
}
|
|
14302
|
+
if (error instanceof InsufficientFundsError) {
|
|
14303
|
+
return {
|
|
14304
|
+
...base,
|
|
14305
|
+
type: "InsufficientFundsError",
|
|
14306
|
+
code: InsufficientFundsError.code,
|
|
14307
|
+
message: error.message,
|
|
14308
|
+
suggestion: InsufficientFundsError.suggestion,
|
|
14309
|
+
...includeStack && error.stack ? { stack: error.stack } : {}
|
|
14310
|
+
};
|
|
14311
|
+
}
|
|
14312
|
+
if (error instanceof ConfigError) {
|
|
14313
|
+
return {
|
|
14314
|
+
...base,
|
|
14315
|
+
type: "ConfigError",
|
|
14316
|
+
code: ConfigError.code,
|
|
14317
|
+
message: error.message,
|
|
14318
|
+
suggestion: ConfigError.suggestion,
|
|
14319
|
+
...includeStack && error.stack ? { stack: error.stack } : {}
|
|
14320
|
+
};
|
|
14321
|
+
}
|
|
14322
|
+
if (error instanceof AgentNotFoundError) {
|
|
14323
|
+
return {
|
|
14324
|
+
...base,
|
|
14325
|
+
type: "AgentNotFoundError",
|
|
14326
|
+
code: AgentNotFoundError.code,
|
|
14327
|
+
message: error.message,
|
|
14328
|
+
suggestion: AgentNotFoundError.suggestion,
|
|
14329
|
+
...includeStack && error.stack ? { stack: error.stack } : {}
|
|
14330
|
+
};
|
|
14331
|
+
}
|
|
14332
|
+
if (error instanceof WalletError) {
|
|
14333
|
+
return {
|
|
14334
|
+
...base,
|
|
14335
|
+
type: "WalletError",
|
|
14336
|
+
code: WalletError.code,
|
|
14337
|
+
message: error.message,
|
|
14338
|
+
suggestion: WalletError.suggestion,
|
|
14339
|
+
...includeStack && error.stack ? { stack: error.stack } : {}
|
|
14340
|
+
};
|
|
14341
|
+
}
|
|
14342
|
+
if (error instanceof Error) {
|
|
14343
|
+
return {
|
|
14344
|
+
...base,
|
|
14345
|
+
type: error.name || "Error",
|
|
14346
|
+
code: "UNKNOWN_ERROR",
|
|
14347
|
+
message: error.message,
|
|
14348
|
+
...includeStack && error.stack ? { stack: error.stack } : {}
|
|
14349
|
+
};
|
|
14350
|
+
}
|
|
14351
|
+
return {
|
|
14352
|
+
...base,
|
|
14353
|
+
type: "Unknown",
|
|
14354
|
+
code: "UNKNOWN_ERROR",
|
|
14355
|
+
message: String(error)
|
|
14356
|
+
};
|
|
14357
|
+
}
|
|
14358
|
+
|
|
14359
|
+
// src/providers/agentcard.ts
|
|
14360
|
+
class AgentCardProvider {
|
|
14361
|
+
name = "agentcard";
|
|
14362
|
+
type = "agentcard";
|
|
14363
|
+
jwt;
|
|
14364
|
+
baseUrl;
|
|
14365
|
+
constructor(config) {
|
|
14366
|
+
this.jwt = config.jwt;
|
|
14367
|
+
this.baseUrl = config.baseUrl || "https://api.agentcard.sh";
|
|
14368
|
+
}
|
|
14369
|
+
async request(path, options = {}) {
|
|
14370
|
+
const url = `${this.baseUrl}${path}`;
|
|
14371
|
+
const res = await fetch(url, {
|
|
14372
|
+
...options,
|
|
14373
|
+
headers: {
|
|
14374
|
+
"Content-Type": "application/json",
|
|
14375
|
+
Authorization: `Bearer ${this.jwt}`,
|
|
14376
|
+
...options.headers || {}
|
|
14377
|
+
}
|
|
14378
|
+
});
|
|
14379
|
+
if (!res.ok) {
|
|
14380
|
+
const body = await res.text();
|
|
14381
|
+
throw new ProviderError("agentcard", `HTTP ${res.status}: ${body}`);
|
|
13502
14382
|
}
|
|
13503
14383
|
return res.json();
|
|
13504
14384
|
}
|
|
@@ -13524,7 +14404,8 @@ class AgentCardProvider {
|
|
|
13524
14404
|
expires_at: null,
|
|
13525
14405
|
created_at: new Date().toISOString(),
|
|
13526
14406
|
updated_at: new Date().toISOString(),
|
|
13527
|
-
funding_url: data.funding_url
|
|
14407
|
+
funding_url: data.funding_url,
|
|
14408
|
+
idempotency_key: null
|
|
13528
14409
|
};
|
|
13529
14410
|
}
|
|
13530
14411
|
async listCards() {
|
|
@@ -13545,7 +14426,8 @@ class AgentCardProvider {
|
|
|
13545
14426
|
metadata: {},
|
|
13546
14427
|
expires_at: null,
|
|
13547
14428
|
created_at: c.created_at || new Date().toISOString(),
|
|
13548
|
-
updated_at: new Date().toISOString()
|
|
14429
|
+
updated_at: new Date().toISOString(),
|
|
14430
|
+
idempotency_key: null
|
|
13549
14431
|
}));
|
|
13550
14432
|
}
|
|
13551
14433
|
async getCardDetails(externalId) {
|
|
@@ -13570,7 +14452,8 @@ class AgentCardProvider {
|
|
|
13570
14452
|
pan: data.pan,
|
|
13571
14453
|
cvv: data.cvv,
|
|
13572
14454
|
exp_month: data.exp_month,
|
|
13573
|
-
exp_year: data.exp_year
|
|
14455
|
+
exp_year: data.exp_year,
|
|
14456
|
+
idempotency_key: null
|
|
13574
14457
|
};
|
|
13575
14458
|
}
|
|
13576
14459
|
async getBalance(externalId) {
|
|
@@ -13583,6 +14466,39 @@ class AgentCardProvider {
|
|
|
13583
14466
|
async closeCard(externalId) {
|
|
13584
14467
|
await this.request(`/cards/${externalId}/close`, { method: "POST" });
|
|
13585
14468
|
}
|
|
14469
|
+
async freezeCard(externalId) {
|
|
14470
|
+
await this.request(`/cards/${externalId}/freeze`, { method: "POST" });
|
|
14471
|
+
}
|
|
14472
|
+
async unfreezeCard(externalId) {
|
|
14473
|
+
await this.request(`/cards/${externalId}/unfreeze`, { method: "POST" });
|
|
14474
|
+
}
|
|
14475
|
+
async topUpCard(externalId, amount) {
|
|
14476
|
+
const data = await this.request(`/cards/${externalId}/topup`, {
|
|
14477
|
+
method: "POST",
|
|
14478
|
+
body: JSON.stringify({ amount })
|
|
14479
|
+
});
|
|
14480
|
+
return {
|
|
14481
|
+
balance: data.balance,
|
|
14482
|
+
currency: data.currency || "USD"
|
|
14483
|
+
};
|
|
14484
|
+
}
|
|
14485
|
+
async getTransactions(externalId) {
|
|
14486
|
+
const data = await this.request(`/cards/${externalId}/transactions`);
|
|
14487
|
+
return data.map((tx) => ({
|
|
14488
|
+
id: tx.id,
|
|
14489
|
+
card_id: "",
|
|
14490
|
+
provider_id: "",
|
|
14491
|
+
external_id: tx.id,
|
|
14492
|
+
type: tx.type || "purchase",
|
|
14493
|
+
status: tx.status || "completed",
|
|
14494
|
+
amount: tx.amount,
|
|
14495
|
+
currency: tx.currency || "USD",
|
|
14496
|
+
merchant: tx.merchant || null,
|
|
14497
|
+
description: tx.description || null,
|
|
14498
|
+
metadata: {},
|
|
14499
|
+
created_at: tx.created_at || new Date().toISOString()
|
|
14500
|
+
}));
|
|
14501
|
+
}
|
|
13586
14502
|
}
|
|
13587
14503
|
|
|
13588
14504
|
// src/providers/registry.ts
|
|
@@ -13605,574 +14521,717 @@ function createProviderInstance(type) {
|
|
|
13605
14521
|
registerProviderFactory("agentcard", () => {
|
|
13606
14522
|
throw new Error("AgentCard requires config. Use createAgentCardProvider() instead.");
|
|
13607
14523
|
});
|
|
14524
|
+
var PROVIDER_INSTANCE_TTL_MS = 300000;
|
|
14525
|
+
var instanceCache = new Map;
|
|
14526
|
+
function makeInstanceCacheKey(type, config) {
|
|
14527
|
+
const sorted = Object.keys(config).sort().reduce((acc, k) => {
|
|
14528
|
+
acc[k] = config[k];
|
|
14529
|
+
return acc;
|
|
14530
|
+
}, {});
|
|
14531
|
+
return `${type}:${JSON.stringify(sorted)}`;
|
|
14532
|
+
}
|
|
13608
14533
|
function getProviderInstance(type, config) {
|
|
14534
|
+
const key = makeInstanceCacheKey(type, config);
|
|
14535
|
+
const cached2 = instanceCache.get(key);
|
|
14536
|
+
if (cached2 && cached2.expiresAt > Date.now()) {
|
|
14537
|
+
return cached2.instance;
|
|
14538
|
+
}
|
|
14539
|
+
let instance;
|
|
13609
14540
|
switch (type) {
|
|
13610
14541
|
case "agentcard":
|
|
13611
|
-
|
|
14542
|
+
instance = new AgentCardProvider({
|
|
14543
|
+
jwt: config.jwt,
|
|
14544
|
+
baseUrl: config.baseUrl
|
|
14545
|
+
});
|
|
14546
|
+
break;
|
|
13612
14547
|
default:
|
|
13613
|
-
|
|
13614
|
-
}
|
|
13615
|
-
}
|
|
13616
|
-
|
|
13617
|
-
// src/lib/config.ts
|
|
13618
|
-
import { homedir as homedir5 } from "os";
|
|
13619
|
-
import { join as join5 } from "path";
|
|
13620
|
-
import { existsSync as existsSync3, readFileSync, writeFileSync, mkdirSync as mkdirSync3 } from "fs";
|
|
13621
|
-
function getConfigDir() {
|
|
13622
|
-
if (process.env["WALLETS_CONFIG_DIR"])
|
|
13623
|
-
return process.env["WALLETS_CONFIG_DIR"];
|
|
13624
|
-
const home = homedir5();
|
|
13625
|
-
const newDir = join5(home, ".hasna", "wallets");
|
|
13626
|
-
const legacyDir = join5(home, ".wallets");
|
|
13627
|
-
if (!existsSync3(newDir) && existsSync3(legacyDir)) {
|
|
13628
|
-
return legacyDir;
|
|
13629
|
-
}
|
|
13630
|
-
return newDir;
|
|
13631
|
-
}
|
|
13632
|
-
function getConfigPath() {
|
|
13633
|
-
return join5(getConfigDir(), "config.json");
|
|
13634
|
-
}
|
|
13635
|
-
function loadConfig() {
|
|
13636
|
-
const configPath = getConfigPath();
|
|
13637
|
-
if (!existsSync3(configPath)) {
|
|
13638
|
-
return {};
|
|
13639
|
-
}
|
|
13640
|
-
try {
|
|
13641
|
-
return JSON.parse(readFileSync(configPath, "utf-8"));
|
|
13642
|
-
} catch {
|
|
13643
|
-
return {};
|
|
13644
|
-
}
|
|
13645
|
-
}
|
|
13646
|
-
function saveConfig(config) {
|
|
13647
|
-
const configDir = getConfigDir();
|
|
13648
|
-
if (!existsSync3(configDir)) {
|
|
13649
|
-
mkdirSync3(configDir, { recursive: true });
|
|
14548
|
+
instance = createProviderInstance(type);
|
|
13650
14549
|
}
|
|
13651
|
-
|
|
13652
|
-
|
|
13653
|
-
|
|
13654
|
-
|
|
13655
|
-
|
|
13656
|
-
|
|
13657
|
-
|
|
13658
|
-
|
|
13659
|
-
|
|
13660
|
-
|
|
13661
|
-
|
|
13662
|
-
|
|
13663
|
-
|
|
14550
|
+
instanceCache.set(key, {
|
|
14551
|
+
instance,
|
|
14552
|
+
expiresAt: Date.now() + PROVIDER_INSTANCE_TTL_MS
|
|
14553
|
+
});
|
|
14554
|
+
return instance;
|
|
14555
|
+
}
|
|
14556
|
+
|
|
14557
|
+
// src/mcp/http.ts
|
|
14558
|
+
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
|
|
14559
|
+
var DEFAULT_MCP_HTTP_PORT = 8882;
|
|
14560
|
+
var MCP_HTTP_HOST = "127.0.0.1";
|
|
14561
|
+
function isStdioMode(args) {
|
|
14562
|
+
return args.includes("--stdio") || process.env.MCP_STDIO === "1";
|
|
14563
|
+
}
|
|
14564
|
+
function resolveMcpHttpPort(args) {
|
|
14565
|
+
const portIdx = args.indexOf("--port");
|
|
14566
|
+
if (portIdx >= 0 && args[portIdx + 1])
|
|
14567
|
+
return Number(args[portIdx + 1]);
|
|
14568
|
+
const envPort = process.env.MCP_HTTP_PORT;
|
|
14569
|
+
if (envPort)
|
|
14570
|
+
return Number(envPort);
|
|
14571
|
+
return DEFAULT_MCP_HTTP_PORT;
|
|
14572
|
+
}
|
|
14573
|
+
async function handleMcpRequest(req, buildServer) {
|
|
14574
|
+
const transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: undefined });
|
|
14575
|
+
const server = await buildServer();
|
|
14576
|
+
await server.connect(transport);
|
|
14577
|
+
return transport.handleRequest(req);
|
|
14578
|
+
}
|
|
14579
|
+
function startMcpHttpServer(options) {
|
|
14580
|
+
const { name, port, buildServer } = options;
|
|
14581
|
+
const server = Bun.serve({
|
|
14582
|
+
hostname: MCP_HTTP_HOST,
|
|
14583
|
+
port,
|
|
14584
|
+
async fetch(req) {
|
|
14585
|
+
const url = new URL(req.url);
|
|
14586
|
+
if (url.pathname === "/health" && req.method === "GET")
|
|
14587
|
+
return Response.json({ status: "ok", name });
|
|
14588
|
+
if (url.pathname === "/mcp")
|
|
14589
|
+
return handleMcpRequest(req, buildServer);
|
|
14590
|
+
return new Response("Not Found", { status: 404 });
|
|
14591
|
+
}
|
|
14592
|
+
});
|
|
14593
|
+
console.error(`${name}-mcp HTTP listening on http://${MCP_HTTP_HOST}:${port}/mcp`);
|
|
14594
|
+
return server;
|
|
13664
14595
|
}
|
|
13665
14596
|
|
|
13666
|
-
// src/
|
|
13667
|
-
|
|
13668
|
-
|
|
13669
|
-
|
|
13670
|
-
|
|
13671
|
-
const configDir = getConfigDir();
|
|
13672
|
-
checks.push({
|
|
13673
|
-
name: "Config directory",
|
|
13674
|
-
status: existsSync4(configDir) ? "ok" : "warn",
|
|
13675
|
-
message: existsSync4(configDir) ? `Found at ${configDir}` : `Not found at ${configDir}. Run 'wallets provider add' to create it.`
|
|
14597
|
+
// src/mcp/index.ts
|
|
14598
|
+
function buildServer() {
|
|
14599
|
+
const server = new McpServer({
|
|
14600
|
+
name: "wallets",
|
|
14601
|
+
version: "0.1.0"
|
|
13676
14602
|
});
|
|
13677
|
-
|
|
13678
|
-
|
|
14603
|
+
function resolveId(partialId, table = "cards") {
|
|
14604
|
+
const db = getDatabase();
|
|
14605
|
+
const id = resolvePartialId(db, table, partialId);
|
|
14606
|
+
if (!id)
|
|
14607
|
+
throw new Error(`Could not resolve ID: ${partialId}`);
|
|
14608
|
+
return id;
|
|
14609
|
+
}
|
|
14610
|
+
function getDefaultProvider() {
|
|
14611
|
+
const config = loadConfig();
|
|
14612
|
+
if (!config.default_provider)
|
|
14613
|
+
return null;
|
|
14614
|
+
const record = getProviderByName(config.default_provider);
|
|
14615
|
+
if (!record)
|
|
14616
|
+
return null;
|
|
14617
|
+
return { name: config.default_provider, record };
|
|
14618
|
+
}
|
|
14619
|
+
server.tool("create_card", "Create a new funded virtual card", {
|
|
14620
|
+
amount: exports_external.number().describe("Funding amount in dollars"),
|
|
14621
|
+
name: exports_external.string().optional().describe("Card display name"),
|
|
14622
|
+
provider: exports_external.string().optional().describe("Provider name (uses default if omitted)"),
|
|
14623
|
+
currency: exports_external.string().optional().describe("Currency code (default: USD)"),
|
|
14624
|
+
agent_id: exports_external.string().optional().describe("Agent name to assign the card to"),
|
|
14625
|
+
idempotency_key: exports_external.string().optional().describe("Unique key to prevent duplicate card creation on retries")
|
|
14626
|
+
}, async (params) => {
|
|
13679
14627
|
try {
|
|
13680
|
-
|
|
13681
|
-
|
|
13682
|
-
|
|
13683
|
-
|
|
13684
|
-
|
|
13685
|
-
|
|
13686
|
-
|
|
13687
|
-
|
|
13688
|
-
|
|
13689
|
-
|
|
13690
|
-
|
|
13691
|
-
}
|
|
13692
|
-
} else {
|
|
13693
|
-
checks.push({
|
|
13694
|
-
name: "Default provider",
|
|
13695
|
-
status: "warn",
|
|
13696
|
-
message: "No default provider set. Use 'wallets provider add' to register one."
|
|
13697
|
-
});
|
|
14628
|
+
if (params.idempotency_key) {
|
|
14629
|
+
const existing = getCardByIdempotencyKey(params.idempotency_key);
|
|
14630
|
+
if (existing) {
|
|
14631
|
+
return {
|
|
14632
|
+
content: [
|
|
14633
|
+
{
|
|
14634
|
+
type: "text",
|
|
14635
|
+
text: `existing: ${formatCard(existing)}`
|
|
14636
|
+
}
|
|
14637
|
+
]
|
|
14638
|
+
};
|
|
14639
|
+
}
|
|
13698
14640
|
}
|
|
13699
|
-
|
|
13700
|
-
|
|
13701
|
-
|
|
13702
|
-
|
|
13703
|
-
|
|
13704
|
-
|
|
13705
|
-
|
|
13706
|
-
|
|
13707
|
-
|
|
13708
|
-
|
|
13709
|
-
|
|
13710
|
-
|
|
13711
|
-
|
|
13712
|
-
|
|
13713
|
-
|
|
13714
|
-
|
|
13715
|
-
|
|
13716
|
-
|
|
13717
|
-
|
|
13718
|
-
|
|
13719
|
-
|
|
13720
|
-
|
|
13721
|
-
|
|
13722
|
-
|
|
13723
|
-
|
|
13724
|
-
|
|
13725
|
-
|
|
13726
|
-
|
|
13727
|
-
|
|
13728
|
-
|
|
13729
|
-
|
|
13730
|
-
|
|
13731
|
-
|
|
13732
|
-
|
|
13733
|
-
|
|
13734
|
-
|
|
13735
|
-
|
|
14641
|
+
const providerName = params.provider || getDefaultProvider()?.name;
|
|
14642
|
+
if (!providerName) {
|
|
14643
|
+
return {
|
|
14644
|
+
content: [
|
|
14645
|
+
{
|
|
14646
|
+
type: "text",
|
|
14647
|
+
text: formatError(new Error("No provider specified and no default set. Use register_provider first."))
|
|
14648
|
+
}
|
|
14649
|
+
],
|
|
14650
|
+
isError: true
|
|
14651
|
+
};
|
|
14652
|
+
}
|
|
14653
|
+
const providerRecord = getProviderByName(providerName);
|
|
14654
|
+
if (!providerRecord) {
|
|
14655
|
+
return {
|
|
14656
|
+
content: [
|
|
14657
|
+
{
|
|
14658
|
+
type: "text",
|
|
14659
|
+
text: formatError(new Error(`Provider not found: ${providerName}`))
|
|
14660
|
+
}
|
|
14661
|
+
],
|
|
14662
|
+
isError: true
|
|
14663
|
+
};
|
|
14664
|
+
}
|
|
14665
|
+
const providerConfig = {
|
|
14666
|
+
...providerRecord.config,
|
|
14667
|
+
...getProviderConfig(providerName) || {}
|
|
14668
|
+
};
|
|
14669
|
+
const instance = getProviderInstance(providerRecord.type, providerConfig);
|
|
14670
|
+
let agentId = null;
|
|
14671
|
+
if (params.agent_id) {
|
|
14672
|
+
const agent = registerAgent({ name: params.agent_id });
|
|
14673
|
+
agentId = agent.id;
|
|
14674
|
+
}
|
|
14675
|
+
const result = await instance.createCard({
|
|
14676
|
+
amount: params.amount,
|
|
14677
|
+
name: params.name,
|
|
14678
|
+
currency: params.currency || "USD",
|
|
14679
|
+
agent_id: agentId ?? undefined
|
|
14680
|
+
});
|
|
14681
|
+
const card = createCardRecord({
|
|
14682
|
+
provider_id: providerRecord.id,
|
|
14683
|
+
external_id: result.external_id || result.id,
|
|
14684
|
+
name: result.name,
|
|
14685
|
+
last_four: result.last_four,
|
|
14686
|
+
brand: result.brand,
|
|
14687
|
+
status: result.status,
|
|
14688
|
+
currency: result.currency,
|
|
14689
|
+
balance: result.balance,
|
|
14690
|
+
funded_amount: result.funded_amount,
|
|
14691
|
+
spending_limit: result.spending_limit,
|
|
14692
|
+
agent_id: agentId,
|
|
14693
|
+
metadata: result.funding_url ? { funding_url: result.funding_url } : {},
|
|
14694
|
+
idempotency_key: params.idempotency_key ?? null
|
|
14695
|
+
});
|
|
14696
|
+
let text = `created: ${formatCard(card)}`;
|
|
14697
|
+
if (result.funding_url) {
|
|
14698
|
+
text += `
|
|
14699
|
+
Funding URL: ${result.funding_url}`;
|
|
13736
14700
|
}
|
|
14701
|
+
return { content: [{ type: "text", text }] };
|
|
14702
|
+
} catch (e) {
|
|
14703
|
+
return {
|
|
14704
|
+
content: [{ type: "text", text: formatError(e) }],
|
|
14705
|
+
isError: true
|
|
14706
|
+
};
|
|
13737
14707
|
}
|
|
13738
|
-
|
|
13739
|
-
|
|
13740
|
-
|
|
13741
|
-
|
|
13742
|
-
|
|
14708
|
+
});
|
|
14709
|
+
server.tool("list_cards", "List all virtual cards", {
|
|
14710
|
+
status: exports_external.enum(["active", "frozen", "closed", "pending"]).optional().describe("Filter by status"),
|
|
14711
|
+
provider: exports_external.string().optional().describe("Filter by provider name"),
|
|
14712
|
+
agent_id: exports_external.string().optional().describe("Filter by agent")
|
|
14713
|
+
}, async (params) => {
|
|
14714
|
+
try {
|
|
14715
|
+
let providerId;
|
|
14716
|
+
if (params.provider) {
|
|
14717
|
+
const p = getProviderByName(params.provider);
|
|
14718
|
+
if (p)
|
|
14719
|
+
providerId = p.id;
|
|
14720
|
+
}
|
|
14721
|
+
const cards = listCards({
|
|
14722
|
+
status: params.status,
|
|
14723
|
+
provider_id: providerId
|
|
13743
14724
|
});
|
|
14725
|
+
if (cards.length === 0) {
|
|
14726
|
+
return {
|
|
14727
|
+
content: [{ type: "text", text: "No cards found." }]
|
|
14728
|
+
};
|
|
14729
|
+
}
|
|
14730
|
+
const lines = cards.map(formatCard);
|
|
14731
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
14732
|
+
`) }] };
|
|
14733
|
+
} catch (e) {
|
|
14734
|
+
return {
|
|
14735
|
+
content: [{ type: "text", text: formatError(e) }],
|
|
14736
|
+
isError: true
|
|
14737
|
+
};
|
|
13744
14738
|
}
|
|
13745
|
-
}
|
|
13746
|
-
|
|
13747
|
-
|
|
13748
|
-
|
|
13749
|
-
|
|
13750
|
-
|
|
13751
|
-
|
|
13752
|
-
|
|
13753
|
-
|
|
13754
|
-
|
|
13755
|
-
|
|
13756
|
-
|
|
13757
|
-
|
|
13758
|
-
|
|
13759
|
-
|
|
13760
|
-
|
|
13761
|
-
|
|
13762
|
-
|
|
13763
|
-
|
|
13764
|
-
|
|
13765
|
-
|
|
13766
|
-
|
|
13767
|
-
|
|
13768
|
-
|
|
13769
|
-
|
|
13770
|
-
|
|
13771
|
-
|
|
13772
|
-
}
|
|
13773
|
-
|
|
13774
|
-
|
|
13775
|
-
|
|
13776
|
-
|
|
13777
|
-
|
|
13778
|
-
|
|
13779
|
-
}
|
|
13780
|
-
|
|
13781
|
-
|
|
13782
|
-
}
|
|
13783
|
-
|
|
13784
|
-
|
|
13785
|
-
const sign = tx.type === "refund" || tx.type === "load" ? "+" : "-";
|
|
13786
|
-
return `${id} ${tx.type.padEnd(10)} ${tx.status.padEnd(10)} ${sign}$${tx.amount.toFixed(2).padStart(9)} ${tx.merchant || tx.description || ""}`;
|
|
13787
|
-
}
|
|
13788
|
-
function formatDoctorCheck(check) {
|
|
13789
|
-
const icon = check.status === "ok" ? "[ok]" : check.status === "warn" ? "[!!]" : "[ERR]";
|
|
13790
|
-
return `${icon} ${check.name}: ${check.message}`;
|
|
13791
|
-
}
|
|
13792
|
-
function formatError(error) {
|
|
13793
|
-
if (error instanceof ProviderNotFoundError) {
|
|
13794
|
-
return JSON.stringify({ code: ProviderNotFoundError.code, message: error.message, suggestion: ProviderNotFoundError.suggestion });
|
|
13795
|
-
}
|
|
13796
|
-
if (error instanceof CardNotFoundError) {
|
|
13797
|
-
return JSON.stringify({ code: CardNotFoundError.code, message: error.message, suggestion: CardNotFoundError.suggestion });
|
|
13798
|
-
}
|
|
13799
|
-
if (error instanceof ProviderError) {
|
|
13800
|
-
return JSON.stringify({ code: ProviderError.code, message: error.message, suggestion: ProviderError.suggestion });
|
|
13801
|
-
}
|
|
13802
|
-
if (error instanceof InsufficientFundsError) {
|
|
13803
|
-
return JSON.stringify({ code: InsufficientFundsError.code, message: error.message, suggestion: InsufficientFundsError.suggestion });
|
|
13804
|
-
}
|
|
13805
|
-
if (error instanceof ConfigError) {
|
|
13806
|
-
return JSON.stringify({ code: ConfigError.code, message: error.message, suggestion: ConfigError.suggestion });
|
|
13807
|
-
}
|
|
13808
|
-
if (error instanceof AgentNotFoundError) {
|
|
13809
|
-
return JSON.stringify({ code: AgentNotFoundError.code, message: error.message, suggestion: AgentNotFoundError.suggestion });
|
|
13810
|
-
}
|
|
13811
|
-
if (error instanceof WalletError) {
|
|
13812
|
-
return JSON.stringify({ code: WalletError.code, message: error.message, suggestion: WalletError.suggestion });
|
|
13813
|
-
}
|
|
13814
|
-
if (error instanceof Error) {
|
|
13815
|
-
return JSON.stringify({ code: "UNKNOWN_ERROR", message: error.message });
|
|
13816
|
-
}
|
|
13817
|
-
return JSON.stringify({ code: "UNKNOWN_ERROR", message: String(error) });
|
|
13818
|
-
}
|
|
13819
|
-
|
|
13820
|
-
// src/mcp/index.ts
|
|
13821
|
-
var server = new McpServer({
|
|
13822
|
-
name: "wallets",
|
|
13823
|
-
version: "0.1.0"
|
|
13824
|
-
});
|
|
13825
|
-
function resolveId(partialId, table = "cards") {
|
|
13826
|
-
const db = getDatabase();
|
|
13827
|
-
const id = resolvePartialId(db, table, partialId);
|
|
13828
|
-
if (!id)
|
|
13829
|
-
throw new Error(`Could not resolve ID: ${partialId}`);
|
|
13830
|
-
return id;
|
|
13831
|
-
}
|
|
13832
|
-
function getDefaultProvider() {
|
|
13833
|
-
const config = loadConfig();
|
|
13834
|
-
if (!config.default_provider)
|
|
13835
|
-
return null;
|
|
13836
|
-
const record = getProviderByName(config.default_provider);
|
|
13837
|
-
if (!record)
|
|
13838
|
-
return null;
|
|
13839
|
-
return { name: config.default_provider, record };
|
|
13840
|
-
}
|
|
13841
|
-
server.tool("create_card", "Create a new funded virtual card", {
|
|
13842
|
-
amount: exports_external.number().describe("Funding amount in dollars"),
|
|
13843
|
-
name: exports_external.string().optional().describe("Card display name"),
|
|
13844
|
-
provider: exports_external.string().optional().describe("Provider name (uses default if omitted)"),
|
|
13845
|
-
currency: exports_external.string().optional().describe("Currency code (default: USD)"),
|
|
13846
|
-
agent_id: exports_external.string().optional().describe("Agent name to assign the card to")
|
|
13847
|
-
}, async (params) => {
|
|
13848
|
-
try {
|
|
13849
|
-
const providerName = params.provider || getDefaultProvider()?.name;
|
|
13850
|
-
if (!providerName) {
|
|
13851
|
-
return { content: [{ type: "text", text: formatError(new Error("No provider specified and no default set. Use register_provider first.")) }], isError: true };
|
|
13852
|
-
}
|
|
13853
|
-
const providerRecord = getProviderByName(providerName);
|
|
13854
|
-
if (!providerRecord) {
|
|
13855
|
-
return { content: [{ type: "text", text: formatError(new Error(`Provider not found: ${providerName}`)) }], isError: true };
|
|
13856
|
-
}
|
|
13857
|
-
const providerConfig = { ...providerRecord.config, ...getProviderConfig(providerName) || {} };
|
|
13858
|
-
const instance = getProviderInstance(providerRecord.type, providerConfig);
|
|
13859
|
-
let agentId = null;
|
|
13860
|
-
if (params.agent_id) {
|
|
13861
|
-
const agent = registerAgent({ name: params.agent_id });
|
|
13862
|
-
agentId = agent.id;
|
|
13863
|
-
}
|
|
13864
|
-
const result = await instance.createCard({
|
|
13865
|
-
amount: params.amount,
|
|
13866
|
-
name: params.name,
|
|
13867
|
-
currency: params.currency || "USD",
|
|
13868
|
-
agent_id: agentId ?? undefined
|
|
13869
|
-
});
|
|
13870
|
-
const card = createCardRecord({
|
|
13871
|
-
provider_id: providerRecord.id,
|
|
13872
|
-
external_id: result.external_id || result.id,
|
|
13873
|
-
name: result.name,
|
|
13874
|
-
last_four: result.last_four,
|
|
13875
|
-
brand: result.brand,
|
|
13876
|
-
status: result.status,
|
|
13877
|
-
currency: result.currency,
|
|
13878
|
-
balance: result.balance,
|
|
13879
|
-
funded_amount: result.funded_amount,
|
|
13880
|
-
spending_limit: result.spending_limit,
|
|
13881
|
-
agent_id: agentId,
|
|
13882
|
-
metadata: result.funding_url ? { funding_url: result.funding_url } : {}
|
|
13883
|
-
});
|
|
13884
|
-
let text = `created: ${formatCard(card)}`;
|
|
13885
|
-
if (result.funding_url) {
|
|
13886
|
-
text += `
|
|
13887
|
-
Funding URL: ${result.funding_url}`;
|
|
14739
|
+
});
|
|
14740
|
+
server.tool("get_card_details", "Get full card details (PAN, CVV, expiry)", {
|
|
14741
|
+
card_id: exports_external.string().describe("Card ID (supports partial matching)")
|
|
14742
|
+
}, async (params) => {
|
|
14743
|
+
try {
|
|
14744
|
+
const cardId = resolveId(params.card_id, "cards");
|
|
14745
|
+
const card = getCard(cardId);
|
|
14746
|
+
if (!card)
|
|
14747
|
+
throw new Error(`Card not found: ${params.card_id}`);
|
|
14748
|
+
const provider = getProvider(card.provider_id);
|
|
14749
|
+
if (!provider)
|
|
14750
|
+
throw new Error("Provider not found for card");
|
|
14751
|
+
const providerConfig = {
|
|
14752
|
+
...provider.config,
|
|
14753
|
+
...getProviderConfig(provider.name) || {}
|
|
14754
|
+
};
|
|
14755
|
+
const instance = getProviderInstance(provider.type, providerConfig);
|
|
14756
|
+
const details = await instance.getCardDetails(card.external_id);
|
|
14757
|
+
return {
|
|
14758
|
+
content: [
|
|
14759
|
+
{
|
|
14760
|
+
type: "text",
|
|
14761
|
+
text: [
|
|
14762
|
+
`ID: ${card.id}`,
|
|
14763
|
+
`Name: ${card.name}`,
|
|
14764
|
+
`PAN: ${details.pan}`,
|
|
14765
|
+
`CVV: ${details.cvv}`,
|
|
14766
|
+
`Exp: ${details.exp_month}/${details.exp_year}`,
|
|
14767
|
+
`Status: ${card.status}`,
|
|
14768
|
+
`Balance: $${details.balance.toFixed(2)}`
|
|
14769
|
+
].join(`
|
|
14770
|
+
`)
|
|
14771
|
+
}
|
|
14772
|
+
]
|
|
14773
|
+
};
|
|
14774
|
+
} catch (e) {
|
|
14775
|
+
return {
|
|
14776
|
+
content: [{ type: "text", text: formatError(e) }],
|
|
14777
|
+
isError: true
|
|
14778
|
+
};
|
|
13888
14779
|
}
|
|
13889
|
-
|
|
13890
|
-
|
|
13891
|
-
|
|
13892
|
-
}
|
|
13893
|
-
|
|
13894
|
-
|
|
13895
|
-
|
|
13896
|
-
|
|
13897
|
-
|
|
13898
|
-
|
|
13899
|
-
|
|
13900
|
-
|
|
13901
|
-
|
|
13902
|
-
|
|
13903
|
-
|
|
13904
|
-
|
|
14780
|
+
});
|
|
14781
|
+
server.tool("get_balance", "Check card balance", {
|
|
14782
|
+
card_id: exports_external.string().describe("Card ID")
|
|
14783
|
+
}, async (params) => {
|
|
14784
|
+
try {
|
|
14785
|
+
const cardId = resolveId(params.card_id, "cards");
|
|
14786
|
+
const card = getCard(cardId);
|
|
14787
|
+
if (!card)
|
|
14788
|
+
throw new Error(`Card not found: ${params.card_id}`);
|
|
14789
|
+
const provider = getProvider(card.provider_id);
|
|
14790
|
+
if (!provider)
|
|
14791
|
+
throw new Error("Provider not found for card");
|
|
14792
|
+
const providerConfig = {
|
|
14793
|
+
...provider.config,
|
|
14794
|
+
...getProviderConfig(provider.name) || {}
|
|
14795
|
+
};
|
|
14796
|
+
const instance = getProviderInstance(provider.type, providerConfig);
|
|
14797
|
+
const { balance, currency } = await instance.getBalance(card.external_id);
|
|
14798
|
+
updateCardBalance(cardId, balance);
|
|
14799
|
+
return {
|
|
14800
|
+
content: [
|
|
14801
|
+
{
|
|
14802
|
+
type: "text",
|
|
14803
|
+
text: `${card.name}: $${balance.toFixed(2)} ${currency}`
|
|
14804
|
+
}
|
|
14805
|
+
]
|
|
14806
|
+
};
|
|
14807
|
+
} catch (e) {
|
|
14808
|
+
return {
|
|
14809
|
+
content: [{ type: "text", text: formatError(e) }],
|
|
14810
|
+
isError: true
|
|
14811
|
+
};
|
|
13905
14812
|
}
|
|
13906
|
-
|
|
13907
|
-
|
|
13908
|
-
|
|
13909
|
-
|
|
13910
|
-
|
|
13911
|
-
|
|
14813
|
+
});
|
|
14814
|
+
server.tool("close_card", "Close a card permanently", {
|
|
14815
|
+
card_id: exports_external.string().describe("Card ID")
|
|
14816
|
+
}, async (params) => {
|
|
14817
|
+
try {
|
|
14818
|
+
const cardId = resolveId(params.card_id, "cards");
|
|
14819
|
+
const card = getCard(cardId);
|
|
14820
|
+
if (!card)
|
|
14821
|
+
throw new Error(`Card not found: ${params.card_id}`);
|
|
14822
|
+
const provider = getProvider(card.provider_id);
|
|
14823
|
+
if (!provider)
|
|
14824
|
+
throw new Error("Provider not found for card");
|
|
14825
|
+
const providerConfig = {
|
|
14826
|
+
...provider.config,
|
|
14827
|
+
...getProviderConfig(provider.name) || {}
|
|
14828
|
+
};
|
|
14829
|
+
const instance = getProviderInstance(provider.type, providerConfig);
|
|
14830
|
+
await instance.closeCard(card.external_id);
|
|
14831
|
+
updateCard(cardId, { status: "closed" });
|
|
14832
|
+
return {
|
|
14833
|
+
content: [
|
|
14834
|
+
{
|
|
14835
|
+
type: "text",
|
|
14836
|
+
text: `Card closed: ${card.id.slice(0, 8)} ${card.name}`
|
|
14837
|
+
}
|
|
14838
|
+
]
|
|
14839
|
+
};
|
|
14840
|
+
} catch (e) {
|
|
14841
|
+
return {
|
|
14842
|
+
content: [{ type: "text", text: formatError(e) }],
|
|
14843
|
+
isError: true
|
|
14844
|
+
};
|
|
14845
|
+
}
|
|
14846
|
+
});
|
|
14847
|
+
server.tool("freeze_card", "Freeze a card temporarily", {
|
|
14848
|
+
card_id: exports_external.string().describe("Card ID")
|
|
14849
|
+
}, async (params) => {
|
|
14850
|
+
try {
|
|
14851
|
+
const cardId = resolveId(params.card_id, "cards");
|
|
14852
|
+
const card = getCard(cardId);
|
|
14853
|
+
if (!card)
|
|
14854
|
+
throw new Error(`Card not found: ${params.card_id}`);
|
|
14855
|
+
if (card.status !== "active")
|
|
14856
|
+
throw new Error(`Card is not active: ${card.status}`);
|
|
14857
|
+
const provider = getProvider(card.provider_id);
|
|
14858
|
+
if (!provider)
|
|
14859
|
+
throw new Error("Provider not found for card");
|
|
14860
|
+
const providerConfig = {
|
|
14861
|
+
...provider.config,
|
|
14862
|
+
...getProviderConfig(provider.name) || {}
|
|
14863
|
+
};
|
|
14864
|
+
const instance = getProviderInstance(provider.type, providerConfig);
|
|
14865
|
+
if (instance.freezeCard) {
|
|
14866
|
+
await instance.freezeCard(card.external_id);
|
|
14867
|
+
}
|
|
14868
|
+
updateCard(cardId, { status: "frozen" });
|
|
14869
|
+
return {
|
|
14870
|
+
content: [
|
|
14871
|
+
{
|
|
14872
|
+
type: "text",
|
|
14873
|
+
text: `Card frozen: ${card.id.slice(0, 8)} ${card.name}`
|
|
14874
|
+
}
|
|
14875
|
+
]
|
|
14876
|
+
};
|
|
14877
|
+
} catch (e) {
|
|
14878
|
+
return {
|
|
14879
|
+
content: [{ type: "text", text: formatError(e) }],
|
|
14880
|
+
isError: true
|
|
14881
|
+
};
|
|
14882
|
+
}
|
|
14883
|
+
});
|
|
14884
|
+
server.tool("unfreeze_card", "Unfreeze a frozen card", {
|
|
14885
|
+
card_id: exports_external.string().describe("Card ID")
|
|
14886
|
+
}, async (params) => {
|
|
14887
|
+
try {
|
|
14888
|
+
const cardId = resolveId(params.card_id, "cards");
|
|
14889
|
+
const card = getCard(cardId);
|
|
14890
|
+
if (!card)
|
|
14891
|
+
throw new Error(`Card not found: ${params.card_id}`);
|
|
14892
|
+
if (card.status !== "frozen")
|
|
14893
|
+
throw new Error(`Card is not frozen: ${card.status}`);
|
|
14894
|
+
const provider = getProvider(card.provider_id);
|
|
14895
|
+
if (!provider)
|
|
14896
|
+
throw new Error("Provider not found for card");
|
|
14897
|
+
const providerConfig = {
|
|
14898
|
+
...provider.config,
|
|
14899
|
+
...getProviderConfig(provider.name) || {}
|
|
14900
|
+
};
|
|
14901
|
+
const instance = getProviderInstance(provider.type, providerConfig);
|
|
14902
|
+
if (instance.unfreezeCard) {
|
|
14903
|
+
await instance.unfreezeCard(card.external_id);
|
|
14904
|
+
}
|
|
14905
|
+
updateCard(cardId, { status: "active" });
|
|
14906
|
+
return {
|
|
14907
|
+
content: [
|
|
14908
|
+
{
|
|
14909
|
+
type: "text",
|
|
14910
|
+
text: `Card unfrozen: ${card.id.slice(0, 8)} ${card.name}`
|
|
14911
|
+
}
|
|
14912
|
+
]
|
|
14913
|
+
};
|
|
14914
|
+
} catch (e) {
|
|
14915
|
+
return {
|
|
14916
|
+
content: [{ type: "text", text: formatError(e) }],
|
|
14917
|
+
isError: true
|
|
14918
|
+
};
|
|
14919
|
+
}
|
|
14920
|
+
});
|
|
14921
|
+
server.tool("list_providers", "List registered wallet providers", {}, async () => {
|
|
14922
|
+
try {
|
|
14923
|
+
const providers2 = listProviders();
|
|
14924
|
+
if (providers2.length === 0) {
|
|
14925
|
+
return {
|
|
14926
|
+
content: [
|
|
14927
|
+
{
|
|
14928
|
+
type: "text",
|
|
14929
|
+
text: "No providers registered. Use register_provider to add one."
|
|
14930
|
+
}
|
|
14931
|
+
]
|
|
14932
|
+
};
|
|
14933
|
+
}
|
|
14934
|
+
const lines = providers2.map(formatProvider);
|
|
14935
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
14936
|
+
`) }] };
|
|
14937
|
+
} catch (e) {
|
|
14938
|
+
return {
|
|
14939
|
+
content: [{ type: "text", text: formatError(e) }],
|
|
14940
|
+
isError: true
|
|
14941
|
+
};
|
|
14942
|
+
}
|
|
14943
|
+
});
|
|
14944
|
+
server.tool("register_provider", "Register a wallet provider", {
|
|
14945
|
+
type: exports_external.string().describe("Provider type (e.g., agentcard)"),
|
|
14946
|
+
name: exports_external.string().optional().describe("Display name"),
|
|
14947
|
+
jwt: exports_external.string().optional().describe("JWT/API token"),
|
|
14948
|
+
api_key: exports_external.string().optional().describe("API key"),
|
|
14949
|
+
base_url: exports_external.string().optional().describe("Custom API base URL"),
|
|
14950
|
+
set_default: exports_external.boolean().optional().describe("Set as default provider")
|
|
14951
|
+
}, async (params) => {
|
|
14952
|
+
try {
|
|
14953
|
+
const name = params.name || params.type;
|
|
14954
|
+
const config = {};
|
|
14955
|
+
if (params.jwt)
|
|
14956
|
+
config.jwt = params.jwt;
|
|
14957
|
+
if (params.api_key)
|
|
14958
|
+
config.api_key = params.api_key;
|
|
14959
|
+
if (params.base_url)
|
|
14960
|
+
config.baseUrl = params.base_url;
|
|
14961
|
+
const provider = ensureProvider(name, params.type, config);
|
|
14962
|
+
setProviderConfig(name, config);
|
|
14963
|
+
if (params.set_default) {
|
|
14964
|
+
const cfg = loadConfig();
|
|
14965
|
+
cfg.default_provider = name;
|
|
14966
|
+
saveConfig(cfg);
|
|
14967
|
+
}
|
|
14968
|
+
return {
|
|
14969
|
+
content: [
|
|
14970
|
+
{
|
|
14971
|
+
type: "text",
|
|
14972
|
+
text: `registered: ${formatProvider(provider)}`
|
|
14973
|
+
}
|
|
14974
|
+
]
|
|
14975
|
+
};
|
|
14976
|
+
} catch (e) {
|
|
14977
|
+
return {
|
|
14978
|
+
content: [{ type: "text", text: formatError(e) }],
|
|
14979
|
+
isError: true
|
|
14980
|
+
};
|
|
14981
|
+
}
|
|
14982
|
+
});
|
|
14983
|
+
server.tool("list_transactions", "List card transactions", {
|
|
14984
|
+
card_id: exports_external.string().optional().describe("Card ID to filter"),
|
|
14985
|
+
limit: exports_external.number().optional().describe("Max results (default: 20)"),
|
|
14986
|
+
offset: exports_external.number().optional().describe("Offset for pagination (default: 0)")
|
|
14987
|
+
}, async (params) => {
|
|
14988
|
+
try {
|
|
14989
|
+
let cardId;
|
|
14990
|
+
if (params.card_id) {
|
|
14991
|
+
cardId = resolveId(params.card_id, "cards");
|
|
14992
|
+
}
|
|
14993
|
+
const txns = listTransactions({
|
|
14994
|
+
card_id: cardId,
|
|
14995
|
+
limit: params.limit || 20,
|
|
14996
|
+
offset: params.offset || 0
|
|
14997
|
+
});
|
|
14998
|
+
if (txns.length === 0) {
|
|
14999
|
+
return {
|
|
15000
|
+
content: [{ type: "text", text: "No transactions found." }]
|
|
15001
|
+
};
|
|
15002
|
+
}
|
|
15003
|
+
const lines = txns.map(formatTransaction);
|
|
15004
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
15005
|
+
`) }] };
|
|
15006
|
+
} catch (e) {
|
|
15007
|
+
return {
|
|
15008
|
+
content: [{ type: "text", text: formatError(e) }],
|
|
15009
|
+
isError: true
|
|
15010
|
+
};
|
|
15011
|
+
}
|
|
15012
|
+
});
|
|
15013
|
+
server.tool("doctor", "Run wallet diagnostics", {}, async () => {
|
|
15014
|
+
try {
|
|
15015
|
+
const result = runDoctor();
|
|
15016
|
+
const lines = result.checks.map(formatDoctorCheck);
|
|
15017
|
+
lines.push("");
|
|
15018
|
+
lines.push(result.healthy ? "All checks passed." : "Some checks failed.");
|
|
15019
|
+
return { content: [{ type: "text", text: lines.join(`
|
|
15020
|
+
`) }] };
|
|
15021
|
+
} catch (e) {
|
|
15022
|
+
return {
|
|
15023
|
+
content: [{ type: "text", text: formatError(e) }],
|
|
15024
|
+
isError: true
|
|
15025
|
+
};
|
|
15026
|
+
}
|
|
15027
|
+
});
|
|
15028
|
+
server.tool("describe_tools", "Get full schema for wallet tools", {
|
|
15029
|
+
name: exports_external.string().optional().describe("Tool name to describe (omit for all)")
|
|
15030
|
+
}, async (params) => {
|
|
15031
|
+
const tools = {
|
|
15032
|
+
create_card: "Create a funded virtual card. Params: amount (required, number), name (string), provider (string), currency (string, default USD), agent_id (string).",
|
|
15033
|
+
list_cards: "List cards. Params: status (active|frozen|closed|pending), provider (string), agent_id (string).",
|
|
15034
|
+
get_card_details: "Get PAN, CVV, expiry. Params: card_id (required, string).",
|
|
15035
|
+
get_balance: "Check balance. Params: card_id (required, string).",
|
|
15036
|
+
close_card: "Close permanently. Params: card_id (required, string).",
|
|
15037
|
+
list_providers: "List all providers. No params.",
|
|
15038
|
+
register_provider: "Register provider. Params: type (required), name, jwt, api_key, base_url, set_default.",
|
|
15039
|
+
list_transactions: "List transactions. Params: card_id, limit.",
|
|
15040
|
+
doctor: "Run diagnostics. No params.",
|
|
15041
|
+
register_agent: "Register agent session (idempotent). Params: name, description?",
|
|
15042
|
+
list_agents: "List all registered agents.",
|
|
15043
|
+
heartbeat: "Update last_seen_at to signal agent is active. Params: agent_id",
|
|
15044
|
+
set_focus: "Set active project context. Params: agent_id, project_id?"
|
|
15045
|
+
};
|
|
15046
|
+
if (params.name) {
|
|
15047
|
+
const desc = tools[params.name];
|
|
15048
|
+
if (!desc)
|
|
15049
|
+
return {
|
|
15050
|
+
content: [
|
|
15051
|
+
{ type: "text", text: `Unknown tool: ${params.name}` }
|
|
15052
|
+
]
|
|
15053
|
+
};
|
|
15054
|
+
return {
|
|
15055
|
+
content: [{ type: "text", text: `${params.name}: ${desc}` }]
|
|
15056
|
+
};
|
|
13912
15057
|
}
|
|
13913
|
-
const lines =
|
|
15058
|
+
const lines = Object.entries(tools).map(([k, v]) => `${k}: ${v}`);
|
|
13914
15059
|
return { content: [{ type: "text", text: lines.join(`
|
|
13915
15060
|
`) }] };
|
|
13916
|
-
}
|
|
13917
|
-
|
|
13918
|
-
|
|
13919
|
-
});
|
|
13920
|
-
server.tool("get_card_details", "Get full card details (PAN, CVV, expiry)", {
|
|
13921
|
-
card_id: exports_external.string().describe("Card ID (supports partial matching)")
|
|
13922
|
-
}, async (params) => {
|
|
13923
|
-
try {
|
|
13924
|
-
const cardId = resolveId(params.card_id, "cards");
|
|
13925
|
-
const card = getCard(cardId);
|
|
13926
|
-
if (!card)
|
|
13927
|
-
throw new Error(`Card not found: ${params.card_id}`);
|
|
13928
|
-
const provider = getProvider(card.provider_id);
|
|
13929
|
-
if (!provider)
|
|
13930
|
-
throw new Error("Provider not found for card");
|
|
13931
|
-
const providerConfig = { ...provider.config, ...getProviderConfig(provider.name) || {} };
|
|
13932
|
-
const instance = getProviderInstance(provider.type, providerConfig);
|
|
13933
|
-
const details = await instance.getCardDetails(card.external_id);
|
|
15061
|
+
});
|
|
15062
|
+
server.resource("wallets://cards", "wallets://cards", async () => {
|
|
15063
|
+
const cards = listCards();
|
|
13934
15064
|
return {
|
|
13935
|
-
|
|
13936
|
-
|
|
13937
|
-
|
|
13938
|
-
|
|
13939
|
-
|
|
13940
|
-
|
|
13941
|
-
|
|
13942
|
-
`Exp: ${details.exp_month}/${details.exp_year}`,
|
|
13943
|
-
`Status: ${card.status}`,
|
|
13944
|
-
`Balance: $${details.balance.toFixed(2)}`
|
|
13945
|
-
].join(`
|
|
13946
|
-
`)
|
|
13947
|
-
}]
|
|
15065
|
+
contents: [
|
|
15066
|
+
{
|
|
15067
|
+
uri: "wallets://cards",
|
|
15068
|
+
text: JSON.stringify(cards, null, 2),
|
|
15069
|
+
mimeType: "application/json"
|
|
15070
|
+
}
|
|
15071
|
+
]
|
|
13948
15072
|
};
|
|
13949
|
-
}
|
|
13950
|
-
|
|
13951
|
-
|
|
13952
|
-
});
|
|
13953
|
-
server.tool("get_balance", "Check card balance", {
|
|
13954
|
-
card_id: exports_external.string().describe("Card ID")
|
|
13955
|
-
}, async (params) => {
|
|
13956
|
-
try {
|
|
13957
|
-
const cardId = resolveId(params.card_id, "cards");
|
|
13958
|
-
const card = getCard(cardId);
|
|
13959
|
-
if (!card)
|
|
13960
|
-
throw new Error(`Card not found: ${params.card_id}`);
|
|
13961
|
-
const provider = getProvider(card.provider_id);
|
|
13962
|
-
if (!provider)
|
|
13963
|
-
throw new Error("Provider not found for card");
|
|
13964
|
-
const providerConfig = { ...provider.config, ...getProviderConfig(provider.name) || {} };
|
|
13965
|
-
const instance = getProviderInstance(provider.type, providerConfig);
|
|
13966
|
-
const { balance, currency } = await instance.getBalance(card.external_id);
|
|
13967
|
-
updateCardBalance(cardId, balance);
|
|
15073
|
+
});
|
|
15074
|
+
server.resource("wallets://providers", "wallets://providers", async () => {
|
|
15075
|
+
const providers2 = listProviders();
|
|
13968
15076
|
return {
|
|
13969
|
-
|
|
13970
|
-
|
|
13971
|
-
|
|
13972
|
-
|
|
15077
|
+
contents: [
|
|
15078
|
+
{
|
|
15079
|
+
uri: "wallets://providers",
|
|
15080
|
+
text: JSON.stringify(providers2, null, 2),
|
|
15081
|
+
mimeType: "application/json"
|
|
15082
|
+
}
|
|
15083
|
+
]
|
|
13973
15084
|
};
|
|
13974
|
-
}
|
|
13975
|
-
|
|
13976
|
-
|
|
13977
|
-
|
|
13978
|
-
|
|
13979
|
-
|
|
13980
|
-
|
|
13981
|
-
|
|
13982
|
-
|
|
13983
|
-
|
|
13984
|
-
|
|
13985
|
-
|
|
13986
|
-
|
|
13987
|
-
|
|
13988
|
-
|
|
13989
|
-
|
|
13990
|
-
|
|
13991
|
-
|
|
13992
|
-
|
|
13993
|
-
|
|
13994
|
-
|
|
13995
|
-
|
|
13996
|
-
|
|
13997
|
-
|
|
13998
|
-
|
|
13999
|
-
|
|
14000
|
-
|
|
14001
|
-
|
|
14002
|
-
return {
|
|
15085
|
+
});
|
|
15086
|
+
server.resource("wallets://agents", "wallets://agents", async () => {
|
|
15087
|
+
const agents = listAgents();
|
|
15088
|
+
return {
|
|
15089
|
+
contents: [
|
|
15090
|
+
{
|
|
15091
|
+
uri: "wallets://agents",
|
|
15092
|
+
text: JSON.stringify(agents, null, 2),
|
|
15093
|
+
mimeType: "application/json"
|
|
15094
|
+
}
|
|
15095
|
+
]
|
|
15096
|
+
};
|
|
15097
|
+
});
|
|
15098
|
+
server.tool("register_agent", "Register an agent session (idempotent). Auto-updates last_seen_at on re-register.", {
|
|
15099
|
+
name: exports_external.string().describe("Agent name"),
|
|
15100
|
+
description: exports_external.string().optional().describe("Agent description")
|
|
15101
|
+
}, async (params) => {
|
|
15102
|
+
try {
|
|
15103
|
+
const agent = registerAgent({
|
|
15104
|
+
name: params.name,
|
|
15105
|
+
description: params.description
|
|
15106
|
+
});
|
|
15107
|
+
return {
|
|
15108
|
+
content: [
|
|
15109
|
+
{ type: "text", text: JSON.stringify(agent, null, 2) }
|
|
15110
|
+
]
|
|
15111
|
+
};
|
|
15112
|
+
} catch (e) {
|
|
15113
|
+
return {
|
|
15114
|
+
content: [{ type: "text", text: formatError(e) }],
|
|
15115
|
+
isError: true
|
|
15116
|
+
};
|
|
14003
15117
|
}
|
|
14004
|
-
|
|
14005
|
-
|
|
14006
|
-
|
|
14007
|
-
|
|
14008
|
-
|
|
14009
|
-
|
|
14010
|
-
}
|
|
14011
|
-
|
|
14012
|
-
|
|
14013
|
-
|
|
14014
|
-
|
|
14015
|
-
|
|
14016
|
-
|
|
14017
|
-
|
|
14018
|
-
}, async (params) => {
|
|
14019
|
-
try {
|
|
14020
|
-
const name = params.name || params.type;
|
|
14021
|
-
const config = {};
|
|
14022
|
-
if (params.jwt)
|
|
14023
|
-
config["jwt"] = params.jwt;
|
|
14024
|
-
if (params.api_key)
|
|
14025
|
-
config["api_key"] = params.api_key;
|
|
14026
|
-
if (params.base_url)
|
|
14027
|
-
config["baseUrl"] = params.base_url;
|
|
14028
|
-
const provider = ensureProvider(name, params.type, config);
|
|
14029
|
-
setProviderConfig(name, config);
|
|
14030
|
-
if (params.set_default) {
|
|
14031
|
-
const cfg = loadConfig();
|
|
14032
|
-
cfg.default_provider = name;
|
|
14033
|
-
saveConfig(cfg);
|
|
14034
|
-
}
|
|
14035
|
-
return { content: [{ type: "text", text: `registered: ${formatProvider(provider)}` }] };
|
|
14036
|
-
} catch (e) {
|
|
14037
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
14038
|
-
}
|
|
14039
|
-
});
|
|
14040
|
-
server.tool("list_transactions", "List card transactions", {
|
|
14041
|
-
card_id: exports_external.string().optional().describe("Card ID to filter"),
|
|
14042
|
-
limit: exports_external.number().optional().describe("Max results (default: 20)")
|
|
14043
|
-
}, async (params) => {
|
|
14044
|
-
try {
|
|
14045
|
-
let cardId;
|
|
14046
|
-
if (params.card_id) {
|
|
14047
|
-
cardId = resolveId(params.card_id, "cards");
|
|
15118
|
+
});
|
|
15119
|
+
server.tool("list_agents", "List all registered agents.", {}, async () => {
|
|
15120
|
+
try {
|
|
15121
|
+
const agents = listAgents();
|
|
15122
|
+
return {
|
|
15123
|
+
content: [
|
|
15124
|
+
{ type: "text", text: JSON.stringify(agents, null, 2) }
|
|
15125
|
+
]
|
|
15126
|
+
};
|
|
15127
|
+
} catch (e) {
|
|
15128
|
+
return {
|
|
15129
|
+
content: [{ type: "text", text: formatError(e) }],
|
|
15130
|
+
isError: true
|
|
15131
|
+
};
|
|
14048
15132
|
}
|
|
14049
|
-
|
|
14050
|
-
|
|
14051
|
-
|
|
14052
|
-
|
|
14053
|
-
|
|
14054
|
-
|
|
15133
|
+
});
|
|
15134
|
+
server.tool("heartbeat", "Update last_seen_at to signal agent is active. Call periodically during long tasks.", { agent_id: exports_external.string().describe("Agent ID or name") }, async (params) => {
|
|
15135
|
+
try {
|
|
15136
|
+
const agent = heartbeatAgent(params.agent_id);
|
|
15137
|
+
if (!agent)
|
|
15138
|
+
return {
|
|
15139
|
+
content: [
|
|
15140
|
+
{
|
|
15141
|
+
type: "text",
|
|
15142
|
+
text: `Agent not found: ${params.agent_id}`
|
|
15143
|
+
}
|
|
15144
|
+
],
|
|
15145
|
+
isError: true
|
|
15146
|
+
};
|
|
15147
|
+
return {
|
|
15148
|
+
content: [
|
|
15149
|
+
{ type: "text", text: JSON.stringify(agent, null, 2) }
|
|
15150
|
+
]
|
|
15151
|
+
};
|
|
15152
|
+
} catch (e) {
|
|
15153
|
+
return {
|
|
15154
|
+
content: [{ type: "text", text: formatError(e) }],
|
|
15155
|
+
isError: true
|
|
15156
|
+
};
|
|
14055
15157
|
}
|
|
14056
|
-
|
|
14057
|
-
|
|
14058
|
-
|
|
14059
|
-
|
|
14060
|
-
|
|
14061
|
-
|
|
14062
|
-
|
|
14063
|
-
|
|
14064
|
-
|
|
14065
|
-
|
|
14066
|
-
|
|
14067
|
-
|
|
14068
|
-
|
|
14069
|
-
|
|
14070
|
-
|
|
14071
|
-
|
|
14072
|
-
|
|
14073
|
-
|
|
14074
|
-
|
|
14075
|
-
|
|
14076
|
-
|
|
14077
|
-
|
|
14078
|
-
|
|
14079
|
-
|
|
14080
|
-
|
|
14081
|
-
|
|
14082
|
-
|
|
14083
|
-
|
|
14084
|
-
|
|
14085
|
-
|
|
14086
|
-
|
|
14087
|
-
|
|
14088
|
-
|
|
14089
|
-
|
|
14090
|
-
|
|
14091
|
-
|
|
14092
|
-
}
|
|
14093
|
-
|
|
14094
|
-
|
|
14095
|
-
|
|
14096
|
-
|
|
14097
|
-
|
|
14098
|
-
|
|
14099
|
-
|
|
14100
|
-
|
|
14101
|
-
|
|
14102
|
-
|
|
14103
|
-
|
|
14104
|
-
|
|
14105
|
-
|
|
14106
|
-
}
|
|
14107
|
-
|
|
14108
|
-
|
|
14109
|
-
|
|
14110
|
-
|
|
14111
|
-
|
|
14112
|
-
|
|
14113
|
-
|
|
14114
|
-
|
|
14115
|
-
|
|
14116
|
-
name: exports_external.string().describe("Agent name"),
|
|
14117
|
-
description: exports_external.string().optional().describe("Agent description")
|
|
14118
|
-
}, async (params) => {
|
|
14119
|
-
try {
|
|
14120
|
-
const agent = registerAgent({ name: params.name, description: params.description });
|
|
14121
|
-
return { content: [{ type: "text", text: JSON.stringify(agent, null, 2) }] };
|
|
14122
|
-
} catch (e) {
|
|
14123
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
14124
|
-
}
|
|
14125
|
-
});
|
|
14126
|
-
server.tool("list_agents", "List all registered agents.", {}, async () => {
|
|
14127
|
-
try {
|
|
14128
|
-
const agents = listAgents();
|
|
14129
|
-
return { content: [{ type: "text", text: JSON.stringify(agents, null, 2) }] };
|
|
14130
|
-
} catch (e) {
|
|
14131
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
14132
|
-
}
|
|
14133
|
-
});
|
|
14134
|
-
server.tool("heartbeat", "Update last_seen_at to signal agent is active. Call periodically during long tasks.", { agent_id: exports_external.string().describe("Agent ID or name") }, async (params) => {
|
|
14135
|
-
try {
|
|
14136
|
-
const agent = heartbeatAgent(params.agent_id);
|
|
14137
|
-
if (!agent)
|
|
14138
|
-
return { content: [{ type: "text", text: `Agent not found: ${params.agent_id}` }], isError: true };
|
|
14139
|
-
return { content: [{ type: "text", text: JSON.stringify(agent, null, 2) }] };
|
|
14140
|
-
} catch (e) {
|
|
14141
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
14142
|
-
}
|
|
14143
|
-
});
|
|
14144
|
-
server.tool("set_focus", "Set active project context for this agent session.", {
|
|
14145
|
-
agent_id: exports_external.string().describe("Agent ID or name"),
|
|
14146
|
-
project_id: exports_external.string().nullable().optional().describe("Project ID to focus on, or null to clear")
|
|
14147
|
-
}, async (params) => {
|
|
14148
|
-
try {
|
|
14149
|
-
const agent = setAgentFocus(params.agent_id, params.project_id ?? null);
|
|
14150
|
-
if (!agent)
|
|
14151
|
-
return { content: [{ type: "text", text: `Agent not found: ${params.agent_id}` }], isError: true };
|
|
14152
|
-
return { content: [{ type: "text", text: params.project_id ? `Focus: ${params.project_id}` : "Focus cleared" }] };
|
|
14153
|
-
} catch (e) {
|
|
14154
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
14155
|
-
}
|
|
14156
|
-
});
|
|
14157
|
-
server.tool("send_feedback", "Send feedback about this service", {
|
|
14158
|
-
message: exports_external.string().describe("Feedback message"),
|
|
14159
|
-
email: exports_external.string().optional().describe("Contact email (optional)"),
|
|
14160
|
-
category: exports_external.enum(["bug", "feature", "general"]).optional().describe("Feedback category")
|
|
14161
|
-
}, async (params) => {
|
|
14162
|
-
try {
|
|
14163
|
-
const db = getDatabase();
|
|
14164
|
-
const pkg = require_package();
|
|
14165
|
-
db.run("INSERT INTO feedback (message, email, category, version) VALUES (?, ?, ?, ?)", [params.message, params.email || null, params.category || "general", pkg.version]);
|
|
14166
|
-
return { content: [{ type: "text", text: "Feedback saved. Thank you!" }] };
|
|
14167
|
-
} catch (e) {
|
|
14168
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
14169
|
-
}
|
|
14170
|
-
});
|
|
15158
|
+
});
|
|
15159
|
+
server.tool("set_focus", "Set active project context for this agent session.", {
|
|
15160
|
+
agent_id: exports_external.string().describe("Agent ID or name"),
|
|
15161
|
+
project_id: exports_external.string().nullable().optional().describe("Project ID to focus on, or null to clear")
|
|
15162
|
+
}, async (params) => {
|
|
15163
|
+
try {
|
|
15164
|
+
const agent = setAgentFocus(params.agent_id, params.project_id ?? null);
|
|
15165
|
+
if (!agent)
|
|
15166
|
+
return {
|
|
15167
|
+
content: [
|
|
15168
|
+
{
|
|
15169
|
+
type: "text",
|
|
15170
|
+
text: `Agent not found: ${params.agent_id}`
|
|
15171
|
+
}
|
|
15172
|
+
],
|
|
15173
|
+
isError: true
|
|
15174
|
+
};
|
|
15175
|
+
return {
|
|
15176
|
+
content: [
|
|
15177
|
+
{
|
|
15178
|
+
type: "text",
|
|
15179
|
+
text: params.project_id ? `Focus: ${params.project_id}` : "Focus cleared"
|
|
15180
|
+
}
|
|
15181
|
+
]
|
|
15182
|
+
};
|
|
15183
|
+
} catch (e) {
|
|
15184
|
+
return {
|
|
15185
|
+
content: [{ type: "text", text: formatError(e) }],
|
|
15186
|
+
isError: true
|
|
15187
|
+
};
|
|
15188
|
+
}
|
|
15189
|
+
});
|
|
15190
|
+
server.tool("send_feedback", "Send feedback about this service", {
|
|
15191
|
+
message: exports_external.string().describe("Feedback message"),
|
|
15192
|
+
email: exports_external.string().optional().describe("Contact email (optional)"),
|
|
15193
|
+
category: exports_external.enum(["bug", "feature", "general"]).optional().describe("Feedback category")
|
|
15194
|
+
}, async (params) => {
|
|
15195
|
+
try {
|
|
15196
|
+
const db = getDatabase();
|
|
15197
|
+
const pkg = require_package();
|
|
15198
|
+
db.run("INSERT INTO feedback (message, email, category, version) VALUES (?, ?, ?, ?)", [
|
|
15199
|
+
params.message,
|
|
15200
|
+
params.email || null,
|
|
15201
|
+
params.category || "general",
|
|
15202
|
+
pkg.version
|
|
15203
|
+
]);
|
|
15204
|
+
return {
|
|
15205
|
+
content: [
|
|
15206
|
+
{ type: "text", text: "Feedback saved. Thank you!" }
|
|
15207
|
+
]
|
|
15208
|
+
};
|
|
15209
|
+
} catch (e) {
|
|
15210
|
+
return {
|
|
15211
|
+
content: [{ type: "text", text: formatError(e) }],
|
|
15212
|
+
isError: true
|
|
15213
|
+
};
|
|
15214
|
+
}
|
|
15215
|
+
});
|
|
15216
|
+
return server;
|
|
15217
|
+
}
|
|
14171
15218
|
async function main() {
|
|
14172
|
-
const
|
|
14173
|
-
|
|
15219
|
+
const args = process.argv.slice(2);
|
|
15220
|
+
if (isStdioMode(args)) {
|
|
15221
|
+
const transport = new StdioServerTransport;
|
|
15222
|
+
await buildServer().connect(transport);
|
|
15223
|
+
return;
|
|
15224
|
+
}
|
|
15225
|
+
startMcpHttpServer({
|
|
15226
|
+
name: "wallets",
|
|
15227
|
+
port: resolveMcpHttpPort(args),
|
|
15228
|
+
buildServer
|
|
15229
|
+
});
|
|
14174
15230
|
}
|
|
14175
15231
|
main().catch((e) => {
|
|
14176
15232
|
console.error("Fatal:", e);
|
|
14177
15233
|
process.exit(1);
|
|
14178
15234
|
});
|
|
15235
|
+
export {
|
|
15236
|
+
buildServer
|
|
15237
|
+
};
|