paisa-mcp 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +396 -102
- package/dist/index.js.map +16 -14
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -38033,6 +38033,44 @@ var inputRequired = Object.assign(buildInputRequired, {
|
|
|
38033
38033
|
return { method: "roots/list" };
|
|
38034
38034
|
}
|
|
38035
38035
|
});
|
|
38036
|
+
function acceptedContent(responses, key, schema) {
|
|
38037
|
+
const view = inputResponse(responses, key);
|
|
38038
|
+
if (view.kind !== "elicit" || view.action !== "accept" || view.content === undefined)
|
|
38039
|
+
return;
|
|
38040
|
+
if (schema === undefined)
|
|
38041
|
+
return view.content;
|
|
38042
|
+
const outcome = schema["~standard"].validate(view.content);
|
|
38043
|
+
if (outcome instanceof Promise)
|
|
38044
|
+
throw new TypeError("acceptedContent(responses, key, schema) requires a synchronously-validating schema");
|
|
38045
|
+
return outcome.issues === undefined ? outcome.value : undefined;
|
|
38046
|
+
}
|
|
38047
|
+
function inputResponse(responses, key) {
|
|
38048
|
+
if (responses === undefined || typeof responses !== "object" || responses === null)
|
|
38049
|
+
return { kind: "missing" };
|
|
38050
|
+
const entry = responses[key];
|
|
38051
|
+
if (entry === null || typeof entry !== "object" || Array.isArray(entry))
|
|
38052
|
+
return { kind: "missing" };
|
|
38053
|
+
const candidate = entry;
|
|
38054
|
+
if (candidate["action"] === "accept" || candidate["action"] === "decline" || candidate["action"] === "cancel") {
|
|
38055
|
+
const content = candidate["content"];
|
|
38056
|
+
return {
|
|
38057
|
+
kind: "elicit",
|
|
38058
|
+
action: candidate["action"],
|
|
38059
|
+
...content !== null && typeof content === "object" && !Array.isArray(content) && { content }
|
|
38060
|
+
};
|
|
38061
|
+
}
|
|
38062
|
+
if (Array.isArray(candidate["roots"]))
|
|
38063
|
+
return {
|
|
38064
|
+
kind: "roots",
|
|
38065
|
+
roots: candidate["roots"]
|
|
38066
|
+
};
|
|
38067
|
+
if (typeof candidate["role"] === "string" && candidate["content"] !== undefined)
|
|
38068
|
+
return {
|
|
38069
|
+
kind: "sampling",
|
|
38070
|
+
result: candidate
|
|
38071
|
+
};
|
|
38072
|
+
return { kind: "missing" };
|
|
38073
|
+
}
|
|
38036
38074
|
var REQUEST_STATE_ONLY_LEG_PACING_MS = 250;
|
|
38037
38075
|
function inputRequiredRoundsExceededMessage(method, maxRounds) {
|
|
38038
38076
|
return `Multi-round-trip request '${method}' still required input after ${maxRounds} rounds (inputRequired.maxRounds)`;
|
|
@@ -53145,6 +53183,90 @@ class PaisaApiClient {
|
|
|
53145
53183
|
}
|
|
53146
53184
|
}
|
|
53147
53185
|
|
|
53186
|
+
// ../../node_modules/.bun/@nimit9+signet-lib@0.1.15+d40099021b198cb0/node_modules/@nimit9/signet-lib/dist/crypto/index.js
|
|
53187
|
+
var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
53188
|
+
async function decryptFields(input2, paths, { decrypt, isCiphertext }) {
|
|
53189
|
+
const undecrypted = [];
|
|
53190
|
+
const pending = [];
|
|
53191
|
+
const looksEncrypted = (value, path) => {
|
|
53192
|
+
if (!isCiphertext)
|
|
53193
|
+
return true;
|
|
53194
|
+
try {
|
|
53195
|
+
return isCiphertext(value, path) !== false;
|
|
53196
|
+
} catch {
|
|
53197
|
+
return true;
|
|
53198
|
+
}
|
|
53199
|
+
};
|
|
53200
|
+
const decryptInto = (target, key, value, path) => {
|
|
53201
|
+
pending.push((async () => {
|
|
53202
|
+
try {
|
|
53203
|
+
const plain = await decrypt(value, path);
|
|
53204
|
+
if (typeof plain !== "string")
|
|
53205
|
+
throw new TypeError("decrypt returned a non-string");
|
|
53206
|
+
target[key] = plain;
|
|
53207
|
+
} catch {
|
|
53208
|
+
target[key] = null;
|
|
53209
|
+
undecrypted.push(path);
|
|
53210
|
+
}
|
|
53211
|
+
})());
|
|
53212
|
+
};
|
|
53213
|
+
const copies = new Map;
|
|
53214
|
+
const mine = new WeakSet;
|
|
53215
|
+
const copyOf = (v) => {
|
|
53216
|
+
if (mine.has(v))
|
|
53217
|
+
return v;
|
|
53218
|
+
let c = copies.get(v);
|
|
53219
|
+
if (!c) {
|
|
53220
|
+
c = Array.isArray(v) ? [...v] : { ...v };
|
|
53221
|
+
copies.set(v, c);
|
|
53222
|
+
mine.add(c);
|
|
53223
|
+
}
|
|
53224
|
+
return c;
|
|
53225
|
+
};
|
|
53226
|
+
const walk = (node2, keys, at, set2) => {
|
|
53227
|
+
if (Array.isArray(node2)) {
|
|
53228
|
+
const arr = copyOf(node2);
|
|
53229
|
+
set2(arr);
|
|
53230
|
+
arr.forEach((item, i) => {
|
|
53231
|
+
walk(item, keys, at ? `${at}.${i}` : String(i), (v) => {
|
|
53232
|
+
arr[i] = v;
|
|
53233
|
+
});
|
|
53234
|
+
});
|
|
53235
|
+
return;
|
|
53236
|
+
}
|
|
53237
|
+
if (!isObj(node2) || keys.length === 0)
|
|
53238
|
+
return;
|
|
53239
|
+
const [key, ...rest] = keys;
|
|
53240
|
+
if (!Object.hasOwn(node2, key))
|
|
53241
|
+
return;
|
|
53242
|
+
const obj = copyOf(node2);
|
|
53243
|
+
set2(obj);
|
|
53244
|
+
const path = at ? `${at}.${key}` : key;
|
|
53245
|
+
const value = obj[key];
|
|
53246
|
+
if (rest.length > 0) {
|
|
53247
|
+
walk(value, rest, path, (v) => {
|
|
53248
|
+
obj[key] = v;
|
|
53249
|
+
});
|
|
53250
|
+
return;
|
|
53251
|
+
}
|
|
53252
|
+
if (typeof value === "string" && value !== "" && looksEncrypted(value, path)) {
|
|
53253
|
+
decryptInto(obj, key, value, path);
|
|
53254
|
+
}
|
|
53255
|
+
};
|
|
53256
|
+
let data = input2;
|
|
53257
|
+
for (const p of paths) {
|
|
53258
|
+
const keys = p.split(".").filter(Boolean);
|
|
53259
|
+
if (keys.length === 0)
|
|
53260
|
+
continue;
|
|
53261
|
+
walk(data, keys, "", (v) => {
|
|
53262
|
+
data = v;
|
|
53263
|
+
});
|
|
53264
|
+
}
|
|
53265
|
+
await Promise.all(pending);
|
|
53266
|
+
undecrypted.sort();
|
|
53267
|
+
return { data, undecrypted };
|
|
53268
|
+
}
|
|
53269
|
+
|
|
53148
53270
|
// src/crypto.ts
|
|
53149
53271
|
var import_hash_wasm = __toESM(require_index_umd(), 1);
|
|
53150
53272
|
var ENCRYPTED_TXN_FIELDS = ["description", "referenceNumber", "notes"];
|
|
@@ -53207,16 +53329,6 @@ async function decryptField(encrypted, key) {
|
|
|
53207
53329
|
const plaintext = await crypto.subtle.decrypt({ name: "AES-GCM", iv }, key, ciphertext);
|
|
53208
53330
|
return new TextDecoder().decode(plaintext);
|
|
53209
53331
|
}
|
|
53210
|
-
async function decryptObjectFields(obj, fields, key) {
|
|
53211
|
-
for (const field of fields) {
|
|
53212
|
-
const val = obj[field];
|
|
53213
|
-
if (typeof val !== "string" || !val)
|
|
53214
|
-
continue;
|
|
53215
|
-
try {
|
|
53216
|
-
obj[field] = await decryptField(val, key);
|
|
53217
|
-
} catch {}
|
|
53218
|
-
}
|
|
53219
|
-
}
|
|
53220
53332
|
async function encryptObjectFields(obj, fields, key) {
|
|
53221
53333
|
for (const field of fields) {
|
|
53222
53334
|
const val = obj[field];
|
|
@@ -53225,25 +53337,55 @@ async function encryptObjectFields(obj, fields, key) {
|
|
|
53225
53337
|
obj[field] = await encryptField(val, key);
|
|
53226
53338
|
}
|
|
53227
53339
|
}
|
|
53340
|
+
var CIPHERTEXT_CHARSET_RE = /^[A-Za-z0-9+/]+={0,2}$/;
|
|
53341
|
+
var MIN_CIPHERTEXT_B64_LEN = 40;
|
|
53342
|
+
function looksLikeCiphertext(value) {
|
|
53343
|
+
return value.length >= MIN_CIPHERTEXT_B64_LEN && CIPHERTEXT_CHARSET_RE.test(value);
|
|
53344
|
+
}
|
|
53345
|
+
async function decryptFieldsFailClosed(input2, paths, key) {
|
|
53346
|
+
return decryptFields(input2, paths, {
|
|
53347
|
+
decrypt: (ciphertext) => decryptField(ciphertext, key),
|
|
53348
|
+
isCiphertext: (value) => looksLikeCiphertext(value)
|
|
53349
|
+
});
|
|
53350
|
+
}
|
|
53351
|
+
var TXN_DECRYPT_PATHS = [
|
|
53352
|
+
...ENCRYPTED_TXN_FIELDS,
|
|
53353
|
+
...ENCRYPTED_MERCHANT_FIELDS.map((f) => `merchant.${f}`)
|
|
53354
|
+
];
|
|
53228
53355
|
async function decryptTransactionFields(items, key) {
|
|
53229
|
-
|
|
53230
|
-
await decryptObjectFields(item, ENCRYPTED_TXN_FIELDS, key);
|
|
53231
|
-
const merchant = item.merchant;
|
|
53232
|
-
if (merchant && typeof merchant === "object") {
|
|
53233
|
-
await decryptObjectFields(merchant, ENCRYPTED_MERCHANT_FIELDS, key);
|
|
53234
|
-
}
|
|
53235
|
-
}
|
|
53356
|
+
return decryptFieldsFailClosed(items, TXN_DECRYPT_PATHS, key);
|
|
53236
53357
|
}
|
|
53237
53358
|
async function decryptMerchantFields(items, key) {
|
|
53238
|
-
|
|
53239
|
-
await decryptObjectFields(item, ENCRYPTED_MERCHANT_FIELDS, key);
|
|
53240
|
-
}
|
|
53359
|
+
return decryptFieldsFailClosed(items, ENCRYPTED_MERCHANT_FIELDS, key);
|
|
53241
53360
|
}
|
|
53242
53361
|
async function encryptTransactionFields(items, key) {
|
|
53243
53362
|
for (const item of items) {
|
|
53244
53363
|
await encryptObjectFields(item, ENCRYPTED_TXN_FIELDS, key);
|
|
53245
53364
|
}
|
|
53246
53365
|
}
|
|
53366
|
+
async function decryptPersonCiphers(input2, key) {
|
|
53367
|
+
const { data, undecrypted } = await decryptFields(input2, ["cipher"], {
|
|
53368
|
+
decrypt: async (ciphertext) => {
|
|
53369
|
+
const json2 = await decryptField(ciphertext, key);
|
|
53370
|
+
JSON.parse(json2);
|
|
53371
|
+
return json2;
|
|
53372
|
+
}
|
|
53373
|
+
});
|
|
53374
|
+
const merge3 = (row) => {
|
|
53375
|
+
if (!row || typeof row !== "object")
|
|
53376
|
+
return;
|
|
53377
|
+
const r = row;
|
|
53378
|
+
if (typeof r.cipher !== "string")
|
|
53379
|
+
return;
|
|
53380
|
+
const fields = JSON.parse(r.cipher);
|
|
53381
|
+
Object.assign(r, fields, { cipher: null });
|
|
53382
|
+
};
|
|
53383
|
+
if (Array.isArray(data))
|
|
53384
|
+
data.forEach(merge3);
|
|
53385
|
+
else
|
|
53386
|
+
merge3(data);
|
|
53387
|
+
return { data, undecrypted };
|
|
53388
|
+
}
|
|
53247
53389
|
var VERIFIER_PLAINTEXT = "paisa-verify-v1";
|
|
53248
53390
|
async function checkVerifier(key, encryptedVerifier) {
|
|
53249
53391
|
try {
|
|
@@ -53552,6 +53694,145 @@ function applyAnnotations(tools) {
|
|
|
53552
53694
|
return { ...tool, title: meta3.title, annotations: meta3.annotations };
|
|
53553
53695
|
});
|
|
53554
53696
|
}
|
|
53697
|
+
|
|
53698
|
+
// src/lib/confirm.ts
|
|
53699
|
+
import { createHash, createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
53700
|
+
var ANSWER_KEY = "confirm";
|
|
53701
|
+
function clientCanConfirm(mcp, initializeCapabilities) {
|
|
53702
|
+
const envelope = mcp.mcpReq.envelope;
|
|
53703
|
+
const caps = envelope?.[CLIENT_CAPABILITIES_META_KEY] ?? initializeCapabilities();
|
|
53704
|
+
return Boolean(caps?.elicitation);
|
|
53705
|
+
}
|
|
53706
|
+
function planFingerprint(plan) {
|
|
53707
|
+
const canonical = (v) => Array.isArray(v) ? v.map(canonical) : v && typeof v === "object" ? Object.fromEntries(Object.entries(v).filter(([, x]) => x !== undefined).sort(([a], [b]) => a.localeCompare(b)).map(([k, x]) => [k, canonical(x)])) : v;
|
|
53708
|
+
return createHash("sha256").update(JSON.stringify(canonical(plan))).digest("hex");
|
|
53709
|
+
}
|
|
53710
|
+
var STATE_KEY = randomBytes(32);
|
|
53711
|
+
function sign(nonce, fingerprint) {
|
|
53712
|
+
return createHmac("sha256", STATE_KEY).update(`${nonce}.${fingerprint}`).digest("hex");
|
|
53713
|
+
}
|
|
53714
|
+
function mintState(fingerprint) {
|
|
53715
|
+
const nonce = randomBytes(16).toString("hex");
|
|
53716
|
+
return `${nonce}.${sign(nonce, fingerprint)}`;
|
|
53717
|
+
}
|
|
53718
|
+
function verifyState(state, fingerprint) {
|
|
53719
|
+
if (typeof state !== "string")
|
|
53720
|
+
return false;
|
|
53721
|
+
const [nonce, mac3, ...rest] = state.split(".");
|
|
53722
|
+
if (!nonce || !mac3 || rest.length)
|
|
53723
|
+
return false;
|
|
53724
|
+
const expected = Buffer.from(sign(nonce, fingerprint), "hex");
|
|
53725
|
+
const given = Buffer.from(mac3, "hex");
|
|
53726
|
+
return given.length === expected.length && timingSafeEqual(given, expected);
|
|
53727
|
+
}
|
|
53728
|
+
function confirmPlan(mcp, message, plan, initializeCapabilities = () => {
|
|
53729
|
+
return;
|
|
53730
|
+
}) {
|
|
53731
|
+
if (!clientCanConfirm(mcp, initializeCapabilities))
|
|
53732
|
+
return { kind: "unsupported" };
|
|
53733
|
+
const fingerprint = planFingerprint(plan);
|
|
53734
|
+
const responses = mcp.mcpReq.inputResponses;
|
|
53735
|
+
const answered = responses !== undefined && ANSWER_KEY in responses;
|
|
53736
|
+
const echoed = mcp.mcpReq.requestState?.();
|
|
53737
|
+
if (answered && verifyState(echoed, fingerprint)) {
|
|
53738
|
+
const content = acceptedContent(responses, ANSWER_KEY);
|
|
53739
|
+
if (content?.confirm === true)
|
|
53740
|
+
return { kind: "confirmed" };
|
|
53741
|
+
return {
|
|
53742
|
+
kind: "declined",
|
|
53743
|
+
result: { cancelled: true, message: "Cancelled by the user. Nothing was changed." }
|
|
53744
|
+
};
|
|
53745
|
+
}
|
|
53746
|
+
return {
|
|
53747
|
+
kind: "ask",
|
|
53748
|
+
result: inputRequired({
|
|
53749
|
+
inputRequests: {
|
|
53750
|
+
[ANSWER_KEY]: inputRequired.elicit({
|
|
53751
|
+
message,
|
|
53752
|
+
requestedSchema: {
|
|
53753
|
+
type: "object",
|
|
53754
|
+
properties: {
|
|
53755
|
+
confirm: { type: "boolean", title: "Confirm", description: "This can't be undone." }
|
|
53756
|
+
},
|
|
53757
|
+
required: ["confirm"]
|
|
53758
|
+
}
|
|
53759
|
+
})
|
|
53760
|
+
},
|
|
53761
|
+
requestState: mintState(fingerprint)
|
|
53762
|
+
})
|
|
53763
|
+
};
|
|
53764
|
+
}
|
|
53765
|
+
function withConfirmation(tool, describe3, initializeCapabilities) {
|
|
53766
|
+
const run = tool.handler;
|
|
53767
|
+
return {
|
|
53768
|
+
...tool,
|
|
53769
|
+
handler: (args, ctx) => {
|
|
53770
|
+
const message = describe3(args);
|
|
53771
|
+
if (message === null || !ctx?.mcp)
|
|
53772
|
+
return run(args, ctx);
|
|
53773
|
+
const outcome = confirmPlan(ctx.mcp, message, { tool: tool.name, args }, initializeCapabilities);
|
|
53774
|
+
if (outcome.kind === "confirmed" || outcome.kind === "unsupported")
|
|
53775
|
+
return run(args, ctx);
|
|
53776
|
+
return outcome.result;
|
|
53777
|
+
}
|
|
53778
|
+
};
|
|
53779
|
+
}
|
|
53780
|
+
var safeDate = (v) => typeof v === "string" && /^\d{4}-\d{2}-\d{2}$/.test(v) ? v : v === undefined ? "any" : "a date";
|
|
53781
|
+
var safeAmount = (v) => typeof v === "string" && /^\d+(\.\d{1,2})?$/.test(v) || typeof v === "number" ? String(v) : null;
|
|
53782
|
+
var plural2 = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
|
|
53783
|
+
function describeTransactionFilters(f) {
|
|
53784
|
+
const parts = [];
|
|
53785
|
+
if (f.startDate || f.endDate)
|
|
53786
|
+
parts.push(`dated ${safeDate(f.startDate)} to ${safeDate(f.endDate)}`);
|
|
53787
|
+
if (f.type)
|
|
53788
|
+
parts.push(`${f.type}s only`);
|
|
53789
|
+
if (f.categoryType)
|
|
53790
|
+
parts.push(`category type ${f.categoryType}`);
|
|
53791
|
+
if (f.categoryId)
|
|
53792
|
+
parts.push("in one category");
|
|
53793
|
+
if (f.uncategorized)
|
|
53794
|
+
parts.push("uncategorized");
|
|
53795
|
+
if (f.amountMin !== undefined || f.amountMax !== undefined)
|
|
53796
|
+
parts.push(`amount ₹${f.amountMin ?? 0} to ${f.amountMax === undefined ? "any" : `₹${f.amountMax}`}`);
|
|
53797
|
+
if (f.owner)
|
|
53798
|
+
parts.push("one owner");
|
|
53799
|
+
if (f.search)
|
|
53800
|
+
parts.push("matching a text search");
|
|
53801
|
+
return parts.length ? parts.join(", ") : "with no filters (ALL transactions)";
|
|
53802
|
+
}
|
|
53803
|
+
var describeDeletes = {
|
|
53804
|
+
transactions: (a) => {
|
|
53805
|
+
if (a.id)
|
|
53806
|
+
return "Permanently delete 1 transaction?";
|
|
53807
|
+
if (Array.isArray(a.ids))
|
|
53808
|
+
return `Permanently delete ${plural2(a.ids.length, "transaction")}?`;
|
|
53809
|
+
return `Permanently delete every transaction ${describeTransactionFilters(a.filters ?? {})}?`;
|
|
53810
|
+
},
|
|
53811
|
+
entity: (a) => {
|
|
53812
|
+
const entity = String(a.entity ?? "item").replace(/_/g, " ");
|
|
53813
|
+
const n = Array.isArray(a.ids) ? a.ids.length : 1;
|
|
53814
|
+
const extra = a.entity === "merchant" ? " Their transactions are kept but unlinked." : a.entity === "debt" ? " Its history is lost; settling keeps it." : "";
|
|
53815
|
+
return `Permanently delete ${plural2(n, entity)}?${extra}`;
|
|
53816
|
+
},
|
|
53817
|
+
mergeMerchants: (a) => {
|
|
53818
|
+
const n = Array.isArray(a.duplicateIds) ? a.duplicateIds.length : 0;
|
|
53819
|
+
return `Merge ${plural2(n, "duplicate merchant")} into the one you're keeping? Their transactions move over and the duplicates are deleted.`;
|
|
53820
|
+
},
|
|
53821
|
+
encryptionMode: (a) => `Switch encryption mode to ${a.encryptionMode === "private" ? "private" : "standard"}?`,
|
|
53822
|
+
settleDebt: (a) => {
|
|
53823
|
+
if (a.action !== "settle")
|
|
53824
|
+
return null;
|
|
53825
|
+
const amount = a.settledAmount === undefined ? "0" : safeAmount(a.settledAmount);
|
|
53826
|
+
return amount === null ? "Settle this debt? It will be closed with the remaining balance you gave." : `Settle this debt? It will be closed with a remaining balance of ₹${amount}.`;
|
|
53827
|
+
}
|
|
53828
|
+
};
|
|
53829
|
+
var CONFIRMED_TOOLS = {
|
|
53830
|
+
delete_transactions: describeDeletes.transactions,
|
|
53831
|
+
delete_entity: describeDeletes.entity,
|
|
53832
|
+
merge_merchants: describeDeletes.mergeMerchants,
|
|
53833
|
+
set_encryption_mode: describeDeletes.encryptionMode,
|
|
53834
|
+
manage_debt: describeDeletes.settleDebt
|
|
53835
|
+
};
|
|
53555
53836
|
// src/lib/union-tool.ts
|
|
53556
53837
|
function variant(input2, run) {
|
|
53557
53838
|
return { input: input2, run };
|
|
@@ -53862,7 +54143,8 @@ var dashboardOutputSchema = exports_external.object({
|
|
|
53862
54143
|
recentTransactions: exports_external.array(exports_external.record(exports_external.string(), exports_external.unknown())),
|
|
53863
54144
|
netWorth: exports_external.record(exports_external.string(), exports_external.unknown()).nullable(),
|
|
53864
54145
|
debtSummary: exports_external.object({ totalOutstanding: exports_external.number(), totalEmi: exports_external.number(), count: exports_external.number() }).nullable(),
|
|
53865
|
-
budgetVsActual: exports_external.array(budgetVsActualEntrySchema)
|
|
54146
|
+
budgetVsActual: exports_external.array(budgetVsActualEntrySchema),
|
|
54147
|
+
undecrypted: exports_external.array(exports_external.string()).optional()
|
|
53866
54148
|
});
|
|
53867
54149
|
var spendingTrendsSchema = exports_external.object({
|
|
53868
54150
|
success: exports_external.literal(true),
|
|
@@ -53897,7 +54179,8 @@ var spendingTrendsSchema = exports_external.object({
|
|
|
53897
54179
|
total: exports_external.number(),
|
|
53898
54180
|
count: exports_external.number()
|
|
53899
54181
|
})),
|
|
53900
|
-
budgetVsActual: exports_external.array(budgetVsActualEntrySchema)
|
|
54182
|
+
budgetVsActual: exports_external.array(budgetVsActualEntrySchema),
|
|
54183
|
+
undecrypted: exports_external.array(exports_external.string()).optional()
|
|
53901
54184
|
});
|
|
53902
54185
|
var categoryAnalyticsSchema = exports_external.object({
|
|
53903
54186
|
success: exports_external.literal(true),
|
|
@@ -53912,7 +54195,7 @@ var categoryAnalyticsSchema = exports_external.object({
|
|
|
53912
54195
|
recentTransactions: exports_external.array(exports_external.object({
|
|
53913
54196
|
id: exports_external.string(),
|
|
53914
54197
|
date: exports_external.string(),
|
|
53915
|
-
description: exports_external.string(),
|
|
54198
|
+
description: exports_external.string().nullable(),
|
|
53916
54199
|
amount: exports_external.number(),
|
|
53917
54200
|
type: exports_external.string(),
|
|
53918
54201
|
owner: exports_external.string(),
|
|
@@ -53926,7 +54209,8 @@ var categoryAnalyticsSchema = exports_external.object({
|
|
|
53926
54209
|
total: exports_external.number(),
|
|
53927
54210
|
count: exports_external.number()
|
|
53928
54211
|
})),
|
|
53929
|
-
stats: exports_external.object({ total: exports_external.number(), avgMonthly: exports_external.number(), count: exports_external.number() })
|
|
54212
|
+
stats: exports_external.object({ total: exports_external.number(), avgMonthly: exports_external.number(), count: exports_external.number() }),
|
|
54213
|
+
undecrypted: exports_external.array(exports_external.string()).optional()
|
|
53930
54214
|
});
|
|
53931
54215
|
var merchantAnalyticsSchema = exports_external.object({
|
|
53932
54216
|
success: exports_external.literal(true),
|
|
@@ -53941,7 +54225,7 @@ var merchantAnalyticsSchema = exports_external.object({
|
|
|
53941
54225
|
recentTransactions: exports_external.array(exports_external.object({
|
|
53942
54226
|
id: exports_external.string(),
|
|
53943
54227
|
date: exports_external.string(),
|
|
53944
|
-
description: exports_external.string(),
|
|
54228
|
+
description: exports_external.string().nullable(),
|
|
53945
54229
|
amount: exports_external.number(),
|
|
53946
54230
|
type: exports_external.string(),
|
|
53947
54231
|
owner: exports_external.string(),
|
|
@@ -53956,7 +54240,8 @@ var merchantAnalyticsSchema = exports_external.object({
|
|
|
53956
54240
|
total: exports_external.number(),
|
|
53957
54241
|
count: exports_external.number()
|
|
53958
54242
|
})),
|
|
53959
|
-
stats: exports_external.object({ total: exports_external.number(), avgMonthly: exports_external.number(), count: exports_external.number() })
|
|
54243
|
+
stats: exports_external.object({ total: exports_external.number(), avgMonthly: exports_external.number(), count: exports_external.number() }),
|
|
54244
|
+
undecrypted: exports_external.array(exports_external.string()).optional()
|
|
53960
54245
|
});
|
|
53961
54246
|
var analyticsOutputSchema = exports_external.union([
|
|
53962
54247
|
spendingTrendsSchema,
|
|
@@ -53965,37 +54250,17 @@ var analyticsOutputSchema = exports_external.union([
|
|
|
53965
54250
|
]);
|
|
53966
54251
|
|
|
53967
54252
|
// src/tools/analytics.ts
|
|
53968
|
-
async function dec(value, key) {
|
|
53969
|
-
if (typeof value !== "string" || !value)
|
|
53970
|
-
return value;
|
|
53971
|
-
try {
|
|
53972
|
-
return await decryptField(value, key);
|
|
53973
|
-
} catch {
|
|
53974
|
-
return value;
|
|
53975
|
-
}
|
|
53976
|
-
}
|
|
53977
54253
|
async function decryptAnalytics(data, key) {
|
|
53978
54254
|
if (!data || typeof data !== "object")
|
|
53979
|
-
return;
|
|
53980
|
-
const
|
|
53981
|
-
|
|
53982
|
-
|
|
53983
|
-
|
|
53984
|
-
|
|
53985
|
-
|
|
53986
|
-
|
|
53987
|
-
|
|
53988
|
-
for (const t of recent) {
|
|
53989
|
-
t.description = await dec(t.description, key);
|
|
53990
|
-
if ("merchantName" in t)
|
|
53991
|
-
t.merchantName = await dec(t.merchantName, key);
|
|
53992
|
-
}
|
|
53993
|
-
}
|
|
53994
|
-
const merchant = d.merchant;
|
|
53995
|
-
if (merchant && typeof merchant === "object") {
|
|
53996
|
-
merchant.cleanName = await dec(merchant.cleanName, key);
|
|
53997
|
-
merchant.rawId = await dec(merchant.rawId, key);
|
|
53998
|
-
}
|
|
54255
|
+
return data;
|
|
54256
|
+
const { data: decrypted, undecrypted } = await decryptFieldsFailClosed(data, [
|
|
54257
|
+
"topMerchants.name",
|
|
54258
|
+
"recentTransactions.description",
|
|
54259
|
+
"recentTransactions.merchantName",
|
|
54260
|
+
"merchant.cleanName",
|
|
54261
|
+
"merchant.rawId"
|
|
54262
|
+
], key);
|
|
54263
|
+
return undecrypted.length ? { ...decrypted, undecrypted } : decrypted;
|
|
53999
54264
|
}
|
|
54000
54265
|
function analyticsTools(client, crypto3) {
|
|
54001
54266
|
return [
|
|
@@ -54023,7 +54288,7 @@ function analyticsTools(client, crypto3) {
|
|
|
54023
54288
|
break;
|
|
54024
54289
|
}
|
|
54025
54290
|
if (crypto3)
|
|
54026
|
-
await decryptAnalytics(data, crypto3.key);
|
|
54291
|
+
data = await decryptAnalytics(data, crypto3.key);
|
|
54027
54292
|
return data;
|
|
54028
54293
|
}
|
|
54029
54294
|
})
|
|
@@ -54091,7 +54356,10 @@ function dashboardTools(client, crypto3) {
|
|
|
54091
54356
|
handler: async (params) => {
|
|
54092
54357
|
const data = await client.get("/api/dashboard", params.months ? { months: params.months } : undefined);
|
|
54093
54358
|
if (crypto3 && Array.isArray(data?.recentTransactions)) {
|
|
54094
|
-
await decryptTransactionFields(data.recentTransactions, crypto3.key);
|
|
54359
|
+
const { data: decrypted, undecrypted } = await decryptTransactionFields(data.recentTransactions, crypto3.key);
|
|
54360
|
+
data.recentTransactions = decrypted;
|
|
54361
|
+
if (undecrypted.length)
|
|
54362
|
+
return { ...data, undecrypted };
|
|
54095
54363
|
}
|
|
54096
54364
|
return data;
|
|
54097
54365
|
}
|
|
@@ -54643,13 +54911,17 @@ function merchantVariants(client, crypto3) {
|
|
|
54643
54911
|
return {
|
|
54644
54912
|
list: variant(exports_external.object({ rawId: exports_external.string().optional().describe("merchants: find one by UPI ID or name") }), async ({ rawId }) => {
|
|
54645
54913
|
const data = await client.get("/api/merchants");
|
|
54646
|
-
|
|
54647
|
-
|
|
54914
|
+
let undecrypted = [];
|
|
54915
|
+
if (crypto3 && data.data) {
|
|
54916
|
+
const dec = await decryptMerchantFields(data.data, crypto3.key);
|
|
54917
|
+
data.data = dec.data;
|
|
54918
|
+
undecrypted = dec.undecrypted;
|
|
54919
|
+
}
|
|
54648
54920
|
if (!rawId)
|
|
54649
|
-
return data;
|
|
54921
|
+
return undecrypted.length ? { ...data, undecrypted } : data;
|
|
54650
54922
|
const needle = rawId.toLowerCase();
|
|
54651
54923
|
const merchants = (data.data ?? []).filter((m) => String(m.rawId ?? "").toLowerCase() === needle || String(m.cleanName ?? "").toLowerCase().includes(needle));
|
|
54652
|
-
return { ...data, data: merchants };
|
|
54924
|
+
return { ...data, data: merchants, ...undecrypted.length ? { undecrypted } : {} };
|
|
54653
54925
|
}),
|
|
54654
54926
|
upsert: variant(exports_external.object({
|
|
54655
54927
|
id: exports_external.string().optional().describe("Merchant UUID — pass to update, omit to create"),
|
|
@@ -54758,22 +55030,19 @@ function personVariants(client, crypto3) {
|
|
|
54758
55030
|
if (id) {
|
|
54759
55031
|
const data2 = await client.get(`/api/persons/${id}`);
|
|
54760
55032
|
if (crypto3 && data2.data.cipher) {
|
|
54761
|
-
const
|
|
54762
|
-
|
|
55033
|
+
const { data: decrypted, undecrypted } = await decryptPersonCiphers(data2.data, crypto3.key);
|
|
55034
|
+
data2.data = decrypted;
|
|
55035
|
+
if (undecrypted.length)
|
|
55036
|
+
return { ...data2, undecrypted };
|
|
54763
55037
|
}
|
|
54764
55038
|
return data2;
|
|
54765
55039
|
}
|
|
54766
55040
|
const data = await client.get("/api/persons");
|
|
54767
55041
|
if (crypto3 && data.data) {
|
|
54768
|
-
|
|
54769
|
-
|
|
54770
|
-
|
|
54771
|
-
|
|
54772
|
-
const fields = await decryptPersonCipher(cipher, crypto3.key);
|
|
54773
|
-
Object.assign(person, fields, { cipher: null });
|
|
54774
|
-
} catch {}
|
|
54775
|
-
}
|
|
54776
|
-
}
|
|
55042
|
+
const { data: decrypted, undecrypted } = await decryptPersonCiphers(data.data, crypto3.key);
|
|
55043
|
+
data.data = decrypted;
|
|
55044
|
+
if (undecrypted.length)
|
|
55045
|
+
return { ...data, undecrypted };
|
|
54777
55046
|
}
|
|
54778
55047
|
return data;
|
|
54779
55048
|
}),
|
|
@@ -58567,9 +58836,8 @@ async function fetchAllRowsPaged(client, crypto3, params = {}, maxPages = 100) {
|
|
|
58567
58836
|
if (cursor)
|
|
58568
58837
|
q2.cursor = cursor;
|
|
58569
58838
|
const res = await client.get("/api/transactions", q2);
|
|
58570
|
-
const
|
|
58571
|
-
|
|
58572
|
-
await decryptTransactionFields(batch, crypto3.key);
|
|
58839
|
+
const batch0 = res.data ?? [];
|
|
58840
|
+
const batch = crypto3 ? (await decryptTransactionFields(batch0, crypto3.key)).data : batch0;
|
|
58573
58841
|
out.push(...batch.map(toTxnRow));
|
|
58574
58842
|
if (!res.hasMore || !res.nextCursor || batch.length === 0 || res.nextCursor === cursor)
|
|
58575
58843
|
break;
|
|
@@ -59034,9 +59302,8 @@ async function runRescan(client, crypto3) {
|
|
|
59034
59302
|
bag = (await loadBag(client)).bag;
|
|
59035
59303
|
} catch {}
|
|
59036
59304
|
const merchantsRes = await client.get("/api/merchants");
|
|
59037
|
-
const
|
|
59038
|
-
|
|
59039
|
-
await decryptMerchantFields(allMerchants, crypto3.key);
|
|
59305
|
+
const allMerchants0 = merchantsRes.data ?? [];
|
|
59306
|
+
const allMerchants = crypto3 ? (await decryptMerchantFields(allMerchants0, crypto3.key)).data : allMerchants0;
|
|
59040
59307
|
const globalMerchants = allMerchants.filter((m2) => !m2.householdId && m2.cleanName).map((m2) => ({ id: m2.id, name: m2.cleanName }));
|
|
59041
59308
|
const localMerchants = allMerchants.filter((m2) => m2.householdId && m2.cleanName).map((m2) => ({ id: m2.id, name: m2.cleanName }));
|
|
59042
59309
|
const merchantCategoryById = new Map(allMerchants.filter((m2) => m2.categoryId).map((m2) => [m2.id, m2.categoryId]));
|
|
@@ -59133,9 +59400,13 @@ function transactionTools(client, crypto3) {
|
|
|
59133
59400
|
if (!full)
|
|
59134
59401
|
apiParams.view = "lean";
|
|
59135
59402
|
const data = await client.get("/api/transactions", Object.fromEntries(Object.entries(apiParams).filter(([, v2]) => v2 !== undefined).map(([k2, v2]) => [k2, String(v2)])));
|
|
59136
|
-
|
|
59137
|
-
|
|
59138
|
-
|
|
59403
|
+
let rows = data.data ?? [];
|
|
59404
|
+
let undecrypted = [];
|
|
59405
|
+
if (crypto3) {
|
|
59406
|
+
const dec = await decryptTransactionFields(rows, crypto3.key);
|
|
59407
|
+
rows = dec.data;
|
|
59408
|
+
undecrypted = dec.undecrypted;
|
|
59409
|
+
}
|
|
59139
59410
|
const personNames = new Map;
|
|
59140
59411
|
if (rows.some((r2) => r2.personId)) {
|
|
59141
59412
|
for (const p2 of await loadDecryptedPersons(client, crypto3))
|
|
@@ -59147,12 +59418,14 @@ function transactionTools(client, crypto3) {
|
|
|
59147
59418
|
if (person && personNames.has(person.id))
|
|
59148
59419
|
person.name = personNames.get(person.id) ?? "";
|
|
59149
59420
|
}
|
|
59150
|
-
|
|
59421
|
+
data.data = rows;
|
|
59422
|
+
return undecrypted.length ? { ...data, undecrypted } : data;
|
|
59151
59423
|
}
|
|
59152
59424
|
const out = {
|
|
59153
59425
|
rows: rows.map((r2) => toLeanRow(r2, personNames)),
|
|
59154
59426
|
...data.hasMore ? { more: true, cursor: data.nextCursor } : {},
|
|
59155
|
-
...data.summary ? { spent: data.summary.spent, income: data.summary.income } : {}
|
|
59427
|
+
...data.summary ? { spent: data.summary.spent, income: data.summary.income } : {},
|
|
59428
|
+
...undecrypted.length ? { undecrypted } : {}
|
|
59156
59429
|
};
|
|
59157
59430
|
return out;
|
|
59158
59431
|
}
|
|
@@ -59278,8 +59551,11 @@ function transactionTools(client, crypto3) {
|
|
|
59278
59551
|
}
|
|
59279
59552
|
if (action === "list") {
|
|
59280
59553
|
const data2 = await client.get("/api/transactions/duplicates");
|
|
59281
|
-
if (crypto3 && data2.data)
|
|
59282
|
-
await decryptTransactionFields(data2.data, crypto3.key);
|
|
59554
|
+
if (crypto3 && data2.data) {
|
|
59555
|
+
const { data: decrypted, undecrypted } = await decryptTransactionFields(data2.data, crypto3.key);
|
|
59556
|
+
data2.data = decrypted;
|
|
59557
|
+
return withTruncationWarning(undecrypted.length ? { ...data2, undecrypted } : data2);
|
|
59558
|
+
}
|
|
59283
59559
|
return withTruncationWarning(data2);
|
|
59284
59560
|
}
|
|
59285
59561
|
const data = await client.post("/api/transactions/duplicates/resolve", {
|
|
@@ -59299,8 +59575,11 @@ function transactionTools(client, crypto3) {
|
|
|
59299
59575
|
}),
|
|
59300
59576
|
handler: async ({ accountId, startDate, endDate }) => {
|
|
59301
59577
|
const data = await client.get("/api/transactions/reconciliation-context", { accountId, startDate, endDate });
|
|
59302
|
-
if (crypto3 && data.data)
|
|
59303
|
-
await decryptTransactionFields(data.data, crypto3.key);
|
|
59578
|
+
if (crypto3 && data.data) {
|
|
59579
|
+
const { data: decrypted, undecrypted } = await decryptTransactionFields(data.data, crypto3.key);
|
|
59580
|
+
data.data = decrypted;
|
|
59581
|
+
return withTruncationWarning(undecrypted.length ? { ...data, undecrypted } : data);
|
|
59582
|
+
}
|
|
59304
59583
|
return withTruncationWarning(data);
|
|
59305
59584
|
}
|
|
59306
59585
|
}),
|
|
@@ -59392,8 +59671,11 @@ function transactionTools(client, crypto3) {
|
|
|
59392
59671
|
input: exports_external.object({ ccAccountId: exports_external.string().uuid() }),
|
|
59393
59672
|
handler: async ({ ccAccountId }) => {
|
|
59394
59673
|
const data = await client.get("/api/transactions/settlement-candidates", { ccAccountId });
|
|
59395
|
-
if (crypto3 && data.data)
|
|
59396
|
-
await decryptTransactionFields(data.data, crypto3.key);
|
|
59674
|
+
if (crypto3 && data.data) {
|
|
59675
|
+
const { data: decrypted, undecrypted } = await decryptTransactionFields(data.data, crypto3.key);
|
|
59676
|
+
data.data = decrypted;
|
|
59677
|
+
return withTruncationWarning(undecrypted.length ? { ...data, undecrypted } : data);
|
|
59678
|
+
}
|
|
59397
59679
|
return withTruncationWarning(data);
|
|
59398
59680
|
}
|
|
59399
59681
|
}),
|
|
@@ -59403,11 +59685,15 @@ function transactionTools(client, crypto3) {
|
|
|
59403
59685
|
input: exports_external.object({ id: exports_external.string().uuid() }),
|
|
59404
59686
|
handler: async ({ id }) => {
|
|
59405
59687
|
const res = await client.get(`/api/transactions/${id}`);
|
|
59406
|
-
|
|
59688
|
+
let row = res.data;
|
|
59407
59689
|
if (!row)
|
|
59408
59690
|
return res;
|
|
59409
|
-
|
|
59410
|
-
|
|
59691
|
+
let undecrypted = [];
|
|
59692
|
+
if (crypto3) {
|
|
59693
|
+
const dec = await decryptTransactionFields([row], crypto3.key);
|
|
59694
|
+
row = dec.data[0];
|
|
59695
|
+
undecrypted = dec.undecrypted;
|
|
59696
|
+
}
|
|
59411
59697
|
const lean = Object.fromEntries([
|
|
59412
59698
|
"id",
|
|
59413
59699
|
"date",
|
|
@@ -59423,7 +59709,7 @@ function transactionTools(client, crypto3) {
|
|
|
59423
59709
|
"isDuplicate",
|
|
59424
59710
|
"source"
|
|
59425
59711
|
].filter((k2) => row[k2] !== undefined).map((k2) => [k2, row[k2]]));
|
|
59426
|
-
return lean;
|
|
59712
|
+
return undecrypted.length ? { ...lean, undecrypted } : lean;
|
|
59427
59713
|
}
|
|
59428
59714
|
})
|
|
59429
59715
|
];
|
|
@@ -59448,7 +59734,9 @@ function exportTools(client, crypto3) {
|
|
|
59448
59734
|
format: "json"
|
|
59449
59735
|
});
|
|
59450
59736
|
if (crypto3 && Array.isArray(data?.transactions)) {
|
|
59451
|
-
await decryptTransactionFields(data.transactions, crypto3.key);
|
|
59737
|
+
const { data: decrypted, undecrypted } = await decryptTransactionFields(data.transactions, crypto3.key);
|
|
59738
|
+
data.transactions = decrypted;
|
|
59739
|
+
return withTruncationWarning(undecrypted.length ? { ...data, undecrypted } : data);
|
|
59452
59740
|
}
|
|
59453
59741
|
return withTruncationWarning(data);
|
|
59454
59742
|
}
|
|
@@ -59456,7 +59744,7 @@ function exportTools(client, crypto3) {
|
|
|
59456
59744
|
];
|
|
59457
59745
|
}
|
|
59458
59746
|
|
|
59459
|
-
// ../../node_modules/.bun/@nimit9+signet-ai@0.1.11+
|
|
59747
|
+
// ../../node_modules/.bun/@nimit9+signet-ai@0.1.11+53d0ad1559459462/node_modules/@nimit9/signet-ai/dist/jev.js
|
|
59460
59748
|
var JEV_DEFAULT_MODEL = "jev-1.13.0";
|
|
59461
59749
|
var JEV_DEFAULT_MIN_CONFIDENCE = 0.95;
|
|
59462
59750
|
var JEV_DEFAULT_TIMEOUT_MS = 8000;
|
|
@@ -59942,9 +60230,8 @@ async function runStatementImport(deps, args) {
|
|
|
59942
60230
|
throw new Error(`${basename(args.filePath)} parsed to 0 transactions (format ${format}). If it is a statement with real activity, the layout may not be supported.`);
|
|
59943
60231
|
}
|
|
59944
60232
|
const dates = txns.map((t2) => t2.date).sort();
|
|
59945
|
-
const
|
|
59946
|
-
|
|
59947
|
-
await decryptTransactionFields(existing, crypto3.key);
|
|
60233
|
+
const existing0 = await fetchReconciliationContext(client, account.id, dates[0], dates[dates.length - 1]);
|
|
60234
|
+
const existing = crypto3 ? (await decryptTransactionFields(existing0, crypto3.key)).data : existing0;
|
|
59948
60235
|
const present = existing.map((r2) => ({ ...r2, isDuplicate: false, isIgnored: false }));
|
|
59949
60236
|
const result = reconcile(present, { ...statement, transactions: txns });
|
|
59950
60237
|
const fresh = result.unmatched;
|
|
@@ -60282,7 +60569,7 @@ function registerResources(server, tools) {
|
|
|
60282
60569
|
// package.json
|
|
60283
60570
|
var package_default = {
|
|
60284
60571
|
name: "paisa-mcp",
|
|
60285
|
-
version: "0.
|
|
60572
|
+
version: "0.4.0",
|
|
60286
60573
|
repository: {
|
|
60287
60574
|
type: "git",
|
|
60288
60575
|
url: "git+https://github.com/nimit9/paisa.git",
|
|
@@ -60310,7 +60597,7 @@ var package_default = {
|
|
|
60310
60597
|
"@modelcontextprotocol/client": "^2.1.0",
|
|
60311
60598
|
"@modelcontextprotocol/server": "^2.1.0",
|
|
60312
60599
|
"@nimit9/signet-ai": "^0.1.11",
|
|
60313
|
-
"@nimit9/signet-lib": "^0.1.
|
|
60600
|
+
"@nimit9/signet-lib": "^0.1.15",
|
|
60314
60601
|
"@nimit9/signet-server": "^0.2.2",
|
|
60315
60602
|
"@paisa/parsers": "workspace:*",
|
|
60316
60603
|
"@paisa/reconciliation": "workspace:*",
|
|
@@ -60338,7 +60625,13 @@ function paisaErrorResult(err) {
|
|
|
60338
60625
|
});
|
|
60339
60626
|
}
|
|
60340
60627
|
function buildServer(client, crypto3) {
|
|
60341
|
-
|
|
60628
|
+
let initializeCapabilities = () => {
|
|
60629
|
+
return;
|
|
60630
|
+
};
|
|
60631
|
+
const tools = applyAnnotations(REGISTRATIONS.flatMap((group) => group.tools(client, crypto3)).map((t2) => {
|
|
60632
|
+
const describe3 = CONFIRMED_TOOLS[t2.name];
|
|
60633
|
+
return describe3 ? withConfirmation(t2, describe3, () => initializeCapabilities()) : t2;
|
|
60634
|
+
}));
|
|
60342
60635
|
for (const t2 of tools)
|
|
60343
60636
|
if (t2.input)
|
|
60344
60637
|
withCompactJsonSchema(t2.input);
|
|
@@ -60348,6 +60641,7 @@ function buildServer(client, crypto3) {
|
|
|
60348
60641
|
tools,
|
|
60349
60642
|
errorResult: paisaErrorResult
|
|
60350
60643
|
});
|
|
60644
|
+
initializeCapabilities = () => server.server.getClientCapabilities();
|
|
60351
60645
|
registerResources(server, tools);
|
|
60352
60646
|
registerPrompts(server);
|
|
60353
60647
|
return server;
|
|
@@ -60374,5 +60668,5 @@ main().catch((err) => {
|
|
|
60374
60668
|
process.exit(1);
|
|
60375
60669
|
});
|
|
60376
60670
|
|
|
60377
|
-
//# debugId=
|
|
60671
|
+
//# debugId=F56B7BEACF2C363564756E2164756E21
|
|
60378
60672
|
//# sourceMappingURL=index.js.map
|