@hasna/wallets 0.1.7 → 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 +1753 -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 +83 -73
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,18 +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
46
|
"dev:mcp": "bun run src/mcp/index.ts",
|
|
49
|
-
|
|
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"
|
|
50
53
|
},
|
|
51
54
|
keywords: [
|
|
52
55
|
"wallets",
|
|
@@ -85,10 +88,13 @@ var require_package = __commonJS((exports, module) => {
|
|
|
85
88
|
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
86
89
|
chalk: "^5.4.1",
|
|
87
90
|
commander: "^13.1.0",
|
|
91
|
+
jmespath: "^0.16.0",
|
|
88
92
|
zod: "^3.24.2"
|
|
89
93
|
},
|
|
90
94
|
devDependencies: {
|
|
95
|
+
"@biomejs/biome": "^1.9.4",
|
|
91
96
|
"@types/bun": "^1.2.4",
|
|
97
|
+
lefthook: "^2.1.4",
|
|
92
98
|
typescript: "^5.7.3"
|
|
93
99
|
}
|
|
94
100
|
};
|
|
@@ -4071,6 +4077,33 @@ var coerce = {
|
|
|
4071
4077
|
date: (arg) => ZodDate.create({ ...arg, coerce: true })
|
|
4072
4078
|
};
|
|
4073
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
|
+
|
|
4074
4107
|
// node_modules/@hasna/cloud/dist/index.js
|
|
4075
4108
|
import { createRequire } from "module";
|
|
4076
4109
|
import { Database } from "bun:sqlite";
|
|
@@ -4097,10 +4130,10 @@ var __toESMCache_esm;
|
|
|
4097
4130
|
var __toESM = (mod, isNodeMode, target) => {
|
|
4098
4131
|
var canCache = mod != null && typeof mod === "object";
|
|
4099
4132
|
if (canCache) {
|
|
4100
|
-
var
|
|
4101
|
-
var
|
|
4102
|
-
if (
|
|
4103
|
-
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;
|
|
4104
4137
|
}
|
|
4105
4138
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
4106
4139
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp2(target, "default", { value: mod, enumerable: true }) : target;
|
|
@@ -4111,7 +4144,7 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
|
4111
4144
|
enumerable: true
|
|
4112
4145
|
});
|
|
4113
4146
|
if (canCache)
|
|
4114
|
-
|
|
4147
|
+
cache2.set(mod, to);
|
|
4115
4148
|
return to;
|
|
4116
4149
|
};
|
|
4117
4150
|
var __commonJS2 = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
@@ -13007,9 +13040,6 @@ function ensureFeedbackTable(db) {
|
|
|
13007
13040
|
}
|
|
13008
13041
|
|
|
13009
13042
|
// src/db/database.ts
|
|
13010
|
-
import { homedir as homedir3 } from "os";
|
|
13011
|
-
import { join as join3 } from "path";
|
|
13012
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
13013
13043
|
var _db = null;
|
|
13014
13044
|
var _adapter = null;
|
|
13015
13045
|
var MIGRATIONS = [
|
|
@@ -13084,7 +13114,30 @@ var MIGRATIONS = [
|
|
|
13084
13114
|
},
|
|
13085
13115
|
{
|
|
13086
13116
|
id: 2,
|
|
13087
|
-
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
|
+
`
|
|
13088
13141
|
}
|
|
13089
13142
|
];
|
|
13090
13143
|
function runMigrations(db) {
|
|
@@ -13108,10 +13161,10 @@ function runMigrations(db) {
|
|
|
13108
13161
|
}
|
|
13109
13162
|
}
|
|
13110
13163
|
function resolveDbPath() {
|
|
13111
|
-
if (process.env
|
|
13112
|
-
return process.env
|
|
13113
|
-
if (process.env
|
|
13114
|
-
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;
|
|
13115
13168
|
const home = homedir3();
|
|
13116
13169
|
migrateDotfile("wallets");
|
|
13117
13170
|
const newDir = join3(home, ".hasna", "wallets");
|
|
@@ -13148,84 +13201,66 @@ function resolvePartialId(db, table, partialId) {
|
|
|
13148
13201
|
return null;
|
|
13149
13202
|
}
|
|
13150
13203
|
|
|
13151
|
-
// src/db/
|
|
13152
|
-
|
|
13153
|
-
|
|
13154
|
-
|
|
13155
|
-
status: row.status,
|
|
13156
|
-
config: JSON.parse(row.config || "{}"),
|
|
13157
|
-
metadata: JSON.parse(row.metadata || "{}")
|
|
13158
|
-
};
|
|
13204
|
+
// src/db/agents.ts
|
|
13205
|
+
var AGENT_LIST_TTL = 30000;
|
|
13206
|
+
function rowToAgent(row) {
|
|
13207
|
+
return { ...row };
|
|
13159
13208
|
}
|
|
13160
|
-
function
|
|
13209
|
+
function registerAgent(input, db) {
|
|
13161
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
|
+
}
|
|
13162
13221
|
const id = shortId();
|
|
13163
|
-
d.run(
|
|
13222
|
+
d.run("INSERT INTO agents (id, name, description) VALUES (?, ?, ?)", [
|
|
13164
13223
|
id,
|
|
13165
|
-
|
|
13166
|
-
input.
|
|
13167
|
-
JSON.stringify(input.config || {}),
|
|
13168
|
-
JSON.stringify(input.metadata || {})
|
|
13224
|
+
normalizedName,
|
|
13225
|
+
input.description ?? null
|
|
13169
13226
|
]);
|
|
13170
|
-
|
|
13227
|
+
cacheClear("agents:");
|
|
13228
|
+
return getAgent(id, d);
|
|
13171
13229
|
}
|
|
13172
|
-
function
|
|
13230
|
+
function getAgent(id, db) {
|
|
13173
13231
|
const d = db || getDatabase();
|
|
13174
|
-
const row = d.query("SELECT * FROM
|
|
13175
|
-
return row ?
|
|
13232
|
+
const row = d.query("SELECT * FROM agents WHERE id = ?").get(id);
|
|
13233
|
+
return row ? rowToAgent(row) : null;
|
|
13176
13234
|
}
|
|
13177
|
-
function
|
|
13235
|
+
function getAgentByName(name, db) {
|
|
13178
13236
|
const d = db || getDatabase();
|
|
13179
|
-
const
|
|
13180
|
-
|
|
13237
|
+
const normalizedName = name.trim().toLowerCase();
|
|
13238
|
+
const row = d.query("SELECT * FROM agents WHERE name = ?").get(normalizedName);
|
|
13239
|
+
return row ? rowToAgent(row) : null;
|
|
13181
13240
|
}
|
|
13182
|
-
function
|
|
13183
|
-
|
|
13184
|
-
|
|
13185
|
-
|
|
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
|
+
});
|
|
13186
13247
|
}
|
|
13187
|
-
function
|
|
13248
|
+
function heartbeatAgent(idOrName, db) {
|
|
13188
13249
|
const d = db || getDatabase();
|
|
13189
|
-
const
|
|
13190
|
-
if (!
|
|
13191
|
-
|
|
13192
|
-
|
|
13193
|
-
|
|
13194
|
-
if (input.name !== undefined) {
|
|
13195
|
-
sets.push("name = ?");
|
|
13196
|
-
params.push(input.name);
|
|
13197
|
-
}
|
|
13198
|
-
if (input.config !== undefined) {
|
|
13199
|
-
sets.push("config = ?");
|
|
13200
|
-
params.push(JSON.stringify(input.config));
|
|
13201
|
-
}
|
|
13202
|
-
if (input.metadata !== undefined) {
|
|
13203
|
-
sets.push("metadata = ?");
|
|
13204
|
-
params.push(JSON.stringify(input.metadata));
|
|
13205
|
-
}
|
|
13206
|
-
if (input.status !== undefined) {
|
|
13207
|
-
sets.push("status = ?");
|
|
13208
|
-
params.push(input.status);
|
|
13209
|
-
}
|
|
13210
|
-
if (sets.length > 0) {
|
|
13211
|
-
sets.push("updated_at = ?");
|
|
13212
|
-
params.push(now());
|
|
13213
|
-
params.push(id);
|
|
13214
|
-
d.run(`UPDATE providers SET ${sets.join(", ")} WHERE id = ?`, params);
|
|
13215
|
-
}
|
|
13216
|
-
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);
|
|
13217
13255
|
}
|
|
13218
|
-
function
|
|
13256
|
+
function setAgentFocus(idOrName, projectId, db) {
|
|
13219
13257
|
const d = db || getDatabase();
|
|
13220
|
-
const
|
|
13221
|
-
if (
|
|
13222
|
-
|
|
13223
|
-
|
|
13224
|
-
|
|
13225
|
-
|
|
13226
|
-
return getProvider(existing.id, d);
|
|
13227
|
-
}
|
|
13228
|
-
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);
|
|
13229
13264
|
}
|
|
13230
13265
|
|
|
13231
13266
|
// src/db/cards.ts
|
|
@@ -13240,8 +13275,8 @@ function rowToCard(row) {
|
|
|
13240
13275
|
function createCardRecord(input, db) {
|
|
13241
13276
|
const d = db || getDatabase();
|
|
13242
13277
|
const id = uuid();
|
|
13243
|
-
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)
|
|
13244
|
-
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
13245
13280
|
id,
|
|
13246
13281
|
input.provider_id,
|
|
13247
13282
|
input.external_id,
|
|
@@ -13255,7 +13290,8 @@ function createCardRecord(input, db) {
|
|
|
13255
13290
|
input.spending_limit ?? null,
|
|
13256
13291
|
input.agent_id ?? null,
|
|
13257
13292
|
JSON.stringify(input.metadata || {}),
|
|
13258
|
-
input.expires_at ?? null
|
|
13293
|
+
input.expires_at ?? null,
|
|
13294
|
+
input.idempotency_key ?? null
|
|
13259
13295
|
]);
|
|
13260
13296
|
return getCard(id, d);
|
|
13261
13297
|
}
|
|
@@ -13264,6 +13300,11 @@ function getCard(id, db) {
|
|
|
13264
13300
|
const row = d.query("SELECT * FROM cards WHERE id = ?").get(id);
|
|
13265
13301
|
return row ? rowToCard(row) : null;
|
|
13266
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
|
+
}
|
|
13267
13308
|
function listCards(filter = {}, db) {
|
|
13268
13309
|
const d = db || getDatabase();
|
|
13269
13310
|
const conditions = [];
|
|
@@ -13323,10 +13364,102 @@ function updateCard(id, input, db) {
|
|
|
13323
13364
|
}
|
|
13324
13365
|
function updateCardBalance(id, balance, db) {
|
|
13325
13366
|
const d = db || getDatabase();
|
|
13326
|
-
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
|
+
]);
|
|
13327
13372
|
return getCard(id, d);
|
|
13328
13373
|
}
|
|
13329
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
|
+
|
|
13330
13463
|
// src/db/transactions.ts
|
|
13331
13464
|
function rowToTransaction(row) {
|
|
13332
13465
|
return {
|
|
@@ -13364,53 +13497,688 @@ function listTransactions(filter = {}, db) {
|
|
|
13364
13497
|
return rows.map(rowToTransaction);
|
|
13365
13498
|
}
|
|
13366
13499
|
|
|
13367
|
-
// src/
|
|
13368
|
-
|
|
13369
|
-
|
|
13370
|
-
}
|
|
13371
|
-
|
|
13372
|
-
|
|
13373
|
-
|
|
13374
|
-
|
|
13375
|
-
|
|
13376
|
-
|
|
13377
|
-
|
|
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);
|
|
13378
13708
|
}
|
|
13379
|
-
const id = shortId();
|
|
13380
|
-
d.run("INSERT INTO agents (id, name, description) VALUES (?, ?, ?)", [id, normalizedName, input.description ?? null]);
|
|
13381
|
-
return getAgent(id, d);
|
|
13382
|
-
}
|
|
13383
|
-
function getAgent(id, db) {
|
|
13384
|
-
const d = db || getDatabase();
|
|
13385
|
-
const row = d.query("SELECT * FROM agents WHERE id = ?").get(id);
|
|
13386
|
-
return row ? rowToAgent(row) : null;
|
|
13387
13709
|
}
|
|
13388
|
-
function
|
|
13389
|
-
|
|
13390
|
-
|
|
13391
|
-
|
|
13392
|
-
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
|
+
};
|
|
13393
13720
|
}
|
|
13394
|
-
function
|
|
13395
|
-
const
|
|
13396
|
-
|
|
13397
|
-
|
|
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;
|
|
13398
13800
|
}
|
|
13399
|
-
function
|
|
13400
|
-
const
|
|
13401
|
-
|
|
13402
|
-
|
|
13403
|
-
|
|
13404
|
-
|
|
13405
|
-
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);
|
|
13406
13807
|
}
|
|
13407
|
-
|
|
13408
|
-
|
|
13409
|
-
|
|
13410
|
-
|
|
13411
|
-
|
|
13412
|
-
|
|
13413
|
-
|
|
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
|
+
};
|
|
13414
14182
|
}
|
|
13415
14183
|
|
|
13416
14184
|
// src/types/index.ts
|
|
@@ -13477,29 +14245,140 @@ class AgentNotFoundError extends Error {
|
|
|
13477
14245
|
}
|
|
13478
14246
|
}
|
|
13479
14247
|
|
|
13480
|
-
// src/
|
|
13481
|
-
|
|
13482
|
-
|
|
13483
|
-
|
|
13484
|
-
|
|
13485
|
-
|
|
13486
|
-
|
|
13487
|
-
|
|
13488
|
-
|
|
13489
|
-
|
|
13490
|
-
|
|
13491
|
-
|
|
13492
|
-
|
|
13493
|
-
|
|
13494
|
-
|
|
13495
|
-
|
|
13496
|
-
|
|
13497
|
-
|
|
13498
|
-
|
|
13499
|
-
|
|
13500
|
-
|
|
13501
|
-
|
|
13502
|
-
|
|
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}`);
|
|
13503
14382
|
}
|
|
13504
14383
|
return res.json();
|
|
13505
14384
|
}
|
|
@@ -13525,7 +14404,8 @@ class AgentCardProvider {
|
|
|
13525
14404
|
expires_at: null,
|
|
13526
14405
|
created_at: new Date().toISOString(),
|
|
13527
14406
|
updated_at: new Date().toISOString(),
|
|
13528
|
-
funding_url: data.funding_url
|
|
14407
|
+
funding_url: data.funding_url,
|
|
14408
|
+
idempotency_key: null
|
|
13529
14409
|
};
|
|
13530
14410
|
}
|
|
13531
14411
|
async listCards() {
|
|
@@ -13546,7 +14426,8 @@ class AgentCardProvider {
|
|
|
13546
14426
|
metadata: {},
|
|
13547
14427
|
expires_at: null,
|
|
13548
14428
|
created_at: c.created_at || new Date().toISOString(),
|
|
13549
|
-
updated_at: new Date().toISOString()
|
|
14429
|
+
updated_at: new Date().toISOString(),
|
|
14430
|
+
idempotency_key: null
|
|
13550
14431
|
}));
|
|
13551
14432
|
}
|
|
13552
14433
|
async getCardDetails(externalId) {
|
|
@@ -13571,7 +14452,8 @@ class AgentCardProvider {
|
|
|
13571
14452
|
pan: data.pan,
|
|
13572
14453
|
cvv: data.cvv,
|
|
13573
14454
|
exp_month: data.exp_month,
|
|
13574
|
-
exp_year: data.exp_year
|
|
14455
|
+
exp_year: data.exp_year,
|
|
14456
|
+
idempotency_key: null
|
|
13575
14457
|
};
|
|
13576
14458
|
}
|
|
13577
14459
|
async getBalance(externalId) {
|
|
@@ -13584,6 +14466,39 @@ class AgentCardProvider {
|
|
|
13584
14466
|
async closeCard(externalId) {
|
|
13585
14467
|
await this.request(`/cards/${externalId}/close`, { method: "POST" });
|
|
13586
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
|
+
}
|
|
13587
14502
|
}
|
|
13588
14503
|
|
|
13589
14504
|
// src/providers/registry.ts
|
|
@@ -13606,574 +14521,717 @@ function createProviderInstance(type) {
|
|
|
13606
14521
|
registerProviderFactory("agentcard", () => {
|
|
13607
14522
|
throw new Error("AgentCard requires config. Use createAgentCardProvider() instead.");
|
|
13608
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
|
+
}
|
|
13609
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;
|
|
13610
14540
|
switch (type) {
|
|
13611
14541
|
case "agentcard":
|
|
13612
|
-
|
|
14542
|
+
instance = new AgentCardProvider({
|
|
14543
|
+
jwt: config.jwt,
|
|
14544
|
+
baseUrl: config.baseUrl
|
|
14545
|
+
});
|
|
14546
|
+
break;
|
|
13613
14547
|
default:
|
|
13614
|
-
|
|
13615
|
-
}
|
|
13616
|
-
}
|
|
13617
|
-
|
|
13618
|
-
// src/lib/config.ts
|
|
13619
|
-
import { homedir as homedir5 } from "os";
|
|
13620
|
-
import { join as join5 } from "path";
|
|
13621
|
-
import { existsSync as existsSync3, readFileSync, writeFileSync, mkdirSync as mkdirSync3 } from "fs";
|
|
13622
|
-
function getConfigDir() {
|
|
13623
|
-
if (process.env["WALLETS_CONFIG_DIR"])
|
|
13624
|
-
return process.env["WALLETS_CONFIG_DIR"];
|
|
13625
|
-
const home = homedir5();
|
|
13626
|
-
const newDir = join5(home, ".hasna", "wallets");
|
|
13627
|
-
const legacyDir = join5(home, ".wallets");
|
|
13628
|
-
if (!existsSync3(newDir) && existsSync3(legacyDir)) {
|
|
13629
|
-
return legacyDir;
|
|
13630
|
-
}
|
|
13631
|
-
return newDir;
|
|
13632
|
-
}
|
|
13633
|
-
function getConfigPath() {
|
|
13634
|
-
return join5(getConfigDir(), "config.json");
|
|
13635
|
-
}
|
|
13636
|
-
function loadConfig() {
|
|
13637
|
-
const configPath = getConfigPath();
|
|
13638
|
-
if (!existsSync3(configPath)) {
|
|
13639
|
-
return {};
|
|
13640
|
-
}
|
|
13641
|
-
try {
|
|
13642
|
-
return JSON.parse(readFileSync(configPath, "utf-8"));
|
|
13643
|
-
} catch {
|
|
13644
|
-
return {};
|
|
13645
|
-
}
|
|
13646
|
-
}
|
|
13647
|
-
function saveConfig(config) {
|
|
13648
|
-
const configDir = getConfigDir();
|
|
13649
|
-
if (!existsSync3(configDir)) {
|
|
13650
|
-
mkdirSync3(configDir, { recursive: true });
|
|
14548
|
+
instance = createProviderInstance(type);
|
|
13651
14549
|
}
|
|
13652
|
-
|
|
13653
|
-
|
|
13654
|
-
|
|
13655
|
-
|
|
13656
|
-
|
|
13657
|
-
|
|
13658
|
-
|
|
13659
|
-
|
|
13660
|
-
|
|
13661
|
-
|
|
13662
|
-
|
|
13663
|
-
|
|
13664
|
-
|
|
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;
|
|
13665
14595
|
}
|
|
13666
14596
|
|
|
13667
|
-
// src/
|
|
13668
|
-
|
|
13669
|
-
|
|
13670
|
-
|
|
13671
|
-
|
|
13672
|
-
const configDir = getConfigDir();
|
|
13673
|
-
checks.push({
|
|
13674
|
-
name: "Config directory",
|
|
13675
|
-
status: existsSync4(configDir) ? "ok" : "warn",
|
|
13676
|
-
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"
|
|
13677
14602
|
});
|
|
13678
|
-
|
|
13679
|
-
|
|
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) => {
|
|
13680
14627
|
try {
|
|
13681
|
-
|
|
13682
|
-
|
|
13683
|
-
|
|
13684
|
-
|
|
13685
|
-
|
|
13686
|
-
|
|
13687
|
-
|
|
13688
|
-
|
|
13689
|
-
|
|
13690
|
-
|
|
13691
|
-
|
|
13692
|
-
}
|
|
13693
|
-
} else {
|
|
13694
|
-
checks.push({
|
|
13695
|
-
name: "Default provider",
|
|
13696
|
-
status: "warn",
|
|
13697
|
-
message: "No default provider set. Use 'wallets provider add' to register one."
|
|
13698
|
-
});
|
|
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
|
+
}
|
|
13699
14640
|
}
|
|
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
|
-
|
|
13736
|
-
|
|
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}`;
|
|
13737
14700
|
}
|
|
14701
|
+
return { content: [{ type: "text", text }] };
|
|
14702
|
+
} catch (e) {
|
|
14703
|
+
return {
|
|
14704
|
+
content: [{ type: "text", text: formatError(e) }],
|
|
14705
|
+
isError: true
|
|
14706
|
+
};
|
|
13738
14707
|
}
|
|
13739
|
-
|
|
13740
|
-
|
|
13741
|
-
|
|
13742
|
-
|
|
13743
|
-
|
|
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
|
|
13744
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
|
+
};
|
|
13745
14738
|
}
|
|
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
|
-
|
|
13786
|
-
const sign = tx.type === "refund" || tx.type === "load" ? "+" : "-";
|
|
13787
|
-
return `${id} ${tx.type.padEnd(10)} ${tx.status.padEnd(10)} ${sign}$${tx.amount.toFixed(2).padStart(9)} ${tx.merchant || tx.description || ""}`;
|
|
13788
|
-
}
|
|
13789
|
-
function formatDoctorCheck(check) {
|
|
13790
|
-
const icon = check.status === "ok" ? "[ok]" : check.status === "warn" ? "[!!]" : "[ERR]";
|
|
13791
|
-
return `${icon} ${check.name}: ${check.message}`;
|
|
13792
|
-
}
|
|
13793
|
-
function formatError(error) {
|
|
13794
|
-
if (error instanceof ProviderNotFoundError) {
|
|
13795
|
-
return JSON.stringify({ code: ProviderNotFoundError.code, message: error.message, suggestion: ProviderNotFoundError.suggestion });
|
|
13796
|
-
}
|
|
13797
|
-
if (error instanceof CardNotFoundError) {
|
|
13798
|
-
return JSON.stringify({ code: CardNotFoundError.code, message: error.message, suggestion: CardNotFoundError.suggestion });
|
|
13799
|
-
}
|
|
13800
|
-
if (error instanceof ProviderError) {
|
|
13801
|
-
return JSON.stringify({ code: ProviderError.code, message: error.message, suggestion: ProviderError.suggestion });
|
|
13802
|
-
}
|
|
13803
|
-
if (error instanceof InsufficientFundsError) {
|
|
13804
|
-
return JSON.stringify({ code: InsufficientFundsError.code, message: error.message, suggestion: InsufficientFundsError.suggestion });
|
|
13805
|
-
}
|
|
13806
|
-
if (error instanceof ConfigError) {
|
|
13807
|
-
return JSON.stringify({ code: ConfigError.code, message: error.message, suggestion: ConfigError.suggestion });
|
|
13808
|
-
}
|
|
13809
|
-
if (error instanceof AgentNotFoundError) {
|
|
13810
|
-
return JSON.stringify({ code: AgentNotFoundError.code, message: error.message, suggestion: AgentNotFoundError.suggestion });
|
|
13811
|
-
}
|
|
13812
|
-
if (error instanceof WalletError) {
|
|
13813
|
-
return JSON.stringify({ code: WalletError.code, message: error.message, suggestion: WalletError.suggestion });
|
|
13814
|
-
}
|
|
13815
|
-
if (error instanceof Error) {
|
|
13816
|
-
return JSON.stringify({ code: "UNKNOWN_ERROR", message: error.message });
|
|
13817
|
-
}
|
|
13818
|
-
return JSON.stringify({ code: "UNKNOWN_ERROR", message: String(error) });
|
|
13819
|
-
}
|
|
13820
|
-
|
|
13821
|
-
// src/mcp/index.ts
|
|
13822
|
-
var server = new McpServer({
|
|
13823
|
-
name: "wallets",
|
|
13824
|
-
version: "0.1.0"
|
|
13825
|
-
});
|
|
13826
|
-
function resolveId(partialId, table = "cards") {
|
|
13827
|
-
const db = getDatabase();
|
|
13828
|
-
const id = resolvePartialId(db, table, partialId);
|
|
13829
|
-
if (!id)
|
|
13830
|
-
throw new Error(`Could not resolve ID: ${partialId}`);
|
|
13831
|
-
return id;
|
|
13832
|
-
}
|
|
13833
|
-
function getDefaultProvider() {
|
|
13834
|
-
const config = loadConfig();
|
|
13835
|
-
if (!config.default_provider)
|
|
13836
|
-
return null;
|
|
13837
|
-
const record = getProviderByName(config.default_provider);
|
|
13838
|
-
if (!record)
|
|
13839
|
-
return null;
|
|
13840
|
-
return { name: config.default_provider, record };
|
|
13841
|
-
}
|
|
13842
|
-
server.tool("create_card", "Create a new funded virtual card", {
|
|
13843
|
-
amount: exports_external.number().describe("Funding amount in dollars"),
|
|
13844
|
-
name: exports_external.string().optional().describe("Card display name"),
|
|
13845
|
-
provider: exports_external.string().optional().describe("Provider name (uses default if omitted)"),
|
|
13846
|
-
currency: exports_external.string().optional().describe("Currency code (default: USD)"),
|
|
13847
|
-
agent_id: exports_external.string().optional().describe("Agent name to assign the card to")
|
|
13848
|
-
}, async (params) => {
|
|
13849
|
-
try {
|
|
13850
|
-
const providerName = params.provider || getDefaultProvider()?.name;
|
|
13851
|
-
if (!providerName) {
|
|
13852
|
-
return { content: [{ type: "text", text: formatError(new Error("No provider specified and no default set. Use register_provider first.")) }], isError: true };
|
|
13853
|
-
}
|
|
13854
|
-
const providerRecord = getProviderByName(providerName);
|
|
13855
|
-
if (!providerRecord) {
|
|
13856
|
-
return { content: [{ type: "text", text: formatError(new Error(`Provider not found: ${providerName}`)) }], isError: true };
|
|
13857
|
-
}
|
|
13858
|
-
const providerConfig = { ...providerRecord.config, ...getProviderConfig(providerName) || {} };
|
|
13859
|
-
const instance = getProviderInstance(providerRecord.type, providerConfig);
|
|
13860
|
-
let agentId = null;
|
|
13861
|
-
if (params.agent_id) {
|
|
13862
|
-
const agent = registerAgent({ name: params.agent_id });
|
|
13863
|
-
agentId = agent.id;
|
|
13864
|
-
}
|
|
13865
|
-
const result = await instance.createCard({
|
|
13866
|
-
amount: params.amount,
|
|
13867
|
-
name: params.name,
|
|
13868
|
-
currency: params.currency || "USD",
|
|
13869
|
-
agent_id: agentId ?? undefined
|
|
13870
|
-
});
|
|
13871
|
-
const card = createCardRecord({
|
|
13872
|
-
provider_id: providerRecord.id,
|
|
13873
|
-
external_id: result.external_id || result.id,
|
|
13874
|
-
name: result.name,
|
|
13875
|
-
last_four: result.last_four,
|
|
13876
|
-
brand: result.brand,
|
|
13877
|
-
status: result.status,
|
|
13878
|
-
currency: result.currency,
|
|
13879
|
-
balance: result.balance,
|
|
13880
|
-
funded_amount: result.funded_amount,
|
|
13881
|
-
spending_limit: result.spending_limit,
|
|
13882
|
-
agent_id: agentId,
|
|
13883
|
-
metadata: result.funding_url ? { funding_url: result.funding_url } : {}
|
|
13884
|
-
});
|
|
13885
|
-
let text = `created: ${formatCard(card)}`;
|
|
13886
|
-
if (result.funding_url) {
|
|
13887
|
-
text += `
|
|
13888
|
-
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
|
+
};
|
|
13889
14779
|
}
|
|
13890
|
-
|
|
13891
|
-
|
|
13892
|
-
|
|
13893
|
-
}
|
|
13894
|
-
|
|
13895
|
-
|
|
13896
|
-
|
|
13897
|
-
|
|
13898
|
-
|
|
13899
|
-
|
|
13900
|
-
|
|
13901
|
-
|
|
13902
|
-
|
|
13903
|
-
|
|
13904
|
-
|
|
13905
|
-
|
|
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
|
+
};
|
|
13906
14812
|
}
|
|
13907
|
-
|
|
13908
|
-
|
|
13909
|
-
|
|
13910
|
-
|
|
13911
|
-
|
|
13912
|
-
|
|
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
|
+
};
|
|
13913
15057
|
}
|
|
13914
|
-
const lines =
|
|
15058
|
+
const lines = Object.entries(tools).map(([k, v]) => `${k}: ${v}`);
|
|
13915
15059
|
return { content: [{ type: "text", text: lines.join(`
|
|
13916
15060
|
`) }] };
|
|
13917
|
-
}
|
|
13918
|
-
|
|
13919
|
-
|
|
13920
|
-
});
|
|
13921
|
-
server.tool("get_card_details", "Get full card details (PAN, CVV, expiry)", {
|
|
13922
|
-
card_id: exports_external.string().describe("Card ID (supports partial matching)")
|
|
13923
|
-
}, async (params) => {
|
|
13924
|
-
try {
|
|
13925
|
-
const cardId = resolveId(params.card_id, "cards");
|
|
13926
|
-
const card = getCard(cardId);
|
|
13927
|
-
if (!card)
|
|
13928
|
-
throw new Error(`Card not found: ${params.card_id}`);
|
|
13929
|
-
const provider = getProvider(card.provider_id);
|
|
13930
|
-
if (!provider)
|
|
13931
|
-
throw new Error("Provider not found for card");
|
|
13932
|
-
const providerConfig = { ...provider.config, ...getProviderConfig(provider.name) || {} };
|
|
13933
|
-
const instance = getProviderInstance(provider.type, providerConfig);
|
|
13934
|
-
const details = await instance.getCardDetails(card.external_id);
|
|
15061
|
+
});
|
|
15062
|
+
server.resource("wallets://cards", "wallets://cards", async () => {
|
|
15063
|
+
const cards = listCards();
|
|
13935
15064
|
return {
|
|
13936
|
-
|
|
13937
|
-
|
|
13938
|
-
|
|
13939
|
-
|
|
13940
|
-
|
|
13941
|
-
|
|
13942
|
-
|
|
13943
|
-
`Exp: ${details.exp_month}/${details.exp_year}`,
|
|
13944
|
-
`Status: ${card.status}`,
|
|
13945
|
-
`Balance: $${details.balance.toFixed(2)}`
|
|
13946
|
-
].join(`
|
|
13947
|
-
`)
|
|
13948
|
-
}]
|
|
15065
|
+
contents: [
|
|
15066
|
+
{
|
|
15067
|
+
uri: "wallets://cards",
|
|
15068
|
+
text: JSON.stringify(cards, null, 2),
|
|
15069
|
+
mimeType: "application/json"
|
|
15070
|
+
}
|
|
15071
|
+
]
|
|
13949
15072
|
};
|
|
13950
|
-
}
|
|
13951
|
-
|
|
13952
|
-
|
|
13953
|
-
});
|
|
13954
|
-
server.tool("get_balance", "Check card balance", {
|
|
13955
|
-
card_id: exports_external.string().describe("Card ID")
|
|
13956
|
-
}, async (params) => {
|
|
13957
|
-
try {
|
|
13958
|
-
const cardId = resolveId(params.card_id, "cards");
|
|
13959
|
-
const card = getCard(cardId);
|
|
13960
|
-
if (!card)
|
|
13961
|
-
throw new Error(`Card not found: ${params.card_id}`);
|
|
13962
|
-
const provider = getProvider(card.provider_id);
|
|
13963
|
-
if (!provider)
|
|
13964
|
-
throw new Error("Provider not found for card");
|
|
13965
|
-
const providerConfig = { ...provider.config, ...getProviderConfig(provider.name) || {} };
|
|
13966
|
-
const instance = getProviderInstance(provider.type, providerConfig);
|
|
13967
|
-
const { balance, currency } = await instance.getBalance(card.external_id);
|
|
13968
|
-
updateCardBalance(cardId, balance);
|
|
15073
|
+
});
|
|
15074
|
+
server.resource("wallets://providers", "wallets://providers", async () => {
|
|
15075
|
+
const providers2 = listProviders();
|
|
13969
15076
|
return {
|
|
13970
|
-
|
|
13971
|
-
|
|
13972
|
-
|
|
13973
|
-
|
|
15077
|
+
contents: [
|
|
15078
|
+
{
|
|
15079
|
+
uri: "wallets://providers",
|
|
15080
|
+
text: JSON.stringify(providers2, null, 2),
|
|
15081
|
+
mimeType: "application/json"
|
|
15082
|
+
}
|
|
15083
|
+
]
|
|
13974
15084
|
};
|
|
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
|
-
|
|
14003
|
-
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
|
+
};
|
|
14004
15117
|
}
|
|
14005
|
-
|
|
14006
|
-
|
|
14007
|
-
|
|
14008
|
-
|
|
14009
|
-
|
|
14010
|
-
|
|
14011
|
-
}
|
|
14012
|
-
|
|
14013
|
-
|
|
14014
|
-
|
|
14015
|
-
|
|
14016
|
-
|
|
14017
|
-
|
|
14018
|
-
|
|
14019
|
-
}, async (params) => {
|
|
14020
|
-
try {
|
|
14021
|
-
const name = params.name || params.type;
|
|
14022
|
-
const config = {};
|
|
14023
|
-
if (params.jwt)
|
|
14024
|
-
config["jwt"] = params.jwt;
|
|
14025
|
-
if (params.api_key)
|
|
14026
|
-
config["api_key"] = params.api_key;
|
|
14027
|
-
if (params.base_url)
|
|
14028
|
-
config["baseUrl"] = params.base_url;
|
|
14029
|
-
const provider = ensureProvider(name, params.type, config);
|
|
14030
|
-
setProviderConfig(name, config);
|
|
14031
|
-
if (params.set_default) {
|
|
14032
|
-
const cfg = loadConfig();
|
|
14033
|
-
cfg.default_provider = name;
|
|
14034
|
-
saveConfig(cfg);
|
|
14035
|
-
}
|
|
14036
|
-
return { content: [{ type: "text", text: `registered: ${formatProvider(provider)}` }] };
|
|
14037
|
-
} catch (e) {
|
|
14038
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
14039
|
-
}
|
|
14040
|
-
});
|
|
14041
|
-
server.tool("list_transactions", "List card transactions", {
|
|
14042
|
-
card_id: exports_external.string().optional().describe("Card ID to filter"),
|
|
14043
|
-
limit: exports_external.number().optional().describe("Max results (default: 20)")
|
|
14044
|
-
}, async (params) => {
|
|
14045
|
-
try {
|
|
14046
|
-
let cardId;
|
|
14047
|
-
if (params.card_id) {
|
|
14048
|
-
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
|
+
};
|
|
14049
15132
|
}
|
|
14050
|
-
|
|
14051
|
-
|
|
14052
|
-
|
|
14053
|
-
|
|
14054
|
-
|
|
14055
|
-
|
|
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
|
+
};
|
|
14056
15157
|
}
|
|
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
|
-
|
|
14117
|
-
name: exports_external.string().describe("Agent name"),
|
|
14118
|
-
description: exports_external.string().optional().describe("Agent description")
|
|
14119
|
-
}, async (params) => {
|
|
14120
|
-
try {
|
|
14121
|
-
const agent = registerAgent({ name: params.name, description: params.description });
|
|
14122
|
-
return { content: [{ type: "text", text: JSON.stringify(agent, null, 2) }] };
|
|
14123
|
-
} catch (e) {
|
|
14124
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
14125
|
-
}
|
|
14126
|
-
});
|
|
14127
|
-
server.tool("list_agents", "List all registered agents.", {}, async () => {
|
|
14128
|
-
try {
|
|
14129
|
-
const agents = listAgents();
|
|
14130
|
-
return { content: [{ type: "text", text: JSON.stringify(agents, null, 2) }] };
|
|
14131
|
-
} catch (e) {
|
|
14132
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
14133
|
-
}
|
|
14134
|
-
});
|
|
14135
|
-
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) => {
|
|
14136
|
-
try {
|
|
14137
|
-
const agent = heartbeatAgent(params.agent_id);
|
|
14138
|
-
if (!agent)
|
|
14139
|
-
return { content: [{ type: "text", text: `Agent not found: ${params.agent_id}` }], isError: true };
|
|
14140
|
-
return { content: [{ type: "text", text: JSON.stringify(agent, null, 2) }] };
|
|
14141
|
-
} catch (e) {
|
|
14142
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
14143
|
-
}
|
|
14144
|
-
});
|
|
14145
|
-
server.tool("set_focus", "Set active project context for this agent session.", {
|
|
14146
|
-
agent_id: exports_external.string().describe("Agent ID or name"),
|
|
14147
|
-
project_id: exports_external.string().nullable().optional().describe("Project ID to focus on, or null to clear")
|
|
14148
|
-
}, async (params) => {
|
|
14149
|
-
try {
|
|
14150
|
-
const agent = setAgentFocus(params.agent_id, params.project_id ?? null);
|
|
14151
|
-
if (!agent)
|
|
14152
|
-
return { content: [{ type: "text", text: `Agent not found: ${params.agent_id}` }], isError: true };
|
|
14153
|
-
return { content: [{ type: "text", text: params.project_id ? `Focus: ${params.project_id}` : "Focus cleared" }] };
|
|
14154
|
-
} catch (e) {
|
|
14155
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
14156
|
-
}
|
|
14157
|
-
});
|
|
14158
|
-
server.tool("send_feedback", "Send feedback about this service", {
|
|
14159
|
-
message: exports_external.string().describe("Feedback message"),
|
|
14160
|
-
email: exports_external.string().optional().describe("Contact email (optional)"),
|
|
14161
|
-
category: exports_external.enum(["bug", "feature", "general"]).optional().describe("Feedback category")
|
|
14162
|
-
}, async (params) => {
|
|
14163
|
-
try {
|
|
14164
|
-
const db = getDatabase();
|
|
14165
|
-
const pkg = require_package();
|
|
14166
|
-
db.run("INSERT INTO feedback (message, email, category, version) VALUES (?, ?, ?, ?)", [params.message, params.email || null, params.category || "general", pkg.version]);
|
|
14167
|
-
return { content: [{ type: "text", text: "Feedback saved. Thank you!" }] };
|
|
14168
|
-
} catch (e) {
|
|
14169
|
-
return { content: [{ type: "text", text: formatError(e) }], isError: true };
|
|
14170
|
-
}
|
|
14171
|
-
});
|
|
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
|
+
}
|
|
14172
15218
|
async function main() {
|
|
14173
|
-
const
|
|
14174
|
-
|
|
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
|
+
});
|
|
14175
15230
|
}
|
|
14176
15231
|
main().catch((e) => {
|
|
14177
15232
|
console.error("Fatal:", e);
|
|
14178
15233
|
process.exit(1);
|
|
14179
15234
|
});
|
|
15235
|
+
export {
|
|
15236
|
+
buildServer
|
|
15237
|
+
};
|