paisa-mcp 0.4.2 → 0.4.3
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/dist/index.js +142 -132
- package/dist/index.js.map +12 -11
- package/package.json +2 -3
package/dist/index.js
CHANGED
|
@@ -53256,25 +53256,8 @@ async function decryptFields(input2, paths, { decrypt, isCiphertext }) {
|
|
|
53256
53256
|
return { data, undecrypted };
|
|
53257
53257
|
}
|
|
53258
53258
|
|
|
53259
|
-
// src/
|
|
53259
|
+
// ../crypto/src/index.ts
|
|
53260
53260
|
var import_hash_wasm = __toESM(require_index_umd(), 1);
|
|
53261
|
-
var ENCRYPTED_TXN_FIELDS = ["description", "referenceNumber", "notes"];
|
|
53262
|
-
var ENCRYPTED_MERCHANT_FIELDS = ["cleanName", "rawId", "notes"];
|
|
53263
|
-
async function hmacWithKey(value, key) {
|
|
53264
|
-
const rawKey = await crypto.subtle.exportKey("raw", key);
|
|
53265
|
-
const hmacKey = await crypto.subtle.importKey("raw", rawKey, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
53266
|
-
const sig = await crypto.subtle.sign("HMAC", hmacKey, new TextEncoder().encode(value));
|
|
53267
|
-
return btoa(String.fromCharCode(...new Uint8Array(sig)));
|
|
53268
|
-
}
|
|
53269
|
-
async function encryptPersonCipher(fields, key) {
|
|
53270
|
-
const cipher = await encryptField(JSON.stringify(fields), key);
|
|
53271
|
-
const searchHash = await hmacWithKey(fields.name.toLowerCase().trim().replace(/\s+/g, " "), key);
|
|
53272
|
-
return { cipher, searchHash };
|
|
53273
|
-
}
|
|
53274
|
-
async function decryptPersonCipher(cipher, key) {
|
|
53275
|
-
const json2 = await decryptField(cipher, key);
|
|
53276
|
-
return JSON.parse(json2);
|
|
53277
|
-
}
|
|
53278
53261
|
async function deriveKey(userId, pin, salt) {
|
|
53279
53262
|
const enc = new TextEncoder;
|
|
53280
53263
|
const baseKey = await crypto.subtle.importKey("raw", enc.encode(userId + pin), "PBKDF2", false, [
|
|
@@ -53318,6 +53301,34 @@ async function decryptField(encrypted, key) {
|
|
|
53318
53301
|
const plaintext = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, ciphertext);
|
|
53319
53302
|
return new TextDecoder().decode(plaintext);
|
|
53320
53303
|
}
|
|
53304
|
+
var VERIFIER_PLAINTEXT = "paisa-verify-v1";
|
|
53305
|
+
async function checkVerifier(key, encryptedVerifier) {
|
|
53306
|
+
try {
|
|
53307
|
+
const decrypted = await decryptField(encryptedVerifier, key);
|
|
53308
|
+
return decrypted === VERIFIER_PLAINTEXT;
|
|
53309
|
+
} catch {
|
|
53310
|
+
return false;
|
|
53311
|
+
}
|
|
53312
|
+
}
|
|
53313
|
+
|
|
53314
|
+
// src/crypto.ts
|
|
53315
|
+
var ENCRYPTED_TXN_FIELDS = ["description", "referenceNumber", "notes"];
|
|
53316
|
+
var ENCRYPTED_MERCHANT_FIELDS = ["cleanName", "rawId", "notes"];
|
|
53317
|
+
async function hmacWithKey(value, key) {
|
|
53318
|
+
const rawKey = await crypto.subtle.exportKey("raw", key);
|
|
53319
|
+
const hmacKey = await crypto.subtle.importKey("raw", rawKey, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
53320
|
+
const sig = await crypto.subtle.sign("HMAC", hmacKey, new TextEncoder().encode(value));
|
|
53321
|
+
return btoa(String.fromCharCode(...new Uint8Array(sig)));
|
|
53322
|
+
}
|
|
53323
|
+
async function encryptPersonCipher(fields, key) {
|
|
53324
|
+
const cipher = await encryptField(JSON.stringify(fields), key);
|
|
53325
|
+
const searchHash = await hmacWithKey(fields.name.toLowerCase().trim().replace(/\s+/g, " "), key);
|
|
53326
|
+
return { cipher, searchHash };
|
|
53327
|
+
}
|
|
53328
|
+
async function decryptPersonCipher(cipher, key) {
|
|
53329
|
+
const json2 = await decryptField(cipher, key);
|
|
53330
|
+
return JSON.parse(json2);
|
|
53331
|
+
}
|
|
53321
53332
|
async function encryptObjectFields(obj, fields, key) {
|
|
53322
53333
|
for (const field of fields) {
|
|
53323
53334
|
const val = obj[field];
|
|
@@ -53375,15 +53386,6 @@ async function decryptPersonCiphers(input2, key) {
|
|
|
53375
53386
|
merge3(data);
|
|
53376
53387
|
return { data, undecrypted };
|
|
53377
53388
|
}
|
|
53378
|
-
var VERIFIER_PLAINTEXT = "paisa-verify-v1";
|
|
53379
|
-
async function checkVerifier(key, encryptedVerifier) {
|
|
53380
|
-
try {
|
|
53381
|
-
const decrypted = await decryptField(encryptedVerifier, key);
|
|
53382
|
-
return decrypted === VERIFIER_PLAINTEXT;
|
|
53383
|
-
} catch {
|
|
53384
|
-
return false;
|
|
53385
|
-
}
|
|
53386
|
-
}
|
|
53387
53389
|
|
|
53388
53390
|
// src/lib/crypto-init.ts
|
|
53389
53391
|
async function initCrypto(client, pin, userId) {
|
|
@@ -54573,7 +54575,7 @@ async function decryptAnalytics(data, key) {
|
|
|
54573
54575
|
], key);
|
|
54574
54576
|
return undecrypted.length ? { ...decrypted, undecrypted } : decrypted;
|
|
54575
54577
|
}
|
|
54576
|
-
function analyticsTools(client,
|
|
54578
|
+
function analyticsTools(client, crypto4) {
|
|
54577
54579
|
return [
|
|
54578
54580
|
defineTool({
|
|
54579
54581
|
name: "get_analytics",
|
|
@@ -54598,8 +54600,8 @@ function analyticsTools(client, crypto3) {
|
|
|
54598
54600
|
data = await client.get(`/api/analytics/merchants/${params.id}`, params.months ? { months: params.months } : undefined);
|
|
54599
54601
|
break;
|
|
54600
54602
|
}
|
|
54601
|
-
if (
|
|
54602
|
-
data = await decryptAnalytics(data,
|
|
54603
|
+
if (crypto4)
|
|
54604
|
+
data = await decryptAnalytics(data, crypto4.key);
|
|
54603
54605
|
return data;
|
|
54604
54606
|
}
|
|
54605
54607
|
})
|
|
@@ -54657,7 +54659,7 @@ function budgetTools(client) {
|
|
|
54657
54659
|
}
|
|
54658
54660
|
|
|
54659
54661
|
// src/tools/dashboard.ts
|
|
54660
|
-
function dashboardTools(client,
|
|
54662
|
+
function dashboardTools(client, crypto4) {
|
|
54661
54663
|
return [
|
|
54662
54664
|
defineTool({
|
|
54663
54665
|
name: "get_dashboard",
|
|
@@ -54666,8 +54668,8 @@ function dashboardTools(client, crypto3) {
|
|
|
54666
54668
|
output: dashboardOutputSchema,
|
|
54667
54669
|
handler: async (params) => {
|
|
54668
54670
|
const data = await client.get("/api/dashboard", params.months ? { months: params.months } : undefined);
|
|
54669
|
-
if (
|
|
54670
|
-
const { data: decrypted, undecrypted } = await decryptTransactionFields(data.recentTransactions,
|
|
54671
|
+
if (crypto4 && Array.isArray(data?.recentTransactions)) {
|
|
54672
|
+
const { data: decrypted, undecrypted } = await decryptTransactionFields(data.recentTransactions, crypto4.key);
|
|
54671
54673
|
data.recentTransactions = decrypted;
|
|
54672
54674
|
if (undecrypted.length)
|
|
54673
54675
|
return { ...data, undecrypted };
|
|
@@ -55231,13 +55233,13 @@ async function categorySlugToId(client) {
|
|
|
55231
55233
|
}
|
|
55232
55234
|
return map2;
|
|
55233
55235
|
}
|
|
55234
|
-
function merchantVariants(client,
|
|
55236
|
+
function merchantVariants(client, crypto4) {
|
|
55235
55237
|
return {
|
|
55236
55238
|
list: variant(exports_external.object({ rawId: exports_external.string().optional().describe("merchants: find one by UPI ID or name") }), async ({ rawId }) => {
|
|
55237
55239
|
const data = await client.get("/api/merchants");
|
|
55238
55240
|
let undecrypted = [];
|
|
55239
|
-
if (
|
|
55240
|
-
const dec = await decryptMerchantFields(data.data,
|
|
55241
|
+
if (crypto4 && data.data) {
|
|
55242
|
+
const dec = await decryptMerchantFields(data.data, crypto4.key);
|
|
55241
55243
|
data.data = dec.data;
|
|
55242
55244
|
undecrypted = dec.undecrypted;
|
|
55243
55245
|
}
|
|
@@ -55295,7 +55297,7 @@ function merchantTools(client) {
|
|
|
55295
55297
|
}
|
|
55296
55298
|
|
|
55297
55299
|
// src/tools/persons.ts
|
|
55298
|
-
function personVariants(client,
|
|
55300
|
+
function personVariants(client, crypto4) {
|
|
55299
55301
|
return {
|
|
55300
55302
|
upsert: variant(exports_external.object({
|
|
55301
55303
|
id: exports_external.string().optional().describe("Person UUID — pass to update, omit to create"),
|
|
@@ -55305,13 +55307,13 @@ function personVariants(client, crypto3) {
|
|
|
55305
55307
|
relationship: exports_external.string().optional(),
|
|
55306
55308
|
notes: exports_external.string().optional()
|
|
55307
55309
|
}), async ({ id, ...updates }) => {
|
|
55308
|
-
if (
|
|
55310
|
+
if (crypto4) {
|
|
55309
55311
|
if (id) {
|
|
55310
55312
|
const existing = await client.get(`/api/persons/${id}`);
|
|
55311
55313
|
const personData = existing.data;
|
|
55312
55314
|
let current;
|
|
55313
55315
|
if (personData.cipher) {
|
|
55314
|
-
current = await decryptPersonCipher(personData.cipher,
|
|
55316
|
+
current = await decryptPersonCipher(personData.cipher, crypto4.key);
|
|
55315
55317
|
} else {
|
|
55316
55318
|
current = {
|
|
55317
55319
|
name: personData.name,
|
|
@@ -55328,7 +55330,7 @@ function personVariants(client, crypto3) {
|
|
|
55328
55330
|
relationship: updates.relationship ?? current.relationship ?? null,
|
|
55329
55331
|
notes: updates.notes ?? current.notes ?? null
|
|
55330
55332
|
};
|
|
55331
|
-
const payload2 = await encryptPersonCipher(merged,
|
|
55333
|
+
const payload2 = await encryptPersonCipher(merged, crypto4.key);
|
|
55332
55334
|
const data3 = await client.patch(`/api/persons/${id}`, payload2);
|
|
55333
55335
|
return data3;
|
|
55334
55336
|
}
|
|
@@ -55341,7 +55343,7 @@ function personVariants(client, crypto3) {
|
|
|
55341
55343
|
relationship: updates.relationship ?? null,
|
|
55342
55344
|
notes: updates.notes ?? null
|
|
55343
55345
|
};
|
|
55344
|
-
const payload = await encryptPersonCipher(fields,
|
|
55346
|
+
const payload = await encryptPersonCipher(fields, crypto4.key);
|
|
55345
55347
|
const data2 = await client.post("/api/persons", payload);
|
|
55346
55348
|
return data2;
|
|
55347
55349
|
}
|
|
@@ -55353,8 +55355,8 @@ function personVariants(client, crypto3) {
|
|
|
55353
55355
|
}), async ({ id }) => {
|
|
55354
55356
|
if (id) {
|
|
55355
55357
|
const data2 = await client.get(`/api/persons/${id}`);
|
|
55356
|
-
if (
|
|
55357
|
-
const { data: decrypted, undecrypted } = await decryptPersonCiphers(data2.data,
|
|
55358
|
+
if (crypto4 && data2.data.cipher) {
|
|
55359
|
+
const { data: decrypted, undecrypted } = await decryptPersonCiphers(data2.data, crypto4.key);
|
|
55358
55360
|
data2.data = decrypted;
|
|
55359
55361
|
if (undecrypted.length)
|
|
55360
55362
|
return { ...data2, undecrypted };
|
|
@@ -55362,8 +55364,8 @@ function personVariants(client, crypto3) {
|
|
|
55362
55364
|
return data2;
|
|
55363
55365
|
}
|
|
55364
55366
|
const data = await client.get("/api/persons");
|
|
55365
|
-
if (
|
|
55366
|
-
const { data: decrypted, undecrypted } = await decryptPersonCiphers(data.data,
|
|
55367
|
+
if (crypto4 && data.data) {
|
|
55368
|
+
const { data: decrypted, undecrypted } = await decryptPersonCiphers(data.data, crypto4.key);
|
|
55367
55369
|
data.data = decrypted;
|
|
55368
55370
|
if (undecrypted.length)
|
|
55369
55371
|
return { ...data, undecrypted };
|
|
@@ -55501,11 +55503,11 @@ function reportTools(client) {
|
|
|
55501
55503
|
}
|
|
55502
55504
|
|
|
55503
55505
|
// src/tools/entities.ts
|
|
55504
|
-
function entityTools(client,
|
|
55506
|
+
function entityTools(client, crypto4) {
|
|
55505
55507
|
const category = categoryVariants(client);
|
|
55506
55508
|
const account = accountVariants(client);
|
|
55507
|
-
const person = personVariants(client,
|
|
55508
|
-
const merchant = merchantVariants(client,
|
|
55509
|
+
const person = personVariants(client, crypto4);
|
|
55510
|
+
const merchant = merchantVariants(client, crypto4);
|
|
55509
55511
|
const debt = debtVariants(client);
|
|
55510
55512
|
const household = householdVariants(client);
|
|
55511
55513
|
const goal = goalVariants(client);
|
|
@@ -59154,10 +59156,10 @@ function toTxnRow(r2) {
|
|
|
59154
59156
|
isDuplicate: !!r2.isDuplicate
|
|
59155
59157
|
};
|
|
59156
59158
|
}
|
|
59157
|
-
async function fetchAllRows(client,
|
|
59158
|
-
return (await fetchAllRowsPaged(client,
|
|
59159
|
+
async function fetchAllRows(client, crypto4, params = {}, maxPages = 100) {
|
|
59160
|
+
return (await fetchAllRowsPaged(client, crypto4, params, maxPages)).rows;
|
|
59159
59161
|
}
|
|
59160
|
-
async function fetchAllRowsPaged(client,
|
|
59162
|
+
async function fetchAllRowsPaged(client, crypto4, params = {}, maxPages = 100) {
|
|
59161
59163
|
const out = [];
|
|
59162
59164
|
let cursor;
|
|
59163
59165
|
let truncated = false;
|
|
@@ -59167,7 +59169,7 @@ async function fetchAllRowsPaged(client, crypto3, params = {}, maxPages = 100) {
|
|
|
59167
59169
|
q2.cursor = cursor;
|
|
59168
59170
|
const res = await client.get("/api/transactions", q2);
|
|
59169
59171
|
const batch0 = res.data ?? [];
|
|
59170
|
-
const batch =
|
|
59172
|
+
const batch = crypto4 ? (await decryptTransactionFields(batch0, crypto4.key)).data : batch0;
|
|
59171
59173
|
out.push(...batch.map(toTxnRow));
|
|
59172
59174
|
if (!res.hasMore || !res.nextCursor || batch.length === 0 || res.nextCursor === cursor)
|
|
59173
59175
|
break;
|
|
@@ -59216,14 +59218,14 @@ function normalizePersonName(name) {
|
|
|
59216
59218
|
function normalizeUpiHandle(handle) {
|
|
59217
59219
|
return handle.toLowerCase().trim().split("@")[0];
|
|
59218
59220
|
}
|
|
59219
|
-
async function loadDecryptedPersons(client,
|
|
59221
|
+
async function loadDecryptedPersons(client, crypto4) {
|
|
59220
59222
|
const res = await client.get("/api/persons");
|
|
59221
59223
|
const persons = [];
|
|
59222
59224
|
for (const p2 of res.data ?? []) {
|
|
59223
59225
|
let name = p2.name ?? "";
|
|
59224
|
-
if (
|
|
59226
|
+
if (crypto4 && p2.cipher) {
|
|
59225
59227
|
try {
|
|
59226
|
-
name = (await decryptPersonCipher(p2.cipher,
|
|
59228
|
+
name = (await decryptPersonCipher(p2.cipher, crypto4.key)).name;
|
|
59227
59229
|
} catch {}
|
|
59228
59230
|
}
|
|
59229
59231
|
if (name)
|
|
@@ -59264,8 +59266,8 @@ function isPersonLikeNarration(description) {
|
|
|
59264
59266
|
const { name, paymentMethod } = parseNarration(description);
|
|
59265
59267
|
return classifyEntity({ paymentMethod, extractedName: name }) !== "merchant";
|
|
59266
59268
|
}
|
|
59267
|
-
async function backfillPersons(client,
|
|
59268
|
-
const existing = await loadDecryptedPersons(client,
|
|
59269
|
+
async function backfillPersons(client, crypto4, opts = {}) {
|
|
59270
|
+
const existing = await loadDecryptedPersons(client, crypto4);
|
|
59269
59271
|
const personIdByNorm = new Map;
|
|
59270
59272
|
const personIdByHandle = new Map;
|
|
59271
59273
|
for (const p2 of existing) {
|
|
@@ -59273,7 +59275,7 @@ async function backfillPersons(client, crypto3, opts = {}) {
|
|
|
59273
59275
|
for (const h2 of p2.upiHandles)
|
|
59274
59276
|
personIdByHandle.set(normalizeUpiHandle(h2), p2.id);
|
|
59275
59277
|
}
|
|
59276
|
-
const { rows, truncated } = await fetchAllRowsPaged(client,
|
|
59278
|
+
const { rows, truncated } = await fetchAllRowsPaged(client, crypto4, { unresolved: "true" });
|
|
59277
59279
|
const candidates = rows.filter((r2) => !r2.merchantId && !r2.personId && !r2.isTransfer && !r2.isIgnored);
|
|
59278
59280
|
const directLinks = [];
|
|
59279
59281
|
const groups = new Map;
|
|
@@ -59365,7 +59367,7 @@ async function backfillPersons(client, crypto3, opts = {}) {
|
|
|
59365
59367
|
relationship: null,
|
|
59366
59368
|
notes: null
|
|
59367
59369
|
};
|
|
59368
|
-
return
|
|
59370
|
+
return crypto4 ? await encryptPersonCipher(fields, crypto4.key) : { name: displayName, upiHandles, phoneNumbers: [], relationship: null, notes: null };
|
|
59369
59371
|
}));
|
|
59370
59372
|
const res = await client.post("/api/persons/bulk", {
|
|
59371
59373
|
persons: bodies
|
|
@@ -59500,20 +59502,20 @@ function groupRows(rows) {
|
|
|
59500
59502
|
function isCategorizable(r2) {
|
|
59501
59503
|
return !r2.categoryId && !r2.isTransfer && !r2.isIgnored && !r2.isDuplicate;
|
|
59502
59504
|
}
|
|
59503
|
-
async function fetchUncategorized(client,
|
|
59505
|
+
async function fetchUncategorized(client, crypto4, range = {}) {
|
|
59504
59506
|
const params = { uncategorized: "true" };
|
|
59505
59507
|
if (range.startDate)
|
|
59506
59508
|
params.startDate = range.startDate;
|
|
59507
59509
|
if (range.endDate)
|
|
59508
59510
|
params.endDate = range.endDate;
|
|
59509
|
-
return (await fetchAllRows(client,
|
|
59511
|
+
return (await fetchAllRows(client, crypto4, params)).filter(isCategorizable);
|
|
59510
59512
|
}
|
|
59511
59513
|
var MERCHANT_CREATE_CONCURRENCY = 5;
|
|
59512
59514
|
var titleCase = (s2) => s2.replace(/\b[a-z]/g, (c2) => c2.toUpperCase());
|
|
59513
|
-
async function applyGroupAssignments(client,
|
|
59515
|
+
async function applyGroupAssignments(client, crypto4, assignments, opts = {}) {
|
|
59514
59516
|
const catIndex = await loadCategoryIndex(client);
|
|
59515
59517
|
const resolved = assignments.map((a2) => ({ a: a2, cat: requireCategory(catIndex, a2.categorySlug) }));
|
|
59516
|
-
const groups = groupRows(await fetchUncategorized(client,
|
|
59518
|
+
const groups = groupRows(await fetchUncategorized(client, crypto4, opts));
|
|
59517
59519
|
const result = { applied: [], notFound: [], rulesSaved: 0, unlearnable: [] };
|
|
59518
59520
|
const plan = [];
|
|
59519
59521
|
const claimed = new Set;
|
|
@@ -59568,7 +59570,7 @@ async function applyGroupAssignments(client, crypto3, assignments, opts = {}) {
|
|
|
59568
59570
|
const { group, learn, confirmMerchant } = p2;
|
|
59569
59571
|
if (!learn || group.pattern === UNPARSED) {
|
|
59570
59572
|
directPlans.push({ p: p2, reason: learn ? UNPARSED : undefined });
|
|
59571
|
-
} else if (
|
|
59573
|
+
} else if (crypto4 && !confirmMerchant && group.rows.some((r2) => isPersonLikeNarration(r2.description))) {
|
|
59572
59574
|
directPlans.push({ p: p2, reason: `${group.pattern} (private mode: person-like)` });
|
|
59573
59575
|
} else if (taught.has(group.pattern) && taught.get(group.pattern) !== p2.categoryId) {
|
|
59574
59576
|
directPlans.push({ p: p2, reason: `${group.pattern} (conflicting category for same pattern)` });
|
|
@@ -59666,12 +59668,12 @@ function flagBatchDuplicates(txns) {
|
|
|
59666
59668
|
return false;
|
|
59667
59669
|
});
|
|
59668
59670
|
}
|
|
59669
|
-
async function importTransactions(client,
|
|
59671
|
+
async function importTransactions(client, crypto4, opts) {
|
|
59670
59672
|
let txns = opts.txns;
|
|
59671
59673
|
let duplicatesSkipped = 0;
|
|
59672
59674
|
if (txns.length > 0) {
|
|
59673
59675
|
const dates = txns.map((t2) => t2.date).sort();
|
|
59674
|
-
const existing = await fetchAllRows(client,
|
|
59676
|
+
const existing = await fetchAllRows(client, crypto4, {
|
|
59675
59677
|
startDate: dates[0],
|
|
59676
59678
|
endDate: dates[dates.length - 1]
|
|
59677
59679
|
});
|
|
@@ -59687,8 +59689,8 @@ async function importTransactions(client, crypto3, opts) {
|
|
|
59687
59689
|
const base = { ...t2, isDuplicate: dupFlags[i2] };
|
|
59688
59690
|
return enrichedData ? { ...base, enrichedData } : base;
|
|
59689
59691
|
});
|
|
59690
|
-
if (
|
|
59691
|
-
await encryptTransactionFields(rows,
|
|
59692
|
+
if (crypto4)
|
|
59693
|
+
await encryptTransactionFields(rows, crypto4.key);
|
|
59692
59694
|
const CHUNK = 200;
|
|
59693
59695
|
let imported = 0;
|
|
59694
59696
|
for (let i2 = 0;i2 < rows.length; i2 += CHUNK) {
|
|
@@ -59703,19 +59705,19 @@ async function importTransactions(client, crypto3, opts) {
|
|
|
59703
59705
|
}
|
|
59704
59706
|
return { imported, duplicatesSkipped, duplicatesFlagged };
|
|
59705
59707
|
}
|
|
59706
|
-
async function runRescan(client,
|
|
59708
|
+
async function runRescan(client, crypto4) {
|
|
59707
59709
|
let bag = structuredClone(EMPTY_BAG);
|
|
59708
59710
|
try {
|
|
59709
59711
|
bag = (await loadBag(client)).bag;
|
|
59710
59712
|
} catch {}
|
|
59711
59713
|
const merchantsRes = await client.get("/api/merchants");
|
|
59712
59714
|
const allMerchants0 = merchantsRes.data ?? [];
|
|
59713
|
-
const allMerchants =
|
|
59715
|
+
const allMerchants = crypto4 ? (await decryptMerchantFields(allMerchants0, crypto4.key)).data : allMerchants0;
|
|
59714
59716
|
const globalMerchants = allMerchants.filter((m2) => !m2.householdId && m2.cleanName).map((m2) => ({ id: m2.id, name: m2.cleanName }));
|
|
59715
59717
|
const localMerchants = allMerchants.filter((m2) => m2.householdId && m2.cleanName).map((m2) => ({ id: m2.id, name: m2.cleanName }));
|
|
59716
59718
|
const merchantCategoryById = new Map(allMerchants.filter((m2) => m2.categoryId).map((m2) => [m2.id, m2.categoryId]));
|
|
59717
|
-
const persons = await loadDecryptedPersons(client,
|
|
59718
|
-
const allRows = await fetchAllRows(client,
|
|
59719
|
+
const persons = await loadDecryptedPersons(client, crypto4);
|
|
59720
|
+
const allRows = await fetchAllRows(client, crypto4, { unresolved: "true" });
|
|
59719
59721
|
const categoryByTxn = new Map(allRows.map((r2) => [r2.id, r2.categoryId]));
|
|
59720
59722
|
const plan = autoCategorizeFromDescriptions({
|
|
59721
59723
|
rows: allRows,
|
|
@@ -59724,8 +59726,17 @@ async function runRescan(client, crypto3) {
|
|
|
59724
59726
|
localMerchants,
|
|
59725
59727
|
persons
|
|
59726
59728
|
});
|
|
59729
|
+
const knownMerchants = new Set([
|
|
59730
|
+
...allMerchants.map((m2) => m2.id),
|
|
59731
|
+
...allRows.flatMap((r2) => r2.merchantId ? [r2.merchantId] : [])
|
|
59732
|
+
]);
|
|
59733
|
+
const knownPersons = new Set([
|
|
59734
|
+
...persons.map((p2) => p2.id),
|
|
59735
|
+
...allRows.flatMap((r2) => r2.personId ? [r2.personId] : [])
|
|
59736
|
+
]);
|
|
59737
|
+
const live = plan.suggestions.filter((s2) => s2.entity.type === "merchant" ? knownMerchants.has(s2.entity.merchantId) : knownPersons.has(s2.entity.personId));
|
|
59727
59738
|
let categorized = 0;
|
|
59728
|
-
const links =
|
|
59739
|
+
const links = live.flatMap((s2) => {
|
|
59729
59740
|
const categoryId = s2.categoryId ?? (s2.entity.type === "merchant" ? merchantCategoryById.get(s2.entity.merchantId) ?? null : null);
|
|
59730
59741
|
return s2.transactionIds.map((txId) => {
|
|
59731
59742
|
const applyCategory = categoryId && !categoryByTxn.get(txId);
|
|
@@ -59745,7 +59756,7 @@ var TRUNCATED_WARNING = "TRUNCATED: the API capped this result, so it is INCOMPL
|
|
|
59745
59756
|
function withTruncationWarning(data) {
|
|
59746
59757
|
return data.truncated ? { warning: TRUNCATED_WARNING, ...data } : data;
|
|
59747
59758
|
}
|
|
59748
|
-
function transactionTools(client,
|
|
59759
|
+
function transactionTools(client, crypto4) {
|
|
59749
59760
|
return [
|
|
59750
59761
|
defineTool({
|
|
59751
59762
|
name: "get_transactions",
|
|
@@ -59792,7 +59803,7 @@ function transactionTools(client, crypto3) {
|
|
|
59792
59803
|
apiParams.categoryId = requireCategory(await loadCategoryIndex(client), categorySlug).id;
|
|
59793
59804
|
}
|
|
59794
59805
|
if (personName && !apiParams.personId) {
|
|
59795
|
-
const persons = await loadDecryptedPersons(client,
|
|
59806
|
+
const persons = await loadDecryptedPersons(client, crypto4);
|
|
59796
59807
|
const match = resolvePersonByName(persons, personName);
|
|
59797
59808
|
const err = (error62) => ({ error: error62 });
|
|
59798
59809
|
if (!match) {
|
|
@@ -59809,14 +59820,14 @@ function transactionTools(client, crypto3) {
|
|
|
59809
59820
|
const data = await client.get("/api/transactions", Object.fromEntries(Object.entries(apiParams).filter(([, v2]) => v2 !== undefined).map(([k2, v2]) => [k2, String(v2)])));
|
|
59810
59821
|
let rows = data.data ?? [];
|
|
59811
59822
|
let undecrypted = [];
|
|
59812
|
-
if (
|
|
59813
|
-
const dec = await decryptTransactionFields(rows,
|
|
59823
|
+
if (crypto4) {
|
|
59824
|
+
const dec = await decryptTransactionFields(rows, crypto4.key);
|
|
59814
59825
|
rows = dec.data;
|
|
59815
59826
|
undecrypted = dec.undecrypted;
|
|
59816
59827
|
}
|
|
59817
59828
|
const personNames = new Map;
|
|
59818
59829
|
if (rows.some((r2) => r2.personId)) {
|
|
59819
|
-
for (const p2 of await loadDecryptedPersons(client,
|
|
59830
|
+
for (const p2 of await loadDecryptedPersons(client, crypto4))
|
|
59820
59831
|
personNames.set(p2.id, p2.name);
|
|
59821
59832
|
}
|
|
59822
59833
|
if (full) {
|
|
@@ -59847,7 +59858,7 @@ function transactionTools(client, crypto3) {
|
|
|
59847
59858
|
limit: exports_external.number().optional().describe("Max groups (default 40, max 100)")
|
|
59848
59859
|
}),
|
|
59849
59860
|
handler: async ({ startDate, endDate, minTotal, limit }) => {
|
|
59850
|
-
const rows = await fetchUncategorized(client,
|
|
59861
|
+
const rows = await fetchUncategorized(client, crypto4, { startDate, endDate });
|
|
59851
59862
|
const all2 = groupRows(rows);
|
|
59852
59863
|
const shown = all2.filter((g2) => g2.total >= (minTotal ?? 0)).slice(0, Math.min(Math.max(limit ?? 40, 1), 100));
|
|
59853
59864
|
const shownRows = shown.reduce((n2, g2) => n2 + g2.rows.length, 0);
|
|
@@ -59884,7 +59895,7 @@ function transactionTools(client, crypto3) {
|
|
|
59884
59895
|
handler: (args, ctx) => {
|
|
59885
59896
|
const { assignments, startDate, endDate, dryRun } = args;
|
|
59886
59897
|
const { expectedPlanHash } = args;
|
|
59887
|
-
return applyGroupAssignments(client,
|
|
59898
|
+
return applyGroupAssignments(client, crypto4, assignments, {
|
|
59888
59899
|
dryRun,
|
|
59889
59900
|
startDate,
|
|
59890
59901
|
endDate,
|
|
@@ -59897,7 +59908,7 @@ function transactionTools(client, crypto3) {
|
|
|
59897
59908
|
name: "backfill_persons",
|
|
59898
59909
|
description: "Create persons from UPI/IMPS counterparties in unlinked transactions (deduped by name) and link those transactions to them. Idempotent. dryRun previews.",
|
|
59899
59910
|
input: exports_external.object({ dryRun: exports_external.boolean().optional() }),
|
|
59900
|
-
handler: (args, ctx) => backfillPersons(client,
|
|
59911
|
+
handler: (args, ctx) => backfillPersons(client, crypto4, {
|
|
59901
59912
|
onProgress: progressFor(ctx),
|
|
59902
59913
|
dryRun: args.dryRun,
|
|
59903
59914
|
expectedPlanHash: args.expectedPlanHash
|
|
@@ -59919,8 +59930,8 @@ function transactionTools(client, crypto3) {
|
|
|
59919
59930
|
}),
|
|
59920
59931
|
handler: async ({ id, ...updates }) => {
|
|
59921
59932
|
const body = { ...updates };
|
|
59922
|
-
if (
|
|
59923
|
-
body.notes = await encryptField(body.notes,
|
|
59933
|
+
if (crypto4 && body.notes)
|
|
59934
|
+
body.notes = await encryptField(body.notes, crypto4.key);
|
|
59924
59935
|
if (body.categorySlug) {
|
|
59925
59936
|
const cat = requireCategory(await loadCategoryIndex(client), body.categorySlug);
|
|
59926
59937
|
body.categoryId = cat.id;
|
|
@@ -59969,8 +59980,8 @@ function transactionTools(client, crypto3) {
|
|
|
59969
59980
|
}
|
|
59970
59981
|
if (action === "list") {
|
|
59971
59982
|
const data2 = await client.get("/api/transactions/duplicates");
|
|
59972
|
-
if (
|
|
59973
|
-
const { data: decrypted, undecrypted } = await decryptTransactionFields(data2.data,
|
|
59983
|
+
if (crypto4 && data2.data) {
|
|
59984
|
+
const { data: decrypted, undecrypted } = await decryptTransactionFields(data2.data, crypto4.key);
|
|
59974
59985
|
data2.data = decrypted;
|
|
59975
59986
|
return withTruncationWarning(undecrypted.length ? { ...data2, undecrypted } : data2);
|
|
59976
59987
|
}
|
|
@@ -59993,8 +60004,8 @@ function transactionTools(client, crypto3) {
|
|
|
59993
60004
|
}),
|
|
59994
60005
|
handler: async ({ accountId, startDate, endDate }) => {
|
|
59995
60006
|
const data = await client.get("/api/transactions/reconciliation-context", { accountId, startDate, endDate });
|
|
59996
|
-
if (
|
|
59997
|
-
const { data: decrypted, undecrypted } = await decryptTransactionFields(data.data,
|
|
60007
|
+
if (crypto4 && data.data) {
|
|
60008
|
+
const { data: decrypted, undecrypted } = await decryptTransactionFields(data.data, crypto4.key);
|
|
59998
60009
|
data.data = decrypted;
|
|
59999
60010
|
return withTruncationWarning(undecrypted.length ? { ...data, undecrypted } : data);
|
|
60000
60011
|
}
|
|
@@ -60081,7 +60092,7 @@ function transactionTools(client, crypto3) {
|
|
|
60081
60092
|
name: "rescan_transactions",
|
|
60082
60093
|
description: "Apply saved learning rules (merchant aliases, category rules) to uncategorized transactions, like the web app's auto-categorize.",
|
|
60083
60094
|
input: exports_external.object({}),
|
|
60084
|
-
handler: () => runRescan(client,
|
|
60095
|
+
handler: () => runRescan(client, crypto4)
|
|
60085
60096
|
}),
|
|
60086
60097
|
defineTool({
|
|
60087
60098
|
name: "get_settlement_candidates",
|
|
@@ -60089,8 +60100,8 @@ function transactionTools(client, crypto3) {
|
|
|
60089
60100
|
input: exports_external.object({ ccAccountId: exports_external.string().uuid() }),
|
|
60090
60101
|
handler: async ({ ccAccountId }) => {
|
|
60091
60102
|
const data = await client.get("/api/transactions/settlement-candidates", { ccAccountId });
|
|
60092
|
-
if (
|
|
60093
|
-
const { data: decrypted, undecrypted } = await decryptTransactionFields(data.data,
|
|
60103
|
+
if (crypto4 && data.data) {
|
|
60104
|
+
const { data: decrypted, undecrypted } = await decryptTransactionFields(data.data, crypto4.key);
|
|
60094
60105
|
data.data = decrypted;
|
|
60095
60106
|
return withTruncationWarning(undecrypted.length ? { ...data, undecrypted } : data);
|
|
60096
60107
|
}
|
|
@@ -60107,8 +60118,8 @@ function transactionTools(client, crypto3) {
|
|
|
60107
60118
|
if (!row)
|
|
60108
60119
|
return res;
|
|
60109
60120
|
let undecrypted = [];
|
|
60110
|
-
if (
|
|
60111
|
-
const dec = await decryptTransactionFields([row],
|
|
60121
|
+
if (crypto4) {
|
|
60122
|
+
const dec = await decryptTransactionFields([row], crypto4.key);
|
|
60112
60123
|
row = dec.data[0];
|
|
60113
60124
|
undecrypted = dec.undecrypted;
|
|
60114
60125
|
}
|
|
@@ -60134,7 +60145,7 @@ function transactionTools(client, crypto3) {
|
|
|
60134
60145
|
}
|
|
60135
60146
|
|
|
60136
60147
|
// src/tools/export.ts
|
|
60137
|
-
function exportTools(client,
|
|
60148
|
+
function exportTools(client, crypto4) {
|
|
60138
60149
|
return [
|
|
60139
60150
|
defineTool({
|
|
60140
60151
|
name: "export_transactions",
|
|
@@ -60151,8 +60162,8 @@ function exportTools(client, crypto3) {
|
|
|
60151
60162
|
...Object.fromEntries(Object.entries(params).filter(([, v2]) => v2 !== undefined)),
|
|
60152
60163
|
format: "json"
|
|
60153
60164
|
});
|
|
60154
|
-
if (
|
|
60155
|
-
const { data: decrypted, undecrypted } = await decryptTransactionFields(data.transactions,
|
|
60165
|
+
if (crypto4 && Array.isArray(data?.transactions)) {
|
|
60166
|
+
const { data: decrypted, undecrypted } = await decryptTransactionFields(data.transactions, crypto4.key);
|
|
60156
60167
|
data.transactions = decrypted;
|
|
60157
60168
|
return withTruncationWarning(undecrypted.length ? { ...data, undecrypted } : data);
|
|
60158
60169
|
}
|
|
@@ -60394,28 +60405,28 @@ var DEFAULT_MIN_CONFIDENCE = 0.95;
|
|
|
60394
60405
|
var TIMEOUT_MS = 8000;
|
|
60395
60406
|
var CONCURRENCY = 8;
|
|
60396
60407
|
var MIN_LEARN_PATTERN_LEN = 5;
|
|
60397
|
-
function assertPrivateModeAllowed(
|
|
60398
|
-
if (
|
|
60408
|
+
function assertPrivateModeAllowed(crypto4, env2 = process.env) {
|
|
60409
|
+
if (crypto4 && env2.JEV_ALLOW_PRIVATE_MODE !== "1") {
|
|
60399
60410
|
throw new Error("Jev categorization is disabled in private mode: it would send transaction text to a third party. " + "To opt in for your own account, set JEV_ALLOW_PRIVATE_MODE=1 in the MCP's environment. " + "Otherwise use get_uncategorized_groups + categorize_groups.");
|
|
60400
60411
|
}
|
|
60401
60412
|
}
|
|
60402
|
-
function assertJevAllowed(
|
|
60403
|
-
assertPrivateModeAllowed(
|
|
60413
|
+
function assertJevAllowed(crypto4, env2 = process.env) {
|
|
60414
|
+
assertPrivateModeAllowed(crypto4, env2);
|
|
60404
60415
|
const key = env2.JEV_API_KEY;
|
|
60405
60416
|
if (!key)
|
|
60406
60417
|
throw new Error("JEV_API_KEY is not set — Jev categorization is off.");
|
|
60407
60418
|
return key;
|
|
60408
60419
|
}
|
|
60409
|
-
function jevAllowSensitive(
|
|
60410
|
-
return
|
|
60420
|
+
function jevAllowSensitive(crypto4, env2) {
|
|
60421
|
+
return crypto4 === undefined || env2.JEV_ALLOW_PRIVATE_MODE === "1";
|
|
60411
60422
|
}
|
|
60412
|
-
function makeJev(apiKey,
|
|
60423
|
+
function makeJev(apiKey, crypto4, env2 = process.env, fetchImpl) {
|
|
60413
60424
|
return createJev({
|
|
60414
60425
|
apiKey,
|
|
60415
60426
|
model: JEV_MODEL,
|
|
60416
60427
|
minConfidence: DEFAULT_MIN_CONFIDENCE,
|
|
60417
60428
|
timeoutMs: TIMEOUT_MS,
|
|
60418
|
-
allowSensitive: jevAllowSensitive(
|
|
60429
|
+
allowSensitive: jevAllowSensitive(crypto4, env2),
|
|
60419
60430
|
...fetchImpl ? { fetch: fetchImpl } : {}
|
|
60420
60431
|
});
|
|
60421
60432
|
}
|
|
@@ -60435,10 +60446,10 @@ function groupState(type, sample) {
|
|
|
60435
60446
|
const dir = type === "credit" ? "Money received" : "Money paid out";
|
|
60436
60447
|
return `${dir}. Bank narration: "${sample.slice(0, 80)}"`;
|
|
60437
60448
|
}
|
|
60438
|
-
async function jevCategorize(client,
|
|
60439
|
-
assertPrivateModeAllowed(
|
|
60449
|
+
async function jevCategorize(client, crypto4, jev, opts = {}) {
|
|
60450
|
+
assertPrivateModeAllowed(crypto4, opts.env);
|
|
60440
60451
|
const index = await loadCategoryIndex(client);
|
|
60441
|
-
const rows = await fetchUncategorized(client,
|
|
60452
|
+
const rows = await fetchUncategorized(client, crypto4, opts);
|
|
60442
60453
|
const groups = groupRows(rows);
|
|
60443
60454
|
const unparsed = groups.filter((g2) => g2.pattern === UNPARSED).length;
|
|
60444
60455
|
const candidates = groups.filter((g2) => g2.pattern !== UNPARSED);
|
|
@@ -60446,7 +60457,7 @@ async function jevCategorize(client, crypto3, jev, opts = {}) {
|
|
|
60446
60457
|
const suggestions = await mapLimit(candidates, CONCURRENCY, async (g2) => {
|
|
60447
60458
|
const options = optionsFor(g2.type, index);
|
|
60448
60459
|
const sample = g2.rows[0].description;
|
|
60449
|
-
const text =
|
|
60460
|
+
const text = crypto4 ? g2.pattern : sample;
|
|
60450
60461
|
const d2 = await jev.choice({
|
|
60451
60462
|
question: "Which spending category best fits this bank transaction?",
|
|
60452
60463
|
options,
|
|
@@ -60488,7 +60499,7 @@ async function jevCategorize(client, crypto3, jev, opts = {}) {
|
|
|
60488
60499
|
}
|
|
60489
60500
|
}
|
|
60490
60501
|
review.sort((a2, b2) => b2.total - a2.total);
|
|
60491
|
-
const applied = await applyGroupAssignments(client,
|
|
60502
|
+
const applied = await applyGroupAssignments(client, crypto4, accepted.map(({ confidence: _c, ...a2 }) => a2), { dryRun: opts.dryRun, startDate: opts.startDate, endDate: opts.endDate });
|
|
60492
60503
|
const conf = new Map(accepted.map((a2) => [`${a2.pattern}|${a2.type}`, a2.confidence]));
|
|
60493
60504
|
return {
|
|
60494
60505
|
dryRun: !!opts.dryRun,
|
|
@@ -60505,7 +60516,7 @@ async function jevCategorize(client, crypto3, jev, opts = {}) {
|
|
|
60505
60516
|
}
|
|
60506
60517
|
|
|
60507
60518
|
// src/tools/jev.ts
|
|
60508
|
-
function jevTools(client,
|
|
60519
|
+
function jevTools(client, crypto4) {
|
|
60509
60520
|
return [
|
|
60510
60521
|
defineTool({
|
|
60511
60522
|
name: "auto_categorize_jev",
|
|
@@ -60517,9 +60528,9 @@ function jevTools(client, crypto3) {
|
|
|
60517
60528
|
dryRun: exports_external.boolean().optional()
|
|
60518
60529
|
}),
|
|
60519
60530
|
handler: async ({ startDate, endDate, minConfidence, dryRun }) => {
|
|
60520
|
-
const key = assertJevAllowed(
|
|
60521
|
-
const jev = makeJev(key,
|
|
60522
|
-
const result = await jevCategorize(client,
|
|
60531
|
+
const key = assertJevAllowed(crypto4);
|
|
60532
|
+
const jev = makeJev(key, crypto4);
|
|
60533
|
+
const result = await jevCategorize(client, crypto4, jev, {
|
|
60523
60534
|
startDate,
|
|
60524
60535
|
endDate,
|
|
60525
60536
|
minConfidence,
|
|
@@ -60543,12 +60554,12 @@ function profileVariants(client) {
|
|
|
60543
60554
|
function settingsTools(client) {
|
|
60544
60555
|
const profile = profileVariants(client);
|
|
60545
60556
|
const household = householdVariants(client);
|
|
60546
|
-
const
|
|
60557
|
+
const crypto4 = authCryptoVariants(client);
|
|
60547
60558
|
const get = unionTool({
|
|
60548
60559
|
name: "get_settings",
|
|
60549
60560
|
description: "Read settings. profile: your name, email, owner. household: name, currency, locale, owner labels, members. encryption: mode (standard/private) and whether a PIN verifier / recovery key is set.",
|
|
60550
60561
|
key: "section",
|
|
60551
|
-
variants: { profile: profile.get, household: household.get, encryption:
|
|
60562
|
+
variants: { profile: profile.get, household: household.get, encryption: crypto4.status }
|
|
60552
60563
|
});
|
|
60553
60564
|
const update = unionTool({
|
|
60554
60565
|
name: "update_settings",
|
|
@@ -60630,7 +60641,7 @@ async function fetchReconciliationContext(client, accountId, start, end, budget
|
|
|
60630
60641
|
}
|
|
60631
60642
|
var sum2 = (rows) => Math.round(rows.reduce((n2, r2) => n2 + r2.amount, 0));
|
|
60632
60643
|
async function runStatementImport(deps, args) {
|
|
60633
|
-
const { client, crypto:
|
|
60644
|
+
const { client, crypto: crypto4 } = deps;
|
|
60634
60645
|
const accounts = (await client.get("/api/accounts")).data ?? [];
|
|
60635
60646
|
const account = resolveAccount(accounts, args.account);
|
|
60636
60647
|
const format = pickFormat(account, args.format);
|
|
@@ -60649,7 +60660,7 @@ async function runStatementImport(deps, args) {
|
|
|
60649
60660
|
}
|
|
60650
60661
|
const dates = txns.map((t2) => t2.date).sort();
|
|
60651
60662
|
const existing0 = await fetchReconciliationContext(client, account.id, dates[0], dates[dates.length - 1]);
|
|
60652
|
-
const existing =
|
|
60663
|
+
const existing = crypto4 ? (await decryptTransactionFields(existing0, crypto4.key)).data : existing0;
|
|
60653
60664
|
const present = existing.map((r2) => ({ ...r2, isDuplicate: false, isIgnored: false }));
|
|
60654
60665
|
const result = reconcile(present, { ...statement, transactions: txns });
|
|
60655
60666
|
const fresh = result.unmatched;
|
|
@@ -60690,7 +60701,7 @@ async function runStatementImport(deps, args) {
|
|
|
60690
60701
|
if (fresh.length === 0)
|
|
60691
60702
|
return { ...summary, dryRun: false, imported: 0 };
|
|
60692
60703
|
const savings = account.type !== "credit_card";
|
|
60693
|
-
const imported = await importTransactions(client,
|
|
60704
|
+
const imported = await importTransactions(client, crypto4, {
|
|
60694
60705
|
onProgress: deps.onProgress,
|
|
60695
60706
|
accountId: account.id,
|
|
60696
60707
|
source: savings ? "bank_statement" : "cc_statement",
|
|
@@ -60703,7 +60714,7 @@ async function runStatementImport(deps, args) {
|
|
|
60703
60714
|
referenceNumber: t2.referenceNumber ?? undefined
|
|
60704
60715
|
}))
|
|
60705
60716
|
});
|
|
60706
|
-
const rescan = await runRescan(client,
|
|
60717
|
+
const rescan = await runRescan(client, crypto4);
|
|
60707
60718
|
return { ...summary, dryRun: false, ...imported, rescan };
|
|
60708
60719
|
}
|
|
60709
60720
|
|
|
@@ -60739,16 +60750,16 @@ var statementSource = exports_external.enum([
|
|
|
60739
60750
|
"ai_import"
|
|
60740
60751
|
]);
|
|
60741
60752
|
var granularity = exports_external.number().int().min(1).max(5).describe("1=bank, 2=CC, 3=manual, 4=app export, 5=telegram/AI");
|
|
60742
|
-
function statementImportTools(client,
|
|
60753
|
+
function statementImportTools(client, crypto4) {
|
|
60743
60754
|
const pdf = (confirm2) => variant(pdfInput, (args, ctx) => runStatementImport({
|
|
60744
60755
|
client,
|
|
60745
|
-
crypto:
|
|
60756
|
+
crypto: crypto4,
|
|
60746
60757
|
readFile: readFileAsArrayBuffer,
|
|
60747
60758
|
parsers: realParsers,
|
|
60748
60759
|
onProgress: progressFor(ctx)
|
|
60749
60760
|
}, confirm2 ? { ...args, confirm: true } : args));
|
|
60750
60761
|
const csvParse = variant(exports_external.object({ filePath: exports_external.string() }), async ({ filePath }) => {
|
|
60751
|
-
if (
|
|
60762
|
+
if (crypto4)
|
|
60752
60763
|
return CSV_PRIVATE_MODE_ERROR;
|
|
60753
60764
|
const buf = await readFile2(filePath);
|
|
60754
60765
|
const form = new FormData;
|
|
@@ -60783,7 +60794,7 @@ function statementImportTools(client, crypto3) {
|
|
|
60783
60794
|
skipDuplicates: exports_external.boolean().optional(),
|
|
60784
60795
|
force: exports_external.boolean().optional()
|
|
60785
60796
|
}), async ({ filePath, mapping, accountId, owner, skipDuplicates, force }) => {
|
|
60786
|
-
if (
|
|
60797
|
+
if (crypto4)
|
|
60787
60798
|
return CSV_PRIVATE_MODE_ERROR;
|
|
60788
60799
|
const csvText = await readFile2(filePath, "utf8");
|
|
60789
60800
|
const data = await client.post("/api/transactions/import/confirm", {
|
|
@@ -60808,14 +60819,14 @@ function statementImportTools(client, crypto3) {
|
|
|
60808
60819
|
referenceNumber: exports_external.string().optional()
|
|
60809
60820
|
}))
|
|
60810
60821
|
}), async ({ accountId, source, granularity: granularity2, transactions: txns }, ctx) => {
|
|
60811
|
-
const data = await importTransactions(client,
|
|
60822
|
+
const data = await importTransactions(client, crypto4, {
|
|
60812
60823
|
onProgress: progressFor(ctx),
|
|
60813
60824
|
accountId,
|
|
60814
60825
|
source,
|
|
60815
60826
|
granularity: granularity2,
|
|
60816
60827
|
txns
|
|
60817
60828
|
});
|
|
60818
|
-
const rescan = await runRescan(client,
|
|
60829
|
+
const rescan = await runRescan(client, crypto4);
|
|
60819
60830
|
return { import: data, rescan };
|
|
60820
60831
|
}),
|
|
60821
60832
|
json_file: variant(exports_external.object({
|
|
@@ -60839,14 +60850,14 @@ function statementImportTools(client, crypto3) {
|
|
|
60839
60850
|
} catch (e2) {
|
|
60840
60851
|
throw new Error(`Failed to read/parse ${filePath}: ${e2 instanceof Error ? e2.message : String(e2)}`);
|
|
60841
60852
|
}
|
|
60842
|
-
const data = await importTransactions(client,
|
|
60853
|
+
const data = await importTransactions(client, crypto4, {
|
|
60843
60854
|
onProgress: progressFor(ctx),
|
|
60844
60855
|
accountId,
|
|
60845
60856
|
source,
|
|
60846
60857
|
granularity: granularity2,
|
|
60847
60858
|
txns
|
|
60848
60859
|
});
|
|
60849
|
-
const rescan = await runRescan(client,
|
|
60860
|
+
const rescan = await runRescan(client, crypto4);
|
|
60850
60861
|
return { import: data, count: txns.length, rescan };
|
|
60851
60862
|
})
|
|
60852
60863
|
}
|
|
@@ -61003,7 +61014,7 @@ function registerResources2(server2, tools) {
|
|
|
61003
61014
|
// package.json
|
|
61004
61015
|
var package_default = {
|
|
61005
61016
|
name: "paisa-mcp",
|
|
61006
|
-
version: "0.4.
|
|
61017
|
+
version: "0.4.3",
|
|
61007
61018
|
repository: {
|
|
61008
61019
|
type: "git",
|
|
61009
61020
|
url: "git+https://github.com/nimit9/paisa.git",
|
|
@@ -61033,13 +61044,12 @@ var package_default = {
|
|
|
61033
61044
|
"@nimit9/signet-ai": "^0.1.11",
|
|
61034
61045
|
"@nimit9/signet-lib": "^0.1.15",
|
|
61035
61046
|
"@nimit9/signet-server": "^0.2.7",
|
|
61047
|
+
"@paisa/crypto": "workspace:*",
|
|
61036
61048
|
"@paisa/parsers": "workspace:*",
|
|
61037
61049
|
"@paisa/reconciliation": "workspace:*",
|
|
61038
61050
|
"@paisa/types": "workspace:*",
|
|
61039
|
-
"@scure/bip39": "^2.2.0",
|
|
61040
61051
|
"@types/bun": "^1.3.14",
|
|
61041
61052
|
axios: "^1.16.1",
|
|
61042
|
-
"hash-wasm": "^4.12.0",
|
|
61043
61053
|
typescript: "^7.0.2",
|
|
61044
61054
|
zod: "^4.6.5"
|
|
61045
61055
|
},
|
|
@@ -61059,8 +61069,8 @@ function paisaErrorResult(err) {
|
|
|
61059
61069
|
});
|
|
61060
61070
|
}
|
|
61061
61071
|
var CONFIRM_KEY2 = randomBytes(32);
|
|
61062
|
-
function buildServer(client,
|
|
61063
|
-
const tools = applyAnnotations(REGISTRATIONS.flatMap((group) => group.tools(client,
|
|
61072
|
+
function buildServer(client, crypto4) {
|
|
61073
|
+
const tools = applyAnnotations(REGISTRATIONS.flatMap((group) => group.tools(client, crypto4)).map((t2) => {
|
|
61064
61074
|
const describe3 = CONFIRMED_TOOLS[t2.name];
|
|
61065
61075
|
if (describe3)
|
|
61066
61076
|
return withConfirmation(t2, describe3);
|
|
@@ -61103,5 +61113,5 @@ main().catch((err) => {
|
|
|
61103
61113
|
process.exit(1);
|
|
61104
61114
|
});
|
|
61105
61115
|
|
|
61106
|
-
//# debugId=
|
|
61116
|
+
//# debugId=1892106D83105DD764756E2164756E21
|
|
61107
61117
|
//# sourceMappingURL=index.js.map
|