paisa-mcp 0.1.0 → 0.3.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 +829 -823
- package/dist/index.js.map +19 -17
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -39147,6 +39147,220 @@ function validateAndWarnToolName(name) {
|
|
|
39147
39147
|
issueToolNameWarning(name, result.warnings);
|
|
39148
39148
|
return result.isValid;
|
|
39149
39149
|
}
|
|
39150
|
+
var MAX_TEMPLATE_LENGTH = 1e6;
|
|
39151
|
+
var MAX_VARIABLE_LENGTH = 1e6;
|
|
39152
|
+
var MAX_TEMPLATE_EXPRESSIONS = 1e4;
|
|
39153
|
+
var MAX_REGEX_LENGTH = 1e6;
|
|
39154
|
+
var UriTemplate = class UriTemplate2 {
|
|
39155
|
+
static isTemplate(str) {
|
|
39156
|
+
return /\{[^}\s]+\}/.test(str);
|
|
39157
|
+
}
|
|
39158
|
+
static validateLength(str, max, context) {
|
|
39159
|
+
if (str.length > max)
|
|
39160
|
+
throw new Error(`${context} exceeds maximum length of ${max} characters (got ${str.length})`);
|
|
39161
|
+
}
|
|
39162
|
+
template;
|
|
39163
|
+
parts;
|
|
39164
|
+
get variableNames() {
|
|
39165
|
+
return this.parts.flatMap((part) => typeof part === "string" ? [] : part.names);
|
|
39166
|
+
}
|
|
39167
|
+
constructor(template) {
|
|
39168
|
+
UriTemplate2.validateLength(template, MAX_TEMPLATE_LENGTH, "Template");
|
|
39169
|
+
this.template = template;
|
|
39170
|
+
this.parts = this.parse(template);
|
|
39171
|
+
}
|
|
39172
|
+
toString() {
|
|
39173
|
+
return this.template;
|
|
39174
|
+
}
|
|
39175
|
+
parse(template) {
|
|
39176
|
+
const parts = [];
|
|
39177
|
+
let currentText = "";
|
|
39178
|
+
let i = 0;
|
|
39179
|
+
let expressionCount = 0;
|
|
39180
|
+
while (i < template.length)
|
|
39181
|
+
if (template[i] === "{") {
|
|
39182
|
+
if (currentText) {
|
|
39183
|
+
parts.push(currentText);
|
|
39184
|
+
currentText = "";
|
|
39185
|
+
}
|
|
39186
|
+
const end = template.indexOf("}", i);
|
|
39187
|
+
if (end === -1)
|
|
39188
|
+
throw new Error("Unclosed template expression");
|
|
39189
|
+
expressionCount++;
|
|
39190
|
+
if (expressionCount > MAX_TEMPLATE_EXPRESSIONS)
|
|
39191
|
+
throw new Error(`Template contains too many expressions (max ${MAX_TEMPLATE_EXPRESSIONS})`);
|
|
39192
|
+
const expr = template.slice(i + 1, end);
|
|
39193
|
+
const operator = this.getOperator(expr);
|
|
39194
|
+
const exploded = expr.includes("*");
|
|
39195
|
+
const names = this.getNames(expr);
|
|
39196
|
+
const name = names[0];
|
|
39197
|
+
for (const name$1 of names)
|
|
39198
|
+
UriTemplate2.validateLength(name$1, MAX_VARIABLE_LENGTH, "Variable name");
|
|
39199
|
+
parts.push({
|
|
39200
|
+
name,
|
|
39201
|
+
operator,
|
|
39202
|
+
names,
|
|
39203
|
+
exploded
|
|
39204
|
+
});
|
|
39205
|
+
i = end + 1;
|
|
39206
|
+
} else {
|
|
39207
|
+
currentText += template[i];
|
|
39208
|
+
i++;
|
|
39209
|
+
}
|
|
39210
|
+
if (currentText)
|
|
39211
|
+
parts.push(currentText);
|
|
39212
|
+
return parts;
|
|
39213
|
+
}
|
|
39214
|
+
getOperator(expr) {
|
|
39215
|
+
return [
|
|
39216
|
+
"+",
|
|
39217
|
+
"#",
|
|
39218
|
+
".",
|
|
39219
|
+
"/",
|
|
39220
|
+
"?",
|
|
39221
|
+
"&"
|
|
39222
|
+
].find((op) => expr.startsWith(op)) || "";
|
|
39223
|
+
}
|
|
39224
|
+
getNames(expr) {
|
|
39225
|
+
const operator = this.getOperator(expr);
|
|
39226
|
+
return expr.slice(operator.length).split(",").map((name) => name.replace("*", "").trim()).filter((name) => name.length > 0);
|
|
39227
|
+
}
|
|
39228
|
+
encodeValue(value, operator) {
|
|
39229
|
+
UriTemplate2.validateLength(value, MAX_VARIABLE_LENGTH, "Variable value");
|
|
39230
|
+
if (operator === "+" || operator === "#")
|
|
39231
|
+
return encodeURI(value);
|
|
39232
|
+
return encodeURIComponent(value);
|
|
39233
|
+
}
|
|
39234
|
+
expandPart(part, variables) {
|
|
39235
|
+
if (part.operator === "?" || part.operator === "&") {
|
|
39236
|
+
const pairs = part.names.map((name) => {
|
|
39237
|
+
const value$1 = variables[name];
|
|
39238
|
+
if (value$1 === undefined)
|
|
39239
|
+
return "";
|
|
39240
|
+
return `${name}=${Array.isArray(value$1) ? value$1.map((v) => this.encodeValue(v, part.operator)).join(",") : this.encodeValue(value$1.toString(), part.operator)}`;
|
|
39241
|
+
}).filter((pair) => pair.length > 0);
|
|
39242
|
+
if (pairs.length === 0)
|
|
39243
|
+
return "";
|
|
39244
|
+
return (part.operator === "?" ? "?" : "&") + pairs.join("&");
|
|
39245
|
+
}
|
|
39246
|
+
if (part.names.length > 1) {
|
|
39247
|
+
const values = part.names.map((name) => variables[name]).filter((v) => v !== undefined);
|
|
39248
|
+
if (values.length === 0)
|
|
39249
|
+
return "";
|
|
39250
|
+
return values.map((v) => Array.isArray(v) ? v[0] : v).join(",");
|
|
39251
|
+
}
|
|
39252
|
+
const value = variables[part.name];
|
|
39253
|
+
if (value === undefined)
|
|
39254
|
+
return "";
|
|
39255
|
+
const encoded = (Array.isArray(value) ? value : [value]).map((v) => this.encodeValue(v, part.operator));
|
|
39256
|
+
switch (part.operator) {
|
|
39257
|
+
case "":
|
|
39258
|
+
return encoded.join(",");
|
|
39259
|
+
case "+":
|
|
39260
|
+
return encoded.join(",");
|
|
39261
|
+
case "#":
|
|
39262
|
+
return "#" + encoded.join(",");
|
|
39263
|
+
case ".":
|
|
39264
|
+
return "." + encoded.join(".");
|
|
39265
|
+
case "/":
|
|
39266
|
+
return "/" + encoded.join("/");
|
|
39267
|
+
default:
|
|
39268
|
+
return encoded.join(",");
|
|
39269
|
+
}
|
|
39270
|
+
}
|
|
39271
|
+
expand(variables) {
|
|
39272
|
+
let result = "";
|
|
39273
|
+
let hasQueryParam = false;
|
|
39274
|
+
for (const part of this.parts) {
|
|
39275
|
+
if (typeof part === "string") {
|
|
39276
|
+
result += part;
|
|
39277
|
+
continue;
|
|
39278
|
+
}
|
|
39279
|
+
const expanded = this.expandPart(part, variables);
|
|
39280
|
+
if (!expanded)
|
|
39281
|
+
continue;
|
|
39282
|
+
result += (part.operator === "?" || part.operator === "&") && hasQueryParam ? expanded.replace("?", "&") : expanded;
|
|
39283
|
+
if (part.operator === "?" || part.operator === "&")
|
|
39284
|
+
hasQueryParam = true;
|
|
39285
|
+
}
|
|
39286
|
+
return result;
|
|
39287
|
+
}
|
|
39288
|
+
escapeRegExp(str) {
|
|
39289
|
+
return str.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
|
39290
|
+
}
|
|
39291
|
+
partToRegExp(part) {
|
|
39292
|
+
const patterns = [];
|
|
39293
|
+
for (const name$1 of part.names)
|
|
39294
|
+
UriTemplate2.validateLength(name$1, MAX_VARIABLE_LENGTH, "Variable name");
|
|
39295
|
+
if (part.operator === "?" || part.operator === "&") {
|
|
39296
|
+
for (let i = 0;i < part.names.length; i++) {
|
|
39297
|
+
const name$1 = part.names[i];
|
|
39298
|
+
const prefix = i === 0 ? "\\" + part.operator : "&";
|
|
39299
|
+
patterns.push({
|
|
39300
|
+
pattern: prefix + this.escapeRegExp(name$1) + "=([^&]+)",
|
|
39301
|
+
name: name$1
|
|
39302
|
+
});
|
|
39303
|
+
}
|
|
39304
|
+
return patterns;
|
|
39305
|
+
}
|
|
39306
|
+
let pattern;
|
|
39307
|
+
const name = part.name;
|
|
39308
|
+
switch (part.operator) {
|
|
39309
|
+
case "":
|
|
39310
|
+
pattern = part.exploded ? "([^/,]+(?:,[^/,]+)*)" : "([^/,]+)";
|
|
39311
|
+
break;
|
|
39312
|
+
case "+":
|
|
39313
|
+
case "#":
|
|
39314
|
+
pattern = "(.+)";
|
|
39315
|
+
break;
|
|
39316
|
+
case ".":
|
|
39317
|
+
pattern = String.raw`\.([^/,]+)`;
|
|
39318
|
+
break;
|
|
39319
|
+
case "/":
|
|
39320
|
+
pattern = "/" + (part.exploded ? "([^/,]+(?:,[^/,]+)*)" : "([^/,]+)");
|
|
39321
|
+
break;
|
|
39322
|
+
default:
|
|
39323
|
+
pattern = "([^/]+)";
|
|
39324
|
+
}
|
|
39325
|
+
patterns.push({
|
|
39326
|
+
pattern,
|
|
39327
|
+
name
|
|
39328
|
+
});
|
|
39329
|
+
return patterns;
|
|
39330
|
+
}
|
|
39331
|
+
match(uri) {
|
|
39332
|
+
UriTemplate2.validateLength(uri, MAX_TEMPLATE_LENGTH, "URI");
|
|
39333
|
+
let pattern = "^";
|
|
39334
|
+
const names = [];
|
|
39335
|
+
for (const part of this.parts)
|
|
39336
|
+
if (typeof part === "string")
|
|
39337
|
+
pattern += this.escapeRegExp(part);
|
|
39338
|
+
else {
|
|
39339
|
+
const patterns = this.partToRegExp(part);
|
|
39340
|
+
for (const { pattern: partPattern2, name } of patterns) {
|
|
39341
|
+
pattern += partPattern2;
|
|
39342
|
+
names.push({
|
|
39343
|
+
name,
|
|
39344
|
+
exploded: part.exploded
|
|
39345
|
+
});
|
|
39346
|
+
}
|
|
39347
|
+
}
|
|
39348
|
+
pattern += "$";
|
|
39349
|
+
UriTemplate2.validateLength(pattern, MAX_REGEX_LENGTH, "Generated regex pattern");
|
|
39350
|
+
const regex = new RegExp(pattern);
|
|
39351
|
+
const match = uri.match(regex);
|
|
39352
|
+
if (!match)
|
|
39353
|
+
return null;
|
|
39354
|
+
const result = {};
|
|
39355
|
+
for (const [i, name_] of names.entries()) {
|
|
39356
|
+
const { name, exploded } = name_;
|
|
39357
|
+
const value = match[i + 1];
|
|
39358
|
+
const cleanName = name.replace("*", "");
|
|
39359
|
+
result[cleanName] = exploded && value.includes(",") ? value.split(",") : value;
|
|
39360
|
+
}
|
|
39361
|
+
return result;
|
|
39362
|
+
}
|
|
39363
|
+
};
|
|
39150
39364
|
function isZodV4Schema(v) {
|
|
39151
39365
|
return typeof v === "object" && v !== null && "_zod" in v;
|
|
39152
39366
|
}
|
|
@@ -48100,6 +48314,22 @@ var McpServer = class {
|
|
|
48100
48314
|
this.server.sendPromptListChanged();
|
|
48101
48315
|
}
|
|
48102
48316
|
};
|
|
48317
|
+
var ResourceTemplate = class {
|
|
48318
|
+
_uriTemplate;
|
|
48319
|
+
constructor(uriTemplate, _callbacks) {
|
|
48320
|
+
this._callbacks = _callbacks;
|
|
48321
|
+
this._uriTemplate = typeof uriTemplate === "string" ? new UriTemplate(uriTemplate) : uriTemplate;
|
|
48322
|
+
}
|
|
48323
|
+
get uriTemplate() {
|
|
48324
|
+
return this._uriTemplate;
|
|
48325
|
+
}
|
|
48326
|
+
get listCallback() {
|
|
48327
|
+
return this._callbacks.list;
|
|
48328
|
+
}
|
|
48329
|
+
completeCallback(variable) {
|
|
48330
|
+
return this._callbacks.complete?.[variable];
|
|
48331
|
+
}
|
|
48332
|
+
};
|
|
48103
48333
|
function createToolExecutor(inputSchema, handler) {
|
|
48104
48334
|
if (inputSchema) {
|
|
48105
48335
|
const callback$1 = handler;
|
|
@@ -53070,7 +53300,7 @@ async function initCrypto(client, pin, userId) {
|
|
|
53070
53300
|
// ../../node_modules/.bun/@modelcontextprotocol+server@2.1.0/node_modules/@modelcontextprotocol/server/dist/index.mjs
|
|
53071
53301
|
var DEFAULT_MAX_REQUEST_BODY_SIZE = 4 * 1024 * 1024;
|
|
53072
53302
|
|
|
53073
|
-
// ../../node_modules/.bun/@nimit9+signet-server@0.2.
|
|
53303
|
+
// ../../node_modules/.bun/@nimit9+signet-server@0.2.3+e803b2305afdb064/node_modules/@nimit9/signet-server/dist/http/status-codes.js
|
|
53074
53304
|
var STATUS_CODES = {
|
|
53075
53305
|
400: "BAD_REQUEST",
|
|
53076
53306
|
401: "UNAUTHORIZED",
|
|
@@ -53084,7 +53314,7 @@ var STATUS_CODES = {
|
|
|
53084
53314
|
429: "RATE_LIMITED"
|
|
53085
53315
|
};
|
|
53086
53316
|
|
|
53087
|
-
// ../../node_modules/.bun/@nimit9+signet-server@0.2.
|
|
53317
|
+
// ../../node_modules/.bun/@nimit9+signet-server@0.2.3+e803b2305afdb064/node_modules/@nimit9/signet-server/dist/mcp/server.js
|
|
53088
53318
|
var RAW = Symbol.for("signet.mcp.rawResult");
|
|
53089
53319
|
function toolResult(result) {
|
|
53090
53320
|
return Object.defineProperty({ ...result }, RAW, { value: true, enumerable: false });
|
|
@@ -53168,10 +53398,17 @@ function toResult(value, structured) {
|
|
|
53168
53398
|
return { content: [{ type: "text", text }] };
|
|
53169
53399
|
}
|
|
53170
53400
|
function createMcpServer(options) {
|
|
53401
|
+
const unlisted = new Set(options.tools.filter((t) => t.listed === false).map((t) => t.name));
|
|
53402
|
+
const toolsCapability = { tools: { listChanged: false } };
|
|
53171
53403
|
const server = new McpServer({ name: options.name, version: options.version }, {
|
|
53172
|
-
|
|
53404
|
+
...unlisted.size ? {} : { capabilities: toolsCapability },
|
|
53173
53405
|
...options.instructions ? { instructions: options.instructions } : {}
|
|
53174
53406
|
});
|
|
53407
|
+
let restore;
|
|
53408
|
+
if (unlisted.size) {
|
|
53409
|
+
server.server.registerCapabilities(toolsCapability);
|
|
53410
|
+
restore = hideUnlisted(server, unlisted);
|
|
53411
|
+
}
|
|
53175
53412
|
const register2 = server.registerTool.bind(server);
|
|
53176
53413
|
const { c, token } = options.context ?? {};
|
|
53177
53414
|
for (const tool of options.tools) {
|
|
@@ -53215,29 +53452,110 @@ function createMcpServer(options) {
|
|
|
53215
53452
|
...tool.input ? { inputSchema: tool.input } : {},
|
|
53216
53453
|
...tool.output ? { outputSchema: tool.output } : {}
|
|
53217
53454
|
}, tool.input ? (input2, mcp) => run(input2, mcp) : (mcp) => run({}, mcp));
|
|
53455
|
+
restore?.();
|
|
53456
|
+
restore = undefined;
|
|
53218
53457
|
}
|
|
53219
53458
|
return server;
|
|
53220
53459
|
}
|
|
53460
|
+
function hideUnlisted(server, unlisted) {
|
|
53461
|
+
const inner = server.server;
|
|
53462
|
+
const original = inner.setRequestHandler.bind(inner);
|
|
53463
|
+
let caught = false;
|
|
53464
|
+
inner.setRequestHandler = (method, handler) => {
|
|
53465
|
+
if (method !== "tools/list" || typeof handler !== "function")
|
|
53466
|
+
return original(method, handler);
|
|
53467
|
+
caught = true;
|
|
53468
|
+
original(method, async (request, ctx) => {
|
|
53469
|
+
const result = await handler(request, ctx);
|
|
53470
|
+
return { ...result, tools: result.tools.filter((t) => !unlisted.has(t.name)) };
|
|
53471
|
+
});
|
|
53472
|
+
};
|
|
53473
|
+
return () => {
|
|
53474
|
+
delete inner.setRequestHandler;
|
|
53475
|
+
if (!caught)
|
|
53476
|
+
throw new Error("signet mcp: could not hide unlisted tools; @modelcontextprotocol/server no longer installs tools/list through setRequestHandler");
|
|
53477
|
+
};
|
|
53478
|
+
}
|
|
53479
|
+
|
|
53480
|
+
// src/annotations.ts
|
|
53481
|
+
var RO = { readOnlyHint: true, openWorldHint: false };
|
|
53482
|
+
var WI = {
|
|
53483
|
+
readOnlyHint: false,
|
|
53484
|
+
destructiveHint: false,
|
|
53485
|
+
idempotentHint: true,
|
|
53486
|
+
openWorldHint: false
|
|
53487
|
+
};
|
|
53488
|
+
var W = {
|
|
53489
|
+
readOnlyHint: false,
|
|
53490
|
+
destructiveHint: false,
|
|
53491
|
+
idempotentHint: false,
|
|
53492
|
+
openWorldHint: false
|
|
53493
|
+
};
|
|
53494
|
+
var D = {
|
|
53495
|
+
readOnlyHint: false,
|
|
53496
|
+
destructiveHint: true,
|
|
53497
|
+
idempotentHint: false,
|
|
53498
|
+
openWorldHint: false
|
|
53499
|
+
};
|
|
53500
|
+
var WI_OPEN = {
|
|
53501
|
+
readOnlyHint: false,
|
|
53502
|
+
destructiveHint: false,
|
|
53503
|
+
idempotentHint: true,
|
|
53504
|
+
openWorldHint: true
|
|
53505
|
+
};
|
|
53506
|
+
var TOOL_META = {
|
|
53507
|
+
get_transactions: { title: "Search Transactions", annotations: RO },
|
|
53508
|
+
get_uncategorized_groups: { title: "Group Uncategorized Transactions", annotations: RO },
|
|
53509
|
+
categorize_groups: { title: "Categorize Transaction Groups", annotations: W },
|
|
53510
|
+
backfill_persons: { title: "Backfill Persons from Transactions", annotations: WI },
|
|
53511
|
+
update_transaction: { title: "Update Transaction", annotations: WI },
|
|
53512
|
+
bulk_link_entity: { title: "Bulk Link Transactions", annotations: WI },
|
|
53513
|
+
manage_duplicates: { title: "Manage Duplicate Transactions", annotations: W },
|
|
53514
|
+
get_reconciliation_context: { title: "Get Reconciliation Context", annotations: RO },
|
|
53515
|
+
bulk_categorize_transactions: { title: "Bulk Categorize Transactions", annotations: WI },
|
|
53516
|
+
delete_transactions: { title: "Delete Transactions", annotations: D },
|
|
53517
|
+
rescan_transactions: { title: "Rescan Transactions", annotations: WI },
|
|
53518
|
+
get_settlement_candidates: { title: "Find Settlement Candidates", annotations: RO },
|
|
53519
|
+
get_transaction: { title: "Get Transaction", annotations: RO },
|
|
53520
|
+
preview_import: { title: "Preview Statement Import", annotations: RO },
|
|
53521
|
+
import_statement: { title: "Import Statement", annotations: W },
|
|
53522
|
+
auto_categorize_jev: { title: "Auto-Categorize with Jev", annotations: W },
|
|
53523
|
+
list_entities: { title: "List Entities", annotations: RO },
|
|
53524
|
+
upsert_entity: { title: "Create or Update Entity", annotations: W },
|
|
53525
|
+
delete_entity: { title: "Delete Entity", annotations: D },
|
|
53526
|
+
merge_merchants: { title: "Merge Merchants", annotations: D },
|
|
53527
|
+
get_analytics: { title: "Get Analytics", annotations: RO },
|
|
53528
|
+
upsert_savings: { title: "Create or Update Savings Vehicle", annotations: W },
|
|
53529
|
+
contribute_to_goal: { title: "Contribute to Goal", annotations: W },
|
|
53530
|
+
set_budget: { title: "Set Budget", annotations: WI },
|
|
53531
|
+
get_dashboard: { title: "Get Dashboard", annotations: RO },
|
|
53532
|
+
sync_recurring: { title: "Sync Recurring Merchants", annotations: WI },
|
|
53533
|
+
check_alerts: { title: "Check Alerts", annotations: W },
|
|
53534
|
+
manage_alerts: { title: "Manage Alerts and Rules", annotations: WI },
|
|
53535
|
+
manage_debt: { title: "Add or Settle Debt", annotations: W },
|
|
53536
|
+
generate_monthly_report: { title: "Generate Monthly Report", annotations: WI },
|
|
53537
|
+
get_report: { title: "Get Report", annotations: RO },
|
|
53538
|
+
export_transactions: { title: "Export Transactions", annotations: RO },
|
|
53539
|
+
set_learning_rule: { title: "Set Learning Rule", annotations: WI },
|
|
53540
|
+
update_net_worth: { title: "Update Net Worth", annotations: WI },
|
|
53541
|
+
upsert_investment: { title: "Create or Update Investment", annotations: W },
|
|
53542
|
+
sync_zerodha: { title: "Sync Zerodha", annotations: WI_OPEN },
|
|
53543
|
+
get_settings: { title: "Get Settings", annotations: RO },
|
|
53544
|
+
update_settings: { title: "Update Settings", annotations: WI },
|
|
53545
|
+
set_encryption_mode: { title: "Set Encryption Mode", annotations: D }
|
|
53546
|
+
};
|
|
53547
|
+
function applyAnnotations(tools) {
|
|
53548
|
+
return tools.map((tool) => {
|
|
53549
|
+
const meta3 = TOOL_META[tool.name];
|
|
53550
|
+
if (!meta3)
|
|
53551
|
+
throw new Error(`No TOOL_META entry for tool "${tool.name}" — add one to annotations.ts`);
|
|
53552
|
+
return { ...tool, title: meta3.title, annotations: meta3.annotations };
|
|
53553
|
+
});
|
|
53554
|
+
}
|
|
53221
53555
|
// src/lib/union-tool.ts
|
|
53222
53556
|
function variant(input2, run) {
|
|
53223
53557
|
return { input: input2, run };
|
|
53224
53558
|
}
|
|
53225
|
-
function isAlias(tool) {
|
|
53226
|
-
return typeof tool.aliasFor === "string";
|
|
53227
|
-
}
|
|
53228
|
-
function aliasTool(name, input2, aliasFor, note, route) {
|
|
53229
|
-
const tool = defineTool({
|
|
53230
|
-
name,
|
|
53231
|
-
description: `Deprecated alias of ${aliasFor}.`,
|
|
53232
|
-
input: input2,
|
|
53233
|
-
handler: async (args, ctx) => {
|
|
53234
|
-
const [to, next] = route(args);
|
|
53235
|
-
const parsed = to.input ? await to.input.parseAsync(next) : next;
|
|
53236
|
-
return to.handler(parsed, ctx);
|
|
53237
|
-
}
|
|
53238
|
-
});
|
|
53239
|
-
return { ...tool, aliasFor, aliasNote: note };
|
|
53240
|
-
}
|
|
53241
53559
|
function compactJsonSchema(schema) {
|
|
53242
53560
|
const walk = (node2, root) => {
|
|
53243
53561
|
if (Array.isArray(node2))
|
|
@@ -53334,91 +53652,109 @@ function unionTool(opts) {
|
|
|
53334
53652
|
return v.run(rest, ctx);
|
|
53335
53653
|
}
|
|
53336
53654
|
});
|
|
53337
|
-
return {
|
|
53338
|
-
tool,
|
|
53339
|
-
alias: (aliasName, value) => aliasTool(aliasName, variants[value].input, name, `${key}: "${value}"`, (a) => [
|
|
53340
|
-
tool,
|
|
53341
|
-
{ ...a, [key]: value }
|
|
53342
|
-
])
|
|
53343
|
-
};
|
|
53655
|
+
return { tool };
|
|
53344
53656
|
}
|
|
53345
53657
|
|
|
53346
|
-
// src/
|
|
53347
|
-
var
|
|
53348
|
-
var
|
|
53349
|
-
|
|
53350
|
-
|
|
53351
|
-
|
|
53352
|
-
|
|
53353
|
-
}
|
|
53354
|
-
|
|
53355
|
-
|
|
53356
|
-
|
|
53357
|
-
|
|
53358
|
-
|
|
53359
|
-
}
|
|
53360
|
-
|
|
53361
|
-
|
|
53362
|
-
|
|
53363
|
-
|
|
53364
|
-
|
|
53365
|
-
|
|
53366
|
-
|
|
53367
|
-
|
|
53368
|
-
|
|
53369
|
-
|
|
53370
|
-
|
|
53371
|
-
}
|
|
53372
|
-
|
|
53373
|
-
|
|
53374
|
-
|
|
53375
|
-
|
|
53376
|
-
|
|
53377
|
-
|
|
53378
|
-
|
|
53379
|
-
|
|
53380
|
-
|
|
53381
|
-
|
|
53382
|
-
|
|
53383
|
-
|
|
53384
|
-
|
|
53385
|
-
|
|
53386
|
-
|
|
53387
|
-
|
|
53388
|
-
|
|
53389
|
-
|
|
53390
|
-
|
|
53391
|
-
|
|
53392
|
-
|
|
53393
|
-
|
|
53394
|
-
|
|
53395
|
-
|
|
53396
|
-
|
|
53397
|
-
|
|
53398
|
-
|
|
53399
|
-
|
|
53400
|
-
|
|
53401
|
-
|
|
53402
|
-
|
|
53403
|
-
|
|
53404
|
-
|
|
53405
|
-
|
|
53406
|
-
|
|
53407
|
-
|
|
53408
|
-
|
|
53409
|
-
|
|
53410
|
-
|
|
53411
|
-
|
|
53412
|
-
|
|
53413
|
-
|
|
53414
|
-
|
|
53415
|
-
|
|
53416
|
-
|
|
53417
|
-
|
|
53418
|
-
|
|
53419
|
-
|
|
53420
|
-
|
|
53421
|
-
|
|
53658
|
+
// src/prompts.ts
|
|
53659
|
+
var CONFIRM = "Ask me before any write; show what will change first (counts, not a dump).";
|
|
53660
|
+
var month = exports_external.string().regex(/^\d{4}-\d{2}$/).optional().describe("YYYY-MM; defaults to the last full month");
|
|
53661
|
+
function monthText(m) {
|
|
53662
|
+
if (!m)
|
|
53663
|
+
return "the last full month";
|
|
53664
|
+
const [y, mm] = m.split("-");
|
|
53665
|
+
return `${m} (month=${Number(mm)}, year=${y})`;
|
|
53666
|
+
}
|
|
53667
|
+
function rangeText(a) {
|
|
53668
|
+
if (a.startDate && a.endDate)
|
|
53669
|
+
return ` startDate=${a.startDate} endDate=${a.endDate}`;
|
|
53670
|
+
if (a.startDate)
|
|
53671
|
+
return ` startDate=${a.startDate}`;
|
|
53672
|
+
if (a.endDate)
|
|
53673
|
+
return ` endDate=${a.endDate}`;
|
|
53674
|
+
return "";
|
|
53675
|
+
}
|
|
53676
|
+
var PROMPTS = [
|
|
53677
|
+
{
|
|
53678
|
+
name: "monthly_review",
|
|
53679
|
+
title: "Monthly review",
|
|
53680
|
+
description: "Review one month: income, spending, savings rate, budgets, what's due next.",
|
|
53681
|
+
args: exports_external.object({ month }),
|
|
53682
|
+
text: (a) => [
|
|
53683
|
+
`Review my finances for ${monthText(a.month)}.`,
|
|
53684
|
+
"1. `check_alerts` — note anything urgent.",
|
|
53685
|
+
"2. `get_report` report='monthly' months=3 — income, expenses, savings rate vs the months before.",
|
|
53686
|
+
"3. `get_analytics` report='spending_trends' months=3 — category breakdown and budget vs actual (read that month's row).",
|
|
53687
|
+
"4. `get_transactions` for that month with amountMin to find the biggest items (limit 25).",
|
|
53688
|
+
"5. `get_report` report='upcoming' days=30 — EMIs, SIPs, renewals, card bills.",
|
|
53689
|
+
"Summarize: income, spend, savings rate, top categories, over-budget categories, unusual items, upcoming dues. Do not edit, import or delete anything.",
|
|
53690
|
+
"If I ask to save it, call `generate_monthly_report` with month and year."
|
|
53691
|
+
].join(`
|
|
53692
|
+
`)
|
|
53693
|
+
},
|
|
53694
|
+
{
|
|
53695
|
+
name: "import_statement",
|
|
53696
|
+
title: "Import a statement",
|
|
53697
|
+
description: "Preview, confirm, import and categorize a bank or card statement (PDF or CSV).",
|
|
53698
|
+
args: exports_external.object({
|
|
53699
|
+
filePath: exports_external.string().optional().describe("Local path of the statement file")
|
|
53700
|
+
}),
|
|
53701
|
+
text: (a) => [
|
|
53702
|
+
`Import the statement ${a.filePath ? `at ${a.filePath}` : "I give you (ask for the file path)"}.`,
|
|
53703
|
+
"1. `list_entities` entity='bank_accounts' — pick the account. Missing? Create it with `upsert_entity` entity='bank_account' only after I confirm name, bank, type, last4 and owner.",
|
|
53704
|
+
"2. `preview_import` — from='pdf' with filePath + account, or from='csv' with filePath. It writes nothing. Show me the new / already-in-Paisa / conflict counts and totals (CSV: the detected mapping).",
|
|
53705
|
+
"3. After I confirm, `import_statement` with the same args (CSV: the confirmed mapping). Never pass force unless I say so. CSV import is not available in private mode.",
|
|
53706
|
+
"4. Credit card statement: `get_settlement_candidates` with ccAccountId, then mark the matching bank debits isTransfer=true with `update_transaction` once I confirm.",
|
|
53707
|
+
"5. `sync_recurring`, then categorize what's left with `get_uncategorized_groups` → `categorize_groups` (see the categorize_uncategorized prompt).",
|
|
53708
|
+
CONFIRM
|
|
53709
|
+
].join(`
|
|
53710
|
+
`)
|
|
53711
|
+
},
|
|
53712
|
+
{
|
|
53713
|
+
name: "categorize_uncategorized",
|
|
53714
|
+
title: "Categorize uncategorized",
|
|
53715
|
+
description: "Group uncategorized transactions, propose categories, apply them and save learning rules.",
|
|
53716
|
+
args: exports_external.object({
|
|
53717
|
+
startDate: exports_external.string().optional().describe("YYYY-MM-DD"),
|
|
53718
|
+
endDate: exports_external.string().optional().describe("YYYY-MM-DD")
|
|
53719
|
+
}),
|
|
53720
|
+
text: (a) => [
|
|
53721
|
+
"Categorize my uncategorized transactions.",
|
|
53722
|
+
"1. `list_entities` entity='categories' (or the paisa://categories resource) for valid slugs.",
|
|
53723
|
+
`2. \`get_uncategorized_groups\`${rangeText(a)} — groups by pattern, biggest first.`,
|
|
53724
|
+
"3. Propose a category slug per group as a table (pattern, count, total, slug). Ask about groups you are unsure of instead of guessing.",
|
|
53725
|
+
`4. After I confirm, \`categorize_groups\`${rangeText(a)} with dryRun=true, show the counts, then run it again without dryRun. It also saves learning rules for future imports.`,
|
|
53726
|
+
"5. Transfers to/from individuals: `backfill_persons` with dryRun=true, then for real once I confirm.",
|
|
53727
|
+
"6. Tell me how many uncategorized groups remain.",
|
|
53728
|
+
CONFIRM
|
|
53729
|
+
].join(`
|
|
53730
|
+
`)
|
|
53731
|
+
},
|
|
53732
|
+
{
|
|
53733
|
+
name: "budget_check",
|
|
53734
|
+
title: "Budget check",
|
|
53735
|
+
description: "Compare one month's spending to its budgets and suggest changes.",
|
|
53736
|
+
args: exports_external.object({ month }),
|
|
53737
|
+
text: (a) => [
|
|
53738
|
+
`Check my budgets for ${monthText(a.month)}.`,
|
|
53739
|
+
"1. `list_entities` entity='budgets' with month and year.",
|
|
53740
|
+
"2. `get_analytics` report='spending_trends' months=3 — budget vs actual (read that month's row).",
|
|
53741
|
+
"3. Show a table: category, budget, spent, remaining, % used. Flag categories over 80% and over 100%, and big categories with no budget.",
|
|
53742
|
+
"4. Suggest changes. Call `set_budget` (categoryId from `list_entities` entity='categories') only for the changes I approve."
|
|
53743
|
+
].join(`
|
|
53744
|
+
`)
|
|
53745
|
+
}
|
|
53746
|
+
];
|
|
53747
|
+
function registerPrompts(server) {
|
|
53748
|
+
for (const p of PROMPTS) {
|
|
53749
|
+
const cb = (args) => ({
|
|
53750
|
+
messages: [{ role: "user", content: { type: "text", text: p.text(args) } }]
|
|
53751
|
+
});
|
|
53752
|
+
if (p.args) {
|
|
53753
|
+
server.registerPrompt(p.name, { title: p.title, description: p.description, argsSchema: p.args }, (args) => cb(args));
|
|
53754
|
+
} else {
|
|
53755
|
+
server.registerPrompt(p.name, { title: p.title, description: p.description }, () => cb({}));
|
|
53756
|
+
}
|
|
53757
|
+
}
|
|
53422
53758
|
}
|
|
53423
53759
|
|
|
53424
53760
|
// src/tools/alert-rules.ts
|
|
@@ -53477,11 +53813,7 @@ function alertTools(client) {
|
|
|
53477
53813
|
return { success: true, generated, unread };
|
|
53478
53814
|
}
|
|
53479
53815
|
}),
|
|
53480
|
-
manage.tool
|
|
53481
|
-
manage.alias("dismiss_alert", "dismiss"),
|
|
53482
|
-
manage.alias("mark_alert_read", "read"),
|
|
53483
|
-
manage.alias("list_alert_rules", "list_rules"),
|
|
53484
|
-
manage.alias("set_alert_rule", "set_rule")
|
|
53816
|
+
manage.tool
|
|
53485
53817
|
];
|
|
53486
53818
|
}
|
|
53487
53819
|
|
|
@@ -53810,7 +54142,7 @@ function debtTools(client) {
|
|
|
53810
54142
|
})
|
|
53811
54143
|
}
|
|
53812
54144
|
});
|
|
53813
|
-
return [manage.tool
|
|
54145
|
+
return [manage.tool];
|
|
53814
54146
|
}
|
|
53815
54147
|
|
|
53816
54148
|
// src/tools/accounts.ts
|
|
@@ -54571,11 +54903,7 @@ function reportTools(client) {
|
|
|
54571
54903
|
}),
|
|
54572
54904
|
handler: (body) => client.post("/api/reports/generate", body)
|
|
54573
54905
|
}),
|
|
54574
|
-
report2.tool
|
|
54575
|
-
report2.alias("get_reports", "monthly"),
|
|
54576
|
-
report2.alias("get_net_worth", "net_worth"),
|
|
54577
|
-
report2.alias("get_upcoming", "upcoming"),
|
|
54578
|
-
report2.alias("get_spending_heatmap", "heatmap")
|
|
54906
|
+
report2.tool
|
|
54579
54907
|
];
|
|
54580
54908
|
}
|
|
54581
54909
|
|
|
@@ -54654,56 +54982,7 @@ function entityTools(client, crypto3) {
|
|
|
54654
54982
|
fixed_deposit: investment.deleteFd
|
|
54655
54983
|
}
|
|
54656
54984
|
});
|
|
54657
|
-
return [
|
|
54658
|
-
list.tool,
|
|
54659
|
-
upsert.tool,
|
|
54660
|
-
del.tool,
|
|
54661
|
-
list.alias("list_categories", "categories"),
|
|
54662
|
-
list.alias("list_merchants", "merchants"),
|
|
54663
|
-
list.alias("list_bank_accounts", "bank_accounts"),
|
|
54664
|
-
list.alias("list_persons", "persons"),
|
|
54665
|
-
list.alias("list_budgets", "budgets"),
|
|
54666
|
-
list.alias("list_debts", "debts"),
|
|
54667
|
-
list.alias("list_invites", "invites"),
|
|
54668
|
-
list.alias("list_alerts", "alerts"),
|
|
54669
|
-
list.alias("list_recurring", "recurring"),
|
|
54670
|
-
list.alias("list_push_subscriptions", "push_subscriptions"),
|
|
54671
|
-
list.alias("list_saved_reports", "saved_reports"),
|
|
54672
|
-
aliasTool("list_learning", exports_external.object({ type: exports_external.enum(LEARNING_SLICES) }), "list_entities", 'entity: "learning", slice: <type>', ({ type }) => [list.tool, { entity: "learning", slice: type }]),
|
|
54673
|
-
aliasTool("list_savings", exports_external.object({
|
|
54674
|
-
type: exports_external.enum(["goals", "emis", "sinking_funds", "insurance", "contributions"]),
|
|
54675
|
-
id: exports_external.string().optional()
|
|
54676
|
-
}), "list_entities", "entity: <type> (contributions → goal_contributions)", ({ type, id }) => {
|
|
54677
|
-
if (type !== "contributions")
|
|
54678
|
-
return [list.tool, { entity: type }];
|
|
54679
|
-
if (!id)
|
|
54680
|
-
throw new Error("list_savings type='contributions' requires id (goal UUID)");
|
|
54681
|
-
return [list.tool, { entity: "goal_contributions", id }];
|
|
54682
|
-
}),
|
|
54683
|
-
aliasTool("list_investments", exports_external.object({
|
|
54684
|
-
type: exports_external.enum(["holdings", "sips", "fixed_deposits", "transactions"]),
|
|
54685
|
-
id: exports_external.string().optional()
|
|
54686
|
-
}), "list_entities", "entity: <type> (transactions → investment_transactions)", ({ type, id }) => {
|
|
54687
|
-
if (type !== "transactions")
|
|
54688
|
-
return [list.tool, { entity: type }];
|
|
54689
|
-
if (!id)
|
|
54690
|
-
throw new Error("list_investments type='transactions' requires id");
|
|
54691
|
-
return [list.tool, { entity: "investment_transactions", id }];
|
|
54692
|
-
}),
|
|
54693
|
-
upsert.alias("upsert_merchant", "merchant"),
|
|
54694
|
-
upsert.alias("upsert_person", "person"),
|
|
54695
|
-
upsert.alias("upsert_category", "category"),
|
|
54696
|
-
upsert.alias("upsert_bank_account", "bank_account"),
|
|
54697
|
-
upsert.alias("upsert_insurance", "insurance"),
|
|
54698
|
-
upsert.alias("create_invite", "invite"),
|
|
54699
|
-
del.alias("delete_category", "category"),
|
|
54700
|
-
del.alias("delete_merchant", "merchant"),
|
|
54701
|
-
del.alias("delete_person", "person"),
|
|
54702
|
-
del.alias("delete_debt", "debt"),
|
|
54703
|
-
del.alias("revoke_invite", "invite"),
|
|
54704
|
-
aliasTool("delete_savings", exports_external.object({ type: exports_external.enum(["goal", "emi", "insurance", "sinking_fund"]), id: exports_external.string() }), "delete_entity", "entity: <type>", ({ type, id }) => [del.tool, { entity: type, id }]),
|
|
54705
|
-
aliasTool("delete_investment", exports_external.object({ type: exports_external.enum(["sip", "fixed_deposit"]), id: exports_external.string() }), "delete_entity", "entity: <type>", ({ type, id }) => [del.tool, { entity: type, id }])
|
|
54706
|
-
];
|
|
54985
|
+
return [list.tool, upsert.tool, del.tool];
|
|
54707
54986
|
}
|
|
54708
54987
|
|
|
54709
54988
|
// ../reconciliation/src/aliases.ts
|
|
@@ -57279,22 +57558,22 @@ function parseMonthDayYear(raw) {
|
|
|
57279
57558
|
const match = raw.trim().match(/^(\w+)\s+(\d{1,2}),?\s+(\d{4})$/);
|
|
57280
57559
|
if (!match)
|
|
57281
57560
|
return null;
|
|
57282
|
-
const
|
|
57283
|
-
if (!
|
|
57561
|
+
const month2 = MONTH_MAP[match[1].toLowerCase()];
|
|
57562
|
+
if (!month2)
|
|
57284
57563
|
return null;
|
|
57285
57564
|
const day = parseInt(match[2], 10);
|
|
57286
57565
|
const year = parseInt(match[3], 10);
|
|
57287
|
-
const iso = `${year}-${String(
|
|
57288
|
-
return { month, day, year, iso };
|
|
57566
|
+
const iso = `${year}-${String(month2).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
57567
|
+
return { month: month2, day, year, iso };
|
|
57289
57568
|
}
|
|
57290
57569
|
function parseMonthDay(raw) {
|
|
57291
57570
|
const match = raw.trim().match(/^(\w+)\s+(\d{1,2})$/);
|
|
57292
57571
|
if (!match)
|
|
57293
57572
|
return null;
|
|
57294
|
-
const
|
|
57295
|
-
if (!
|
|
57573
|
+
const month2 = MONTH_MAP[match[1].toLowerCase()];
|
|
57574
|
+
if (!month2)
|
|
57296
57575
|
return null;
|
|
57297
|
-
return { month, day: parseInt(match[2], 10) };
|
|
57576
|
+
return { month: month2, day: parseInt(match[2], 10) };
|
|
57298
57577
|
}
|
|
57299
57578
|
function parseAmexPdfDate(raw, meta3) {
|
|
57300
57579
|
const parsed = parseMonthDay(raw);
|
|
@@ -57697,10 +57976,10 @@ function parseMonthNameDate(raw) {
|
|
|
57697
57976
|
const match = raw.trim().match(/^(\d{1,2})\s+(\w{3})\w*,?\s+(\d{4})$/);
|
|
57698
57977
|
if (!match)
|
|
57699
57978
|
return null;
|
|
57700
|
-
const
|
|
57701
|
-
if (!
|
|
57979
|
+
const month2 = MONTH_NAMES[match[2].toLowerCase().slice(0, 3)];
|
|
57980
|
+
if (!month2)
|
|
57702
57981
|
return null;
|
|
57703
|
-
return `${match[3]}-${
|
|
57982
|
+
return `${match[3]}-${month2}-${match[1].padStart(2, "0")}`;
|
|
57704
57983
|
}
|
|
57705
57984
|
function parseDdMmYyyy(raw) {
|
|
57706
57985
|
const match = raw.trim().match(/^(\d{2})\/(\d{2})\/(\d{4})/);
|
|
@@ -57998,13 +58277,13 @@ function parseHdfcDate(raw) {
|
|
|
57998
58277
|
if (!match)
|
|
57999
58278
|
return null;
|
|
58000
58279
|
const day = match[1];
|
|
58001
|
-
const
|
|
58280
|
+
const month2 = match[2];
|
|
58002
58281
|
let year = match[3];
|
|
58003
58282
|
if (year.length === 2) {
|
|
58004
58283
|
const num = parseInt(year, 10);
|
|
58005
58284
|
year = num > 50 ? `19${year}` : `20${year}`;
|
|
58006
58285
|
}
|
|
58007
|
-
return `${year}-${
|
|
58286
|
+
return `${year}-${month2}-${day}`;
|
|
58008
58287
|
}
|
|
58009
58288
|
function parseAmount3(raw) {
|
|
58010
58289
|
if (!raw)
|
|
@@ -59177,603 +59456,231 @@ function exportTools(client, crypto3) {
|
|
|
59177
59456
|
];
|
|
59178
59457
|
}
|
|
59179
59458
|
|
|
59180
|
-
// ../../node_modules/.bun/@
|
|
59181
|
-
var
|
|
59182
|
-
var
|
|
59183
|
-
|
|
59184
|
-
|
|
59185
|
-
|
|
59186
|
-
|
|
59187
|
-
|
|
59188
|
-
|
|
59189
|
-
|
|
59190
|
-
|
|
59191
|
-
|
|
59192
|
-
|
|
59193
|
-
|
|
59194
|
-
|
|
59195
|
-
|
|
59196
|
-
|
|
59197
|
-
|
|
59198
|
-
|
|
59199
|
-
|
|
59200
|
-
|
|
59201
|
-
|
|
59202
|
-
|
|
59203
|
-
return
|
|
59204
|
-
|
|
59205
|
-
|
|
59206
|
-
|
|
59207
|
-
|
|
59208
|
-
|
|
59209
|
-
|
|
59210
|
-
|
|
59211
|
-
|
|
59212
|
-
|
|
59213
|
-
|
|
59214
|
-
|
|
59215
|
-
|
|
59216
|
-
return
|
|
59217
|
-
}
|
|
59218
|
-
|
|
59219
|
-
|
|
59220
|
-
|
|
59221
|
-
|
|
59222
|
-
|
|
59223
|
-
|
|
59224
|
-
|
|
59225
|
-
|
|
59226
|
-
if (
|
|
59227
|
-
|
|
59228
|
-
|
|
59229
|
-
|
|
59230
|
-
|
|
59231
|
-
|
|
59232
|
-
|
|
59233
|
-
|
|
59234
|
-
|
|
59235
|
-
|
|
59236
|
-
|
|
59237
|
-
|
|
59238
|
-
|
|
59239
|
-
|
|
59240
|
-
|
|
59241
|
-
|
|
59242
|
-
|
|
59243
|
-
|
|
59244
|
-
|
|
59245
|
-
|
|
59246
|
-
}
|
|
59247
|
-
|
|
59248
|
-
|
|
59249
|
-
|
|
59250
|
-
|
|
59251
|
-
|
|
59252
|
-
return ms;
|
|
59253
|
-
const raw = headers.get("retry-after");
|
|
59254
|
-
if (raw === null)
|
|
59255
|
-
return;
|
|
59256
|
-
const seconds = Number(raw);
|
|
59257
|
-
if (Number.isFinite(seconds))
|
|
59258
|
-
return seconds >= 0 ? seconds * 1000 : undefined;
|
|
59259
|
-
const date5 = Date.parse(raw);
|
|
59260
|
-
if (!Number.isNaN(date5))
|
|
59261
|
-
return Math.max(0, date5 - now);
|
|
59262
|
-
};
|
|
59263
|
-
var retryDelayMs = (attempt, headers, policy = DEFAULT_RETRY_POLICY, random = Math.random) => {
|
|
59264
|
-
if (policy.respectRetryAfter && headers !== undefined) {
|
|
59265
|
-
const retryAfter = parseRetryAfter(headers);
|
|
59266
|
-
if (retryAfter !== undefined && retryAfter <= policy.maxRetryAfterMs)
|
|
59267
|
-
return retryAfter;
|
|
59268
|
-
}
|
|
59269
|
-
const exponential = Math.min(policy.backoffInitialMs * 2 ** attempt, policy.backoffMaxMs);
|
|
59270
|
-
return Math.round(exponential * (1 - random() * policy.backoffJitter));
|
|
59271
|
-
};
|
|
59272
|
-
var sleep3 = (ms, signal) => new Promise((resolve, reject) => {
|
|
59273
|
-
if (signal?.aborted)
|
|
59274
|
-
return reject(signal.reason);
|
|
59275
|
-
const onAbort = () => {
|
|
59276
|
-
clearTimeout(timer);
|
|
59277
|
-
reject(signal?.reason);
|
|
59278
|
-
};
|
|
59279
|
-
const timer = setTimeout(() => {
|
|
59280
|
-
signal?.removeEventListener("abort", onAbort);
|
|
59281
|
-
resolve();
|
|
59282
|
-
}, ms);
|
|
59283
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
59284
|
-
});
|
|
59285
|
-
var TypeSafeError = class extends Error {
|
|
59286
|
-
constructor(message, options) {
|
|
59287
|
-
super(message, options);
|
|
59288
|
-
this.name = new.target.name;
|
|
59289
|
-
}
|
|
59290
|
-
};
|
|
59291
|
-
var isRecord = (value) => typeof value === "object" && value !== null;
|
|
59292
|
-
var extractMessage = (body) => {
|
|
59293
|
-
if (typeof body === "string")
|
|
59294
|
-
return body || undefined;
|
|
59295
|
-
if (!isRecord(body))
|
|
59296
|
-
return;
|
|
59297
|
-
const { error: error62, message, detail } = body;
|
|
59298
|
-
if (typeof error62 === "string")
|
|
59299
|
-
return error62;
|
|
59300
|
-
if (isRecord(error62) && typeof error62.message === "string")
|
|
59301
|
-
return error62.message;
|
|
59302
|
-
if (typeof message === "string")
|
|
59303
|
-
return message;
|
|
59304
|
-
if (typeof detail === "string")
|
|
59305
|
-
return detail;
|
|
59306
|
-
if (isRecord(detail) && typeof detail.message === "string")
|
|
59307
|
-
return detail.message;
|
|
59308
|
-
if (Array.isArray(detail))
|
|
59309
|
-
return describeValidationErrors(detail);
|
|
59310
|
-
};
|
|
59311
|
-
var describeValidationErrors = (errors3) => {
|
|
59312
|
-
const parts = errors3.flatMap((e2) => {
|
|
59313
|
-
if (!isRecord(e2) || typeof e2.msg !== "string")
|
|
59314
|
-
return [];
|
|
59315
|
-
const loc = Array.isArray(e2.loc) ? e2.loc.filter((x2) => x2 !== "body").join(".") : "";
|
|
59316
|
-
return [loc ? `${loc}: ${e2.msg}` : e2.msg];
|
|
59317
|
-
});
|
|
59318
|
-
return parts.length > 0 ? parts.join("; ") : undefined;
|
|
59319
|
-
};
|
|
59320
|
-
var MAX_RAW_BODY_IN_MESSAGE = 200;
|
|
59321
|
-
var APIError = class APIError2 extends TypeSafeError {
|
|
59322
|
-
status;
|
|
59323
|
-
headers;
|
|
59324
|
-
body;
|
|
59325
|
-
requestId;
|
|
59326
|
-
constructor(status, body, headers, message) {
|
|
59327
|
-
super(message ?? APIError2.describe(status, body));
|
|
59328
|
-
this.status = status;
|
|
59329
|
-
this.body = body;
|
|
59330
|
-
this.headers = headers;
|
|
59331
|
-
this.requestId = requestIdFrom(headers);
|
|
59332
|
-
}
|
|
59333
|
-
static describe(status, body) {
|
|
59334
|
-
const detail = extractMessage(body);
|
|
59335
|
-
if (detail)
|
|
59336
|
-
return `${status} ${detail}`;
|
|
59337
|
-
if (body === undefined)
|
|
59338
|
-
return `${status} status code (no body)`;
|
|
59339
|
-
const raw = typeof body === "string" ? body : JSON.stringify(body);
|
|
59340
|
-
return `${status} ${raw.length > MAX_RAW_BODY_IN_MESSAGE ? `${raw.slice(0, MAX_RAW_BODY_IN_MESSAGE)}…` : raw}`;
|
|
59341
|
-
}
|
|
59342
|
-
static fromResponse(status, body, headers) {
|
|
59343
|
-
if (status === 400)
|
|
59344
|
-
return new BadRequestError(status, body, headers);
|
|
59345
|
-
if (status === 401)
|
|
59346
|
-
return new AuthenticationError(status, body, headers);
|
|
59347
|
-
if (status === 403)
|
|
59348
|
-
return new PermissionDeniedError(status, body, headers);
|
|
59349
|
-
if (status === 404)
|
|
59350
|
-
return new NotFoundError(status, body, headers);
|
|
59351
|
-
if (status === 422)
|
|
59352
|
-
return new UnprocessableEntityError(status, body, headers);
|
|
59353
|
-
if (status === 429)
|
|
59354
|
-
return new RateLimitError(status, body, headers);
|
|
59355
|
-
if (status >= 500)
|
|
59356
|
-
return new InternalServerError(status, body, headers);
|
|
59357
|
-
return new APIError2(status, body, headers);
|
|
59358
|
-
}
|
|
59359
|
-
};
|
|
59360
|
-
var BadRequestError = class extends APIError {
|
|
59361
|
-
};
|
|
59362
|
-
var AuthenticationError = class extends APIError {
|
|
59363
|
-
};
|
|
59364
|
-
var PermissionDeniedError = class extends APIError {
|
|
59365
|
-
};
|
|
59366
|
-
var NotFoundError = class extends APIError {
|
|
59367
|
-
};
|
|
59368
|
-
var UnprocessableEntityError = class extends APIError {
|
|
59369
|
-
};
|
|
59370
|
-
var RateLimitError = class extends APIError {
|
|
59371
|
-
retryAfterMs = parseRetryAfter(this.headers);
|
|
59372
|
-
};
|
|
59373
|
-
var InternalServerError = class extends APIError {
|
|
59374
|
-
};
|
|
59375
|
-
var APIConnectionError = class extends TypeSafeError {
|
|
59376
|
-
constructor(message = "Connection error.", options) {
|
|
59377
|
-
super(message, options);
|
|
59378
|
-
}
|
|
59379
|
-
};
|
|
59380
|
-
var APITimeoutError = class extends APIConnectionError {
|
|
59381
|
-
timeoutMs;
|
|
59382
|
-
constructor(timeoutMs, options) {
|
|
59383
|
-
super(`Request timed out after ${timeoutMs}ms.`, options);
|
|
59384
|
-
this.timeoutMs = timeoutMs;
|
|
59385
|
-
}
|
|
59386
|
-
};
|
|
59387
|
-
var APIUserAbortError = class extends TypeSafeError {
|
|
59388
|
-
constructor(message = "Request was aborted.", options) {
|
|
59389
|
-
super(message, options);
|
|
59390
|
-
}
|
|
59391
|
-
};
|
|
59392
|
-
var LOG_LEVELS = [
|
|
59393
|
-
"debug",
|
|
59394
|
-
"info",
|
|
59395
|
-
"warn",
|
|
59396
|
-
"error",
|
|
59397
|
-
"off"
|
|
59398
|
-
];
|
|
59399
|
-
var DEFAULT_LOG_LEVEL = "warn";
|
|
59400
|
-
var isLogLevel = (value) => LOG_LEVELS.includes(value);
|
|
59401
|
-
var parseLogLevel = (value, source) => {
|
|
59402
|
-
if (isLogLevel(value))
|
|
59403
|
-
return value;
|
|
59404
|
-
throw new TypeSafeError(`Invalid log level "${value}" from ${source}. Expected one of: ${LOG_LEVELS.join(", ")}.`);
|
|
59405
|
-
};
|
|
59406
|
-
var PREFIX = "[typesafe-sdk]";
|
|
59407
|
-
var consoleLogger = {
|
|
59408
|
-
debug: (message, ...args) => console.debug(`${PREFIX} ${message}`, ...args),
|
|
59409
|
-
info: (message, ...args) => console.info(`${PREFIX} ${message}`, ...args),
|
|
59410
|
-
warn: (message, ...args) => console.warn(`${PREFIX} ${message}`, ...args),
|
|
59411
|
-
error: (message, ...args) => console.error(`${PREFIX} ${message}`, ...args)
|
|
59412
|
-
};
|
|
59413
|
-
var RANK = {
|
|
59414
|
-
debug: 0,
|
|
59415
|
-
info: 1,
|
|
59416
|
-
warn: 2,
|
|
59417
|
-
error: 3,
|
|
59418
|
-
off: 4
|
|
59419
|
-
};
|
|
59420
|
-
var drop = () => {};
|
|
59421
|
-
var withLevel = (sink, level) => {
|
|
59422
|
-
const enabled = (at2) => RANK[at2] >= RANK[level];
|
|
59423
|
-
return {
|
|
59424
|
-
debug: enabled("debug") ? (message, ...args) => sink.debug(message, ...args) : drop,
|
|
59425
|
-
info: enabled("info") ? (message, ...args) => sink.info(message, ...args) : drop,
|
|
59426
|
-
warn: enabled("warn") ? (message, ...args) => sink.warn(message, ...args) : drop,
|
|
59427
|
-
error: enabled("error") ? (message, ...args) => sink.error(message, ...args) : drop
|
|
59428
|
-
};
|
|
59429
|
-
};
|
|
59430
|
-
var KEY_HEADERS = /* @__PURE__ */ new Set([
|
|
59431
|
-
"authorization",
|
|
59432
|
-
"proxy-authorization",
|
|
59433
|
-
"x-api-key"
|
|
59434
|
-
]);
|
|
59435
|
-
var OPAQUE_HEADERS = /* @__PURE__ */ new Set(["cookie", "set-cookie"]);
|
|
59436
|
-
var redactKey = (value) => {
|
|
59437
|
-
const [scheme, secret] = value.includes(" ") ? value.split(/\s+/, 2) : [undefined, value];
|
|
59438
|
-
const tail = secret && secret.length > 8 ? secret.slice(-4) : "";
|
|
59439
|
-
return `${scheme ? `${scheme} ` : ""}***${tail}`;
|
|
59440
|
-
};
|
|
59441
|
-
var redact = (name, value) => {
|
|
59442
|
-
const lower = name.toLowerCase();
|
|
59443
|
-
if (KEY_HEADERS.has(lower))
|
|
59444
|
-
return redactKey(value);
|
|
59445
|
-
if (OPAQUE_HEADERS.has(lower))
|
|
59446
|
-
return "***";
|
|
59447
|
-
return value;
|
|
59448
|
-
};
|
|
59449
|
-
var redactHeaders = (headers) => Object.fromEntries(Object.entries(headers).map(([name, value]) => [name, redact(name, value)]));
|
|
59450
|
-
var choice = (instructions, criteria) => {
|
|
59451
|
-
if (Array.isArray(criteria))
|
|
59452
|
-
throw new TypeSafeError("Choice criteria must be a map of labels to descriptions, not a list.");
|
|
59453
|
-
return {
|
|
59454
|
-
type: "choice",
|
|
59455
|
-
instructions,
|
|
59456
|
-
criteria
|
|
59457
|
-
};
|
|
59458
|
-
};
|
|
59459
|
-
var validateQuestions = (questions) => {
|
|
59460
|
-
if (Object.keys(questions).length === 0)
|
|
59461
|
-
throw new TypeSafeError("At least one question is required.");
|
|
59462
|
-
for (const [name, question] of Object.entries(questions)) {
|
|
59463
|
-
if (question.type !== "score")
|
|
59464
|
-
continue;
|
|
59465
|
-
if (!Array.isArray(question.criteria))
|
|
59466
|
-
throw new TypeSafeError(`Score question "${name}" has criteria that are not a list; score criteria must be a list of descriptions indexed by score from zero.`);
|
|
59467
|
-
if (question.criteria.length < 2)
|
|
59468
|
-
throw new TypeSafeError(`Score question "${name}" has ${question.criteria.length} criteria; at least two scores are required.`);
|
|
59469
|
-
}
|
|
59470
|
-
};
|
|
59471
|
-
var Models = class {
|
|
59472
|
-
#transport;
|
|
59473
|
-
constructor(transport) {
|
|
59474
|
-
this.#transport = transport;
|
|
59475
|
-
}
|
|
59476
|
-
list(options = {}) {
|
|
59477
|
-
return this.#transport.request("GET", "/v1/models", options).map(unwrapModels);
|
|
59478
|
-
}
|
|
59479
|
-
};
|
|
59480
|
-
var unwrapModels = (wire) => {
|
|
59481
|
-
if (Array.isArray(wire?.models))
|
|
59482
|
-
return wire.models;
|
|
59483
|
-
throw new TypeSafeError("Unexpected response shape from GET /v1/models; expected { models: [...] }.");
|
|
59484
|
-
};
|
|
59485
|
-
var g2 = globalThis;
|
|
59486
|
-
var isBrowser = () => typeof g2.window !== "undefined" && typeof g2.window.document !== "undefined" && typeof g2.navigator !== "undefined";
|
|
59487
|
-
var describeRuntime = () => {
|
|
59488
|
-
const platform = g2.process?.platform && g2.process?.arch ? ` (${g2.process.platform}; ${g2.process.arch})` : "";
|
|
59489
|
-
if (g2.Bun?.version)
|
|
59490
|
-
return `bun/${g2.Bun.version}${platform}`;
|
|
59491
|
-
if (g2.Deno?.version?.deno)
|
|
59492
|
-
return `deno/${g2.Deno.version.deno}${platform}`;
|
|
59493
|
-
if (g2.EdgeRuntime !== undefined)
|
|
59494
|
-
return "vercel-edge";
|
|
59495
|
-
if (g2.navigator?.userAgent === "Cloudflare-Workers")
|
|
59496
|
-
return "cloudflare-workers";
|
|
59497
|
-
if (g2.process?.versions?.node)
|
|
59498
|
-
return `node/${g2.process.versions.node}${platform}`;
|
|
59499
|
-
if (isBrowser())
|
|
59500
|
-
return "browser";
|
|
59501
|
-
return "unknown";
|
|
59502
|
-
};
|
|
59503
|
-
var VERSION2 = "0.6.0";
|
|
59504
|
-
var missingApiKey = () => {
|
|
59505
|
-
throw new TypeSafeError(`No API key was provided. Pass \`apiKey\` to the TypeSafeClient constructor or set the ${ENV.apiKey} environment variable.`);
|
|
59506
|
-
};
|
|
59507
|
-
var missingFetch = () => {
|
|
59508
|
-
throw new TypeSafeError("No global `fetch` is available in this runtime. Pass a `fetch` implementation to the TypeSafeClient constructor.");
|
|
59509
|
-
};
|
|
59510
|
-
var refuseBrowser = () => {
|
|
59511
|
-
throw new TypeSafeError("TypeSafeClient is running in a browser, which would expose your API key to anyone using the page. Call the API from a server instead, or pass `dangerouslyAllowBrowser: true` if you understand the risk.");
|
|
59512
|
-
};
|
|
59513
|
-
var defaultFetch = (input2, init) => globalThis.fetch(input2, init);
|
|
59514
|
-
var assertNonNegativeInteger = (name, value) => {
|
|
59515
|
-
if (!Number.isInteger(value) || value < 0)
|
|
59516
|
-
throw new TypeSafeError(`\`${name}\` must be a non-negative integer, got ${String(value)}.`);
|
|
59517
|
-
return value;
|
|
59518
|
-
};
|
|
59519
|
-
var assertPositiveMs = (name, value) => {
|
|
59520
|
-
if (!Number.isFinite(value) || value <= 0)
|
|
59521
|
-
throw new TypeSafeError(`\`${name}\` must be a positive number of milliseconds, got ${String(value)}.`);
|
|
59522
|
-
return value;
|
|
59523
|
-
};
|
|
59524
|
-
var assertNonNegativeMs = (name, value) => {
|
|
59525
|
-
if (!Number.isFinite(value) || value < 0)
|
|
59526
|
-
throw new TypeSafeError(`\`${name}\` must be a non-negative number of milliseconds, got ${String(value)}.`);
|
|
59527
|
-
return value;
|
|
59528
|
-
};
|
|
59529
|
-
var assertFraction = (name, value) => {
|
|
59530
|
-
if (!Number.isFinite(value) || value < 0 || value > 1)
|
|
59531
|
-
throw new TypeSafeError(`\`${name}\` must be between 0 and 1, got ${String(value)}.`);
|
|
59532
|
-
return value;
|
|
59533
|
-
};
|
|
59534
|
-
var assertStatusSet = (name, statuses) => {
|
|
59535
|
-
for (const status of statuses)
|
|
59536
|
-
if (!Number.isInteger(status) || status < 100 || status > 999)
|
|
59537
|
-
throw new TypeSafeError(`\`${name}\` must contain HTTP status codes, got ${String(status)}.`);
|
|
59538
|
-
return statuses;
|
|
59539
|
-
};
|
|
59540
|
-
var resolveRetryPolicy = (base, overrides) => {
|
|
59541
|
-
const o2 = overrides ?? {};
|
|
59542
|
-
return {
|
|
59543
|
-
maxRetries: o2.maxRetries === undefined ? base.maxRetries : assertNonNegativeInteger("retry.maxRetries", o2.maxRetries),
|
|
59544
|
-
backoffInitialMs: o2.backoffInitialMs === undefined ? base.backoffInitialMs : assertNonNegativeMs("retry.backoffInitialMs", o2.backoffInitialMs),
|
|
59545
|
-
backoffMaxMs: o2.backoffMaxMs === undefined ? base.backoffMaxMs : assertNonNegativeMs("retry.backoffMaxMs", o2.backoffMaxMs),
|
|
59546
|
-
backoffJitter: o2.backoffJitter === undefined ? base.backoffJitter : assertFraction("retry.backoffJitter", o2.backoffJitter),
|
|
59547
|
-
httpStatuses: new Set(o2.httpStatuses === undefined ? base.httpStatuses : assertStatusSet("retry.httpStatuses", o2.httpStatuses)),
|
|
59548
|
-
respectRetryAfter: o2.respectRetryAfter ?? base.respectRetryAfter,
|
|
59549
|
-
maxRetryAfterMs: o2.maxRetryAfterMs === undefined ? base.maxRetryAfterMs : assertNonNegativeMs("retry.maxRetryAfterMs", o2.maxRetryAfterMs),
|
|
59550
|
-
apiConnectionError: o2.apiConnectionError ?? base.apiConnectionError,
|
|
59551
|
-
apiTimeoutError: o2.apiTimeoutError ?? base.apiTimeoutError
|
|
59552
|
-
};
|
|
59553
|
-
};
|
|
59554
|
-
var isRetryableError = (err, policy) => {
|
|
59555
|
-
if (err instanceof APITimeoutError)
|
|
59556
|
-
return policy.apiTimeoutError;
|
|
59557
|
-
if (err instanceof APIConnectionError)
|
|
59558
|
-
return policy.apiConnectionError;
|
|
59559
|
-
return false;
|
|
59560
|
-
};
|
|
59561
|
-
var resolveLogLevel = (fromCode) => {
|
|
59562
|
-
if (fromCode !== undefined)
|
|
59563
|
-
return parseLogLevel(fromCode, "the `logLevel` option");
|
|
59564
|
-
const fromEnv = readEnv(ENV.logLevel);
|
|
59565
|
-
if (fromEnv !== undefined)
|
|
59566
|
-
return parseLogLevel(fromEnv, ENV.logLevel);
|
|
59567
|
-
return DEFAULT_LOG_LEVEL;
|
|
59568
|
-
};
|
|
59569
|
-
var stripTrailingSlashes = (url3) => url3.replace(/\/+$/, "");
|
|
59570
|
-
var mergeHeaders = (...sources) => {
|
|
59571
|
-
const entries = /* @__PURE__ */ new Map;
|
|
59572
|
-
for (const source of sources)
|
|
59573
|
-
for (const [name, value] of Object.entries(source))
|
|
59574
|
-
if (value === undefined)
|
|
59575
|
-
entries.delete(name.toLowerCase());
|
|
59576
|
-
else
|
|
59577
|
-
entries.set(name.toLowerCase(), [name, value]);
|
|
59578
|
-
return Object.fromEntries(entries.values());
|
|
59579
|
-
};
|
|
59580
|
-
var bufferResponse = async (response, signal) => {
|
|
59581
|
-
const reader = response.clone().body?.getReader();
|
|
59582
|
-
if (!reader)
|
|
59583
|
-
return;
|
|
59584
|
-
const cancel = () => {
|
|
59585
|
-
reader.cancel(signal.reason).catch(() => {});
|
|
59586
|
-
response.body?.cancel(signal.reason).catch(() => {});
|
|
59587
|
-
};
|
|
59588
|
-
signal.addEventListener("abort", cancel, { once: true });
|
|
59589
|
-
try {
|
|
59590
|
-
if (signal.aborted)
|
|
59591
|
-
cancel();
|
|
59592
|
-
signal.throwIfAborted();
|
|
59593
|
-
while (!(await reader.read()).done)
|
|
59594
|
-
signal.throwIfAborted();
|
|
59595
|
-
signal.throwIfAborted();
|
|
59596
|
-
} finally {
|
|
59597
|
-
signal.removeEventListener("abort", cancel);
|
|
59598
|
-
reader.releaseLock();
|
|
59599
|
-
}
|
|
59600
|
-
};
|
|
59601
|
-
var RUNTIME = describeRuntime();
|
|
59602
|
-
var TypeSafeClient = class {
|
|
59603
|
-
#apiKey;
|
|
59604
|
-
baseURL;
|
|
59605
|
-
defaultModel;
|
|
59606
|
-
logLevel;
|
|
59607
|
-
logger;
|
|
59608
|
-
retry;
|
|
59609
|
-
timeout;
|
|
59610
|
-
defaultHeaders;
|
|
59611
|
-
fetch;
|
|
59612
|
-
models;
|
|
59613
|
-
#requestCount = 0;
|
|
59614
|
-
constructor(config2 = {}) {
|
|
59615
|
-
if (isBrowser() && !config2.dangerouslyAllowBrowser)
|
|
59616
|
-
refuseBrowser();
|
|
59617
|
-
this.#apiKey = fromCodeOrEnv(config2.apiKey, ENV.apiKey) ?? missingApiKey();
|
|
59618
|
-
this.baseURL = stripTrailingSlashes(fromCodeOrEnv(config2.baseURL, ENV.baseURL) ?? "https://api.typesafe.ai");
|
|
59619
|
-
this.defaultModel = fromCodeOrEnv(config2.defaultModel, ENV.defaultModel) ?? "jev-latest";
|
|
59620
|
-
this.logLevel = resolveLogLevel(config2.logLevel);
|
|
59621
|
-
this.logger = withLevel(config2.logger ?? consoleLogger, this.logLevel);
|
|
59622
|
-
this.retry = resolveRetryPolicy(DEFAULT_RETRY_POLICY, config2.retry);
|
|
59623
|
-
this.timeout = assertPositiveMs("timeout", config2.timeout ?? 1e4);
|
|
59624
|
-
this.defaultHeaders = { ...config2.defaultHeaders };
|
|
59625
|
-
if (config2.fetch === undefined && typeof globalThis.fetch !== "function")
|
|
59626
|
-
missingFetch();
|
|
59627
|
-
this.fetch = config2.fetch ?? defaultFetch;
|
|
59628
|
-
const transport = {
|
|
59629
|
-
request: (method, path, options) => this.#request(method, path, options),
|
|
59630
|
-
defaultModel: this.defaultModel
|
|
59631
|
-
};
|
|
59632
|
-
this.models = new Models(transport);
|
|
59633
|
-
}
|
|
59634
|
-
systemOne(request, options = {}) {
|
|
59635
|
-
validateQuestions(request.questions);
|
|
59636
|
-
const body = {
|
|
59637
|
-
...request,
|
|
59638
|
-
model: request.model ?? this.defaultModel
|
|
59639
|
-
};
|
|
59640
|
-
return this.#request("POST", "/v1/systemone", {
|
|
59641
|
-
...options,
|
|
59642
|
-
body
|
|
59643
|
-
});
|
|
59459
|
+
// ../../node_modules/.bun/@nimit9+signet-ai@0.1.11+a8a356da5edd5fb9/node_modules/@nimit9/signet-ai/dist/jev.js
|
|
59460
|
+
var JEV_DEFAULT_MODEL = "jev-1.13.0";
|
|
59461
|
+
var JEV_DEFAULT_MIN_CONFIDENCE = 0.95;
|
|
59462
|
+
var JEV_DEFAULT_TIMEOUT_MS = 8000;
|
|
59463
|
+
var JEV_DEFAULT_BASE_URL = "https://api.typesafe.ai";
|
|
59464
|
+
function fallbackOf(q2, reason, extra = {}) {
|
|
59465
|
+
const d2 = { value: q2.fallback, confident: false, source: "fallback", reason };
|
|
59466
|
+
if (extra.confidence !== undefined)
|
|
59467
|
+
d2.confidence = extra.confidence;
|
|
59468
|
+
if (extra.answer !== undefined)
|
|
59469
|
+
d2.answer = extra.answer;
|
|
59470
|
+
if (extra.probability !== undefined)
|
|
59471
|
+
d2.probability = extra.probability;
|
|
59472
|
+
return d2;
|
|
59473
|
+
}
|
|
59474
|
+
function labelsOf(options) {
|
|
59475
|
+
return Array.isArray(options) ? [...options] : Object.keys(options);
|
|
59476
|
+
}
|
|
59477
|
+
function isUnit(n2) {
|
|
59478
|
+
return typeof n2 === "number" && Number.isFinite(n2) && n2 >= 0 && n2 <= 1;
|
|
59479
|
+
}
|
|
59480
|
+
function validQuestion(q2) {
|
|
59481
|
+
if (!q2 || typeof q2.question !== "string")
|
|
59482
|
+
return false;
|
|
59483
|
+
if (q2.type === "choice") {
|
|
59484
|
+
const labels = labelsOf(q2.options);
|
|
59485
|
+
return labels.length >= 1 && labels.length <= 255 && labels.includes(q2.fallback);
|
|
59486
|
+
}
|
|
59487
|
+
if (q2.type === "score") {
|
|
59488
|
+
return Array.isArray(q2.levels) && q2.levels.length >= 2 && q2.levels.length <= 10;
|
|
59489
|
+
}
|
|
59490
|
+
return q2.type === "noul";
|
|
59491
|
+
}
|
|
59492
|
+
function toWire(q2) {
|
|
59493
|
+
if (q2.type === "choice") {
|
|
59494
|
+
const criteria = Array.isArray(q2.options) ? Object.fromEntries(q2.options.map((l2) => [l2, null])) : q2.options;
|
|
59495
|
+
return { type: "choice", instructions: q2.question, criteria };
|
|
59496
|
+
}
|
|
59497
|
+
if (q2.type === "score")
|
|
59498
|
+
return { type: "score", instructions: q2.question, criteria: q2.levels };
|
|
59499
|
+
return { type: "noul", instructions: q2.question };
|
|
59500
|
+
}
|
|
59501
|
+
function judge(q2, raw, min) {
|
|
59502
|
+
const a2 = raw;
|
|
59503
|
+
if (!a2 || typeof a2 !== "object")
|
|
59504
|
+
return fallbackOf(q2, "invalid_response");
|
|
59505
|
+
if (q2.type === "noul") {
|
|
59506
|
+
const p2 = a2.noul;
|
|
59507
|
+
if (!isUnit(p2))
|
|
59508
|
+
return fallbackOf(q2, "invalid_response");
|
|
59509
|
+
const confidence2 = Math.max(p2, 1 - p2);
|
|
59510
|
+
const answer2 = p2 >= 0.5;
|
|
59511
|
+
if (confidence2 < min) {
|
|
59512
|
+
return fallbackOf(q2, "low_confidence", { confidence: confidence2, answer: answer2, probability: p2 });
|
|
59513
|
+
}
|
|
59514
|
+
return { value: answer2, confident: true, source: "jev", confidence: confidence2, probability: p2 };
|
|
59515
|
+
}
|
|
59516
|
+
const confidence = a2.confidence;
|
|
59517
|
+
if (!isUnit(confidence))
|
|
59518
|
+
return fallbackOf(q2, "invalid_response");
|
|
59519
|
+
let answer;
|
|
59520
|
+
if (q2.type === "choice") {
|
|
59521
|
+
if (typeof a2.choice !== "string" || !labelsOf(q2.options).includes(a2.choice)) {
|
|
59522
|
+
return fallbackOf(q2, "invalid_response", { confidence });
|
|
59523
|
+
}
|
|
59524
|
+
answer = a2.choice;
|
|
59525
|
+
} else {
|
|
59526
|
+
const s2 = a2.score;
|
|
59527
|
+
if (typeof s2 !== "number" || !Number.isFinite(s2) || s2 < 0 || s2 > q2.levels.length - 1) {
|
|
59528
|
+
return fallbackOf(q2, "invalid_response", { confidence });
|
|
59529
|
+
}
|
|
59530
|
+
answer = s2;
|
|
59644
59531
|
}
|
|
59645
|
-
|
|
59646
|
-
|
|
59647
|
-
|
|
59648
|
-
|
|
59649
|
-
|
|
59650
|
-
|
|
59651
|
-
|
|
59652
|
-
|
|
59653
|
-
|
|
59654
|
-
|
|
59655
|
-
|
|
59656
|
-
|
|
59657
|
-
|
|
59658
|
-
|
|
59659
|
-
|
|
59660
|
-
|
|
59532
|
+
if (confidence < min)
|
|
59533
|
+
return fallbackOf(q2, "low_confidence", { confidence, answer });
|
|
59534
|
+
return { value: answer, confident: true, source: "jev", confidence };
|
|
59535
|
+
}
|
|
59536
|
+
|
|
59537
|
+
class Timeout {
|
|
59538
|
+
}
|
|
59539
|
+
function createJev(config2) {
|
|
59540
|
+
const apiKey = typeof config2.apiKey === "string" ? config2.apiKey.trim() : "";
|
|
59541
|
+
const model = config2.model ?? JEV_DEFAULT_MODEL;
|
|
59542
|
+
const floor = config2.minConfidence ?? JEV_DEFAULT_MIN_CONFIDENCE;
|
|
59543
|
+
const timeoutMs = config2.timeoutMs ?? JEV_DEFAULT_TIMEOUT_MS;
|
|
59544
|
+
const allowSensitive = config2.allowSensitive === true;
|
|
59545
|
+
const url3 = `${(config2.baseUrl ?? JEV_DEFAULT_BASE_URL).replace(/\/+$/, "")}/v1/systemone`;
|
|
59546
|
+
function emit(event) {
|
|
59547
|
+
if (!config2.onEvent)
|
|
59548
|
+
return;
|
|
59549
|
+
try {
|
|
59550
|
+
config2.onEvent(event);
|
|
59551
|
+
} catch {}
|
|
59661
59552
|
}
|
|
59662
|
-
|
|
59663
|
-
const
|
|
59664
|
-
const
|
|
59665
|
-
|
|
59666
|
-
|
|
59667
|
-
|
|
59668
|
-
|
|
59669
|
-
|
|
59670
|
-
|
|
59671
|
-
|
|
59672
|
-
});
|
|
59673
|
-
const body = req.body === undefined ? undefined : JSON.stringify(req.body);
|
|
59674
|
-
for (let attempt = 0;; attempt++) {
|
|
59675
|
-
const retriesLeft = req.retry.maxRetries - attempt;
|
|
59676
|
-
const attemptHeaders = attempt === 0 ? headers : {
|
|
59677
|
-
...headers,
|
|
59678
|
-
"X-TypeSafe-Retry-Count": String(attempt)
|
|
59553
|
+
function finish(questions, decisions, meta3) {
|
|
59554
|
+
const durationMs = Date.now() - meta3.started;
|
|
59555
|
+
for (const [name, d2] of Object.entries(decisions)) {
|
|
59556
|
+
const event = {
|
|
59557
|
+
kind: questions[name].type,
|
|
59558
|
+
name,
|
|
59559
|
+
source: d2.source,
|
|
59560
|
+
sensitive: meta3.sensitive,
|
|
59561
|
+
model,
|
|
59562
|
+
durationMs
|
|
59679
59563
|
};
|
|
59680
|
-
|
|
59681
|
-
|
|
59682
|
-
|
|
59683
|
-
|
|
59684
|
-
|
|
59685
|
-
|
|
59686
|
-
|
|
59687
|
-
res = await this.attempt(tag, url3, {
|
|
59688
|
-
method: req.method,
|
|
59689
|
-
headers: attemptHeaders,
|
|
59690
|
-
body
|
|
59691
|
-
}, req);
|
|
59692
|
-
} catch (err) {
|
|
59693
|
-
if (err instanceof APIUserAbortError || retriesLeft <= 0)
|
|
59694
|
-
throw err;
|
|
59695
|
-
if (!isRetryableError(err, req.retry))
|
|
59696
|
-
throw err;
|
|
59697
|
-
await this.backOff(tag, attempt, retriesLeft, err.message, undefined, req);
|
|
59698
|
-
continue;
|
|
59699
|
-
}
|
|
59700
|
-
const requestId = requestIdFrom(res.headers);
|
|
59701
|
-
this.logger.info(`${tag} <- ${res.status} in ${Date.now() - started}ms${requestId ? ` (request ${requestId})` : ""}`);
|
|
59702
|
-
if (res.ok)
|
|
59703
|
-
return res;
|
|
59704
|
-
const errorBody = await parseBody(res);
|
|
59705
|
-
this.logger.debug(`${tag} <- error body`, errorBody);
|
|
59706
|
-
const error62 = APIError.fromResponse(res.status, errorBody, res.headers);
|
|
59707
|
-
if (retriesLeft <= 0 || !isRetryableStatus(res.status, req.retry))
|
|
59708
|
-
throw error62;
|
|
59709
|
-
await this.backOff(tag, attempt, retriesLeft, `${res.status}`, res.headers, req);
|
|
59564
|
+
if (d2.reason)
|
|
59565
|
+
event.reason = d2.reason;
|
|
59566
|
+
if (d2.confidence !== undefined)
|
|
59567
|
+
event.confidence = d2.confidence;
|
|
59568
|
+
if (meta3.status !== undefined && d2.reason === "http_error")
|
|
59569
|
+
event.status = meta3.status;
|
|
59570
|
+
emit(event);
|
|
59710
59571
|
}
|
|
59572
|
+
return decisions;
|
|
59711
59573
|
}
|
|
59712
|
-
async
|
|
59713
|
-
const controller = new AbortController;
|
|
59714
|
-
const abortFromCaller = () => controller.abort(signal?.reason);
|
|
59715
|
-
if (signal?.aborted)
|
|
59716
|
-
abortFromCaller();
|
|
59717
|
-
signal?.addEventListener("abort", abortFromCaller, { once: true });
|
|
59718
|
-
let timedOut = false;
|
|
59719
|
-
const timer = setTimeout(() => {
|
|
59720
|
-
timedOut = true;
|
|
59721
|
-
controller.abort();
|
|
59722
|
-
}, timeout);
|
|
59574
|
+
async function run(questions, base) {
|
|
59723
59575
|
const started = Date.now();
|
|
59724
|
-
const
|
|
59576
|
+
const sensitive = base.sensitive === true;
|
|
59577
|
+
const all2 = (reason, status2) => finish(questions, Object.fromEntries(Object.entries(questions).map(([n2, q2]) => [n2, fallbackOf(q2, reason)])), { sensitive, started, status: status2 });
|
|
59578
|
+
if (base.sensitive !== false && !allowSensitive)
|
|
59579
|
+
return all2("sensitive_not_allowed");
|
|
59580
|
+
if (!apiKey)
|
|
59581
|
+
return all2("no_key");
|
|
59582
|
+
const min = base.minConfidence ?? floor;
|
|
59583
|
+
const names = Object.keys(questions);
|
|
59584
|
+
if (!isUnit(min) || names.length === 0 || !names.every((n2) => validQuestion(questions[n2]))) {
|
|
59585
|
+
return all2("invalid_request");
|
|
59586
|
+
}
|
|
59587
|
+
if (base.signal?.aborted)
|
|
59588
|
+
return all2("aborted");
|
|
59589
|
+
const controller = new AbortController;
|
|
59590
|
+
const onAbort = () => controller.abort();
|
|
59591
|
+
base.signal?.addEventListener("abort", onAbort, { once: true });
|
|
59592
|
+
let timer;
|
|
59593
|
+
const timeout = new Promise((resolve) => {
|
|
59594
|
+
timer = setTimeout(() => {
|
|
59595
|
+
controller.abort();
|
|
59596
|
+
resolve(new Timeout);
|
|
59597
|
+
}, timeoutMs);
|
|
59598
|
+
});
|
|
59599
|
+
const doFetch = config2.fetch ?? ((u2, i2) => globalThis.fetch(u2, i2));
|
|
59600
|
+
let status;
|
|
59725
59601
|
try {
|
|
59726
|
-
const
|
|
59727
|
-
|
|
59728
|
-
|
|
59729
|
-
|
|
59730
|
-
|
|
59731
|
-
|
|
59602
|
+
const request = (async () => {
|
|
59603
|
+
const res = await doFetch(url3, {
|
|
59604
|
+
method: "POST",
|
|
59605
|
+
headers: {
|
|
59606
|
+
Authorization: `Bearer ${apiKey}`,
|
|
59607
|
+
Accept: "application/json",
|
|
59608
|
+
"Content-Type": "application/json"
|
|
59609
|
+
},
|
|
59610
|
+
body: JSON.stringify({
|
|
59611
|
+
model,
|
|
59612
|
+
state: base.state,
|
|
59613
|
+
questions: Object.fromEntries(names.map((n2) => [n2, toWire(questions[n2])]))
|
|
59614
|
+
}),
|
|
59615
|
+
signal: controller.signal
|
|
59616
|
+
});
|
|
59617
|
+
status = res.status;
|
|
59618
|
+
if (!res.ok)
|
|
59619
|
+
return { ok: false };
|
|
59620
|
+
return { ok: true, body: await res.json() };
|
|
59621
|
+
})();
|
|
59622
|
+
const out = await Promise.race([request, timeout]);
|
|
59623
|
+
if (out instanceof Timeout) {
|
|
59624
|
+
request.catch(() => {});
|
|
59625
|
+
return all2("timeout");
|
|
59626
|
+
}
|
|
59627
|
+
if (!out.ok)
|
|
59628
|
+
return all2("http_error", status);
|
|
59629
|
+
const answers = out.body?.answers;
|
|
59630
|
+
if (!answers || typeof answers !== "object")
|
|
59631
|
+
return all2("invalid_response");
|
|
59632
|
+
return finish(questions, Object.fromEntries(names.map((n2) => [n2, judge(questions[n2], answers[n2], min)])), { sensitive, started });
|
|
59732
59633
|
} catch (err) {
|
|
59733
|
-
if (signal?.aborted)
|
|
59734
|
-
|
|
59735
|
-
|
|
59736
|
-
|
|
59737
|
-
|
|
59738
|
-
this.logger.info(`${tag} timed out after ${elapsed()}`);
|
|
59739
|
-
throw new APITimeoutError(timeout, { cause: err });
|
|
59740
|
-
}
|
|
59741
|
-
this.logger.info(`${tag} connection error after ${elapsed()}`, err);
|
|
59742
|
-
throw new APIConnectionError(err instanceof Error ? `Connection error: ${err.message}` : undefined, { cause: err });
|
|
59634
|
+
if (base.signal?.aborted)
|
|
59635
|
+
return all2("aborted");
|
|
59636
|
+
if (status !== undefined)
|
|
59637
|
+
return all2("invalid_response");
|
|
59638
|
+
return all2(err?.name === "TimeoutError" ? "timeout" : "network_error");
|
|
59743
59639
|
} finally {
|
|
59744
59640
|
clearTimeout(timer);
|
|
59745
|
-
signal?.removeEventListener("abort",
|
|
59641
|
+
base.signal?.removeEventListener("abort", onAbort);
|
|
59746
59642
|
}
|
|
59747
59643
|
}
|
|
59748
|
-
|
|
59749
|
-
|
|
59750
|
-
|
|
59751
|
-
|
|
59752
|
-
|
|
59753
|
-
|
|
59754
|
-
|
|
59755
|
-
} catch (err) {
|
|
59756
|
-
this.logger.info(`${tag} aborted by caller while waiting to retry`);
|
|
59757
|
-
throw new APIUserAbortError(undefined, { cause: err });
|
|
59758
|
-
}
|
|
59644
|
+
function baseOf(args) {
|
|
59645
|
+
return {
|
|
59646
|
+
state: args.state,
|
|
59647
|
+
sensitive: args.sensitive,
|
|
59648
|
+
minConfidence: args.minConfidence,
|
|
59649
|
+
signal: args.signal
|
|
59650
|
+
};
|
|
59759
59651
|
}
|
|
59760
|
-
|
|
59761
|
-
|
|
59762
|
-
|
|
59763
|
-
|
|
59764
|
-
|
|
59765
|
-
|
|
59766
|
-
|
|
59767
|
-
|
|
59768
|
-
|
|
59769
|
-
|
|
59652
|
+
return {
|
|
59653
|
+
enabled: apiKey.length > 0,
|
|
59654
|
+
async choice(args) {
|
|
59655
|
+
const q2 = {
|
|
59656
|
+
type: "choice",
|
|
59657
|
+
question: args.question,
|
|
59658
|
+
options: args.options,
|
|
59659
|
+
fallback: args.fallback
|
|
59660
|
+
};
|
|
59661
|
+
const { result } = await run({ result: q2 }, baseOf(args));
|
|
59662
|
+
return result;
|
|
59663
|
+
},
|
|
59664
|
+
async score(args) {
|
|
59665
|
+
const q2 = {
|
|
59666
|
+
type: "score",
|
|
59667
|
+
question: args.question,
|
|
59668
|
+
levels: args.levels,
|
|
59669
|
+
fallback: args.fallback
|
|
59670
|
+
};
|
|
59671
|
+
const { result } = await run({ result: q2 }, baseOf(args));
|
|
59672
|
+
return result;
|
|
59673
|
+
},
|
|
59674
|
+
async noul(args) {
|
|
59675
|
+
const q2 = { type: "noul", question: args.question, fallback: args.fallback };
|
|
59676
|
+
const { result } = await run({ result: q2 }, baseOf(args));
|
|
59677
|
+
return result;
|
|
59678
|
+
},
|
|
59679
|
+
async batch(args) {
|
|
59680
|
+
return await run(args.questions, baseOf(args));
|
|
59770
59681
|
}
|
|
59771
|
-
|
|
59772
|
-
|
|
59773
|
-
} catch {
|
|
59774
|
-
return text;
|
|
59775
|
-
}
|
|
59776
|
-
};
|
|
59682
|
+
};
|
|
59683
|
+
}
|
|
59777
59684
|
|
|
59778
59685
|
// src/lib/jev-categorize.ts
|
|
59779
59686
|
var JEV_MODEL = "jev-1.13.0";
|
|
@@ -59793,18 +59700,18 @@ function assertJevAllowed(crypto3, env2 = process.env) {
|
|
|
59793
59700
|
throw new Error("JEV_API_KEY is not set — Jev categorization is off.");
|
|
59794
59701
|
return key;
|
|
59795
59702
|
}
|
|
59796
|
-
function
|
|
59797
|
-
|
|
59798
|
-
|
|
59799
|
-
|
|
59800
|
-
|
|
59801
|
-
|
|
59802
|
-
|
|
59803
|
-
|
|
59804
|
-
|
|
59805
|
-
|
|
59806
|
-
|
|
59807
|
-
};
|
|
59703
|
+
function jevAllowSensitive(crypto3, env2) {
|
|
59704
|
+
return crypto3 === undefined || env2.JEV_ALLOW_PRIVATE_MODE === "1";
|
|
59705
|
+
}
|
|
59706
|
+
function makeJev(apiKey, crypto3, env2 = process.env, fetchImpl) {
|
|
59707
|
+
return createJev({
|
|
59708
|
+
apiKey,
|
|
59709
|
+
model: JEV_MODEL,
|
|
59710
|
+
minConfidence: DEFAULT_MIN_CONFIDENCE,
|
|
59711
|
+
timeoutMs: TIMEOUT_MS,
|
|
59712
|
+
allowSensitive: jevAllowSensitive(crypto3, env2),
|
|
59713
|
+
...fetchImpl ? { fetch: fetchImpl } : {}
|
|
59714
|
+
});
|
|
59808
59715
|
}
|
|
59809
59716
|
var UNSURE = "unsure";
|
|
59810
59717
|
function optionsFor(type, index) {
|
|
@@ -59822,26 +59729,33 @@ function groupState(type, sample) {
|
|
|
59822
59729
|
const dir = type === "credit" ? "Money received" : "Money paid out";
|
|
59823
59730
|
return `${dir}. Bank narration: "${sample.slice(0, 80)}"`;
|
|
59824
59731
|
}
|
|
59825
|
-
async function jevCategorize(client, crypto3,
|
|
59732
|
+
async function jevCategorize(client, crypto3, jev, opts = {}) {
|
|
59826
59733
|
assertPrivateModeAllowed(crypto3, opts.env);
|
|
59827
|
-
const min = opts.minConfidence ?? DEFAULT_MIN_CONFIDENCE;
|
|
59828
59734
|
const index = await loadCategoryIndex(client);
|
|
59829
59735
|
const rows = await fetchUncategorized(client, crypto3, opts);
|
|
59830
59736
|
const groups = groupRows(rows);
|
|
59831
|
-
const unparsed = groups.filter((
|
|
59832
|
-
const candidates = groups.filter((
|
|
59737
|
+
const unparsed = groups.filter((g2) => g2.pattern === UNPARSED).length;
|
|
59738
|
+
const candidates = groups.filter((g2) => g2.pattern !== UNPARSED);
|
|
59833
59739
|
let errors3 = 0;
|
|
59834
|
-
const suggestions = await mapLimit(candidates, CONCURRENCY, async (
|
|
59835
|
-
|
|
59836
|
-
|
|
59837
|
-
|
|
59838
|
-
|
|
59839
|
-
|
|
59840
|
-
|
|
59841
|
-
|
|
59842
|
-
|
|
59843
|
-
|
|
59740
|
+
const suggestions = await mapLimit(candidates, CONCURRENCY, async (g2) => {
|
|
59741
|
+
const options = optionsFor(g2.type, index);
|
|
59742
|
+
const sample = g2.rows[0].description;
|
|
59743
|
+
const text = crypto3 ? g2.pattern : sample;
|
|
59744
|
+
const d2 = await jev.choice({
|
|
59745
|
+
question: "Which spending category best fits this bank transaction?",
|
|
59746
|
+
options,
|
|
59747
|
+
fallback: UNSURE,
|
|
59748
|
+
state: groupState(g2.type, text),
|
|
59749
|
+
sensitive: true,
|
|
59750
|
+
minConfidence: opts.minConfidence
|
|
59751
|
+
});
|
|
59752
|
+
if (d2.confident)
|
|
59753
|
+
return { g: g2, choice: d2.value, confidence: d2.confidence, confident: true };
|
|
59754
|
+
if (d2.reason === "low_confidence" && d2.answer !== undefined) {
|
|
59755
|
+
return { g: g2, choice: d2.answer, confidence: d2.confidence ?? 0, confident: false };
|
|
59844
59756
|
}
|
|
59757
|
+
errors3 += 1;
|
|
59758
|
+
return null;
|
|
59845
59759
|
});
|
|
59846
59760
|
const accepted = [];
|
|
59847
59761
|
const review = [];
|
|
@@ -59849,7 +59763,7 @@ async function jevCategorize(client, crypto3, classify, opts = {}) {
|
|
|
59849
59763
|
if (!s2)
|
|
59850
59764
|
continue;
|
|
59851
59765
|
const isTransfer = index.bySlug.get(s2.choice)?.type === "transfer";
|
|
59852
|
-
if (s2.choice !== UNSURE && !isTransfer
|
|
59766
|
+
if (s2.confident && s2.choice !== UNSURE && !isTransfer) {
|
|
59853
59767
|
accepted.push({
|
|
59854
59768
|
pattern: s2.g.pattern,
|
|
59855
59769
|
type: s2.g.type,
|
|
@@ -59898,7 +59812,8 @@ function jevTools(client, crypto3) {
|
|
|
59898
59812
|
}),
|
|
59899
59813
|
handler: async ({ startDate, endDate, minConfidence, dryRun }) => {
|
|
59900
59814
|
const key = assertJevAllowed(crypto3);
|
|
59901
|
-
const
|
|
59815
|
+
const jev = makeJev(key, crypto3);
|
|
59816
|
+
const result = await jevCategorize(client, crypto3, jev, {
|
|
59902
59817
|
startDate,
|
|
59903
59818
|
endDate,
|
|
59904
59819
|
minConfidence,
|
|
@@ -59936,15 +59851,7 @@ function settingsTools(client) {
|
|
|
59936
59851
|
describe: { name: "", ownerLabels: "Owner slug → display name" },
|
|
59937
59852
|
variants: { profile: profile.update, household: household.update }
|
|
59938
59853
|
});
|
|
59939
|
-
return [
|
|
59940
|
-
get.tool,
|
|
59941
|
-
update.tool,
|
|
59942
|
-
get.alias("get_profile", "profile"),
|
|
59943
|
-
get.alias("get_household", "household"),
|
|
59944
|
-
get.alias("get_encryption_status", "encryption"),
|
|
59945
|
-
update.alias("update_profile", "profile"),
|
|
59946
|
-
update.alias("update_household", "household")
|
|
59947
|
-
];
|
|
59854
|
+
return [get.tool, update.tool];
|
|
59948
59855
|
}
|
|
59949
59856
|
|
|
59950
59857
|
// src/tools/statement-import.ts
|
|
@@ -60230,15 +60137,7 @@ function statementImportTools(client, crypto3) {
|
|
|
60230
60137
|
})
|
|
60231
60138
|
}
|
|
60232
60139
|
});
|
|
60233
|
-
return [
|
|
60234
|
-
preview.tool,
|
|
60235
|
-
commit.tool,
|
|
60236
|
-
aliasTool("import_statement_pdf", pdfInput.extend({ confirm: exports_external.boolean().optional() }), "preview_import / import_statement", 'from: "pdf" (confirm:true → import_statement, else preview_import)', ({ confirm, ...args }) => confirm ? [commit.tool, { ...args, from: "pdf" }] : [preview.tool, { ...args, from: "pdf" }]),
|
|
60237
|
-
preview.alias("import_csv_parse", "csv"),
|
|
60238
|
-
commit.alias("import_csv_confirm", "csv"),
|
|
60239
|
-
commit.alias("import_statement_batch", "rows"),
|
|
60240
|
-
commit.alias("import_statement_file", "json_file")
|
|
60241
|
-
];
|
|
60140
|
+
return [preview.tool, commit.tool];
|
|
60242
60141
|
}
|
|
60243
60142
|
|
|
60244
60143
|
// src/registrations.ts
|
|
@@ -60263,10 +60162,127 @@ var REGISTRATIONS = [
|
|
|
60263
60162
|
{ title: "Settings", tools: settingsTools },
|
|
60264
60163
|
{ title: "Encryption & privacy", tools: authCryptoTools }
|
|
60265
60164
|
];
|
|
60165
|
+
|
|
60166
|
+
// src/resources.ts
|
|
60167
|
+
var MERCHANT_CAP = 500;
|
|
60168
|
+
var JSON_MIME = "application/json";
|
|
60169
|
+
var RESOURCES = [
|
|
60170
|
+
{
|
|
60171
|
+
name: "categories",
|
|
60172
|
+
uri: "paisa://categories",
|
|
60173
|
+
title: "Categories",
|
|
60174
|
+
description: "Category tree (id, slug, name, type, children). Same as list_entities categories.",
|
|
60175
|
+
tool: "list_entities",
|
|
60176
|
+
args: { entity: "categories" }
|
|
60177
|
+
},
|
|
60178
|
+
{
|
|
60179
|
+
name: "bank_accounts",
|
|
60180
|
+
uri: "paisa://bank-accounts",
|
|
60181
|
+
title: "Bank accounts",
|
|
60182
|
+
description: "Bank accounts and cards (id, name, bank, type, last4, owner).",
|
|
60183
|
+
tool: "list_entities",
|
|
60184
|
+
args: { entity: "bank_accounts" }
|
|
60185
|
+
},
|
|
60186
|
+
{
|
|
60187
|
+
name: "persons",
|
|
60188
|
+
uri: "paisa://persons",
|
|
60189
|
+
title: "Persons",
|
|
60190
|
+
description: "People you send/receive money from (decrypted in private mode).",
|
|
60191
|
+
tool: "list_entities",
|
|
60192
|
+
args: { entity: "persons" }
|
|
60193
|
+
},
|
|
60194
|
+
{
|
|
60195
|
+
name: "merchants",
|
|
60196
|
+
uri: "paisa://merchants",
|
|
60197
|
+
title: "Merchants",
|
|
60198
|
+
description: `Merchants with their category (first ${MERCHANT_CAP}; look others up with list_entities entity='merchants' rawId).`,
|
|
60199
|
+
tool: "list_entities",
|
|
60200
|
+
args: { entity: "merchants" },
|
|
60201
|
+
shape: capMerchants
|
|
60202
|
+
},
|
|
60203
|
+
{
|
|
60204
|
+
name: "household",
|
|
60205
|
+
uri: "paisa://household",
|
|
60206
|
+
title: "Household",
|
|
60207
|
+
description: "Household name, currency, locale, owner labels and members.",
|
|
60208
|
+
tool: "get_settings",
|
|
60209
|
+
args: { section: "household" }
|
|
60210
|
+
},
|
|
60211
|
+
{
|
|
60212
|
+
name: "profile",
|
|
60213
|
+
uri: "paisa://profile",
|
|
60214
|
+
title: "Profile",
|
|
60215
|
+
description: "Your name, email and owner slug.",
|
|
60216
|
+
tool: "get_settings",
|
|
60217
|
+
args: { section: "profile" }
|
|
60218
|
+
}
|
|
60219
|
+
];
|
|
60220
|
+
var TRANSACTION_TEMPLATE = "paisa://transactions/{id}";
|
|
60221
|
+
function capMerchants(value) {
|
|
60222
|
+
const v2 = value;
|
|
60223
|
+
if (!v2 || !Array.isArray(v2.data) || v2.data.length <= MERCHANT_CAP)
|
|
60224
|
+
return value;
|
|
60225
|
+
return {
|
|
60226
|
+
...v2,
|
|
60227
|
+
data: v2.data.slice(0, MERCHANT_CAP),
|
|
60228
|
+
truncated: {
|
|
60229
|
+
returned: MERCHANT_CAP,
|
|
60230
|
+
total: v2.data.length,
|
|
60231
|
+
hint: "Use list_entities entity='merchants' rawId=<UPI ID or name> to find one."
|
|
60232
|
+
}
|
|
60233
|
+
};
|
|
60234
|
+
}
|
|
60235
|
+
function scrubCiphertext(value) {
|
|
60236
|
+
const scrub = (row) => {
|
|
60237
|
+
if (row && typeof row === "object" && typeof row.cipher === "string") {
|
|
60238
|
+
Object.assign(row, { cipher: null, undecrypted: true });
|
|
60239
|
+
}
|
|
60240
|
+
};
|
|
60241
|
+
const data = value?.data;
|
|
60242
|
+
if (Array.isArray(data))
|
|
60243
|
+
data.forEach(scrub);
|
|
60244
|
+
else
|
|
60245
|
+
scrub(data);
|
|
60246
|
+
return value;
|
|
60247
|
+
}
|
|
60248
|
+
async function runTool(tool, args, mcp) {
|
|
60249
|
+
const input2 = tool.input ? await tool.input.parseAsync(args) : args;
|
|
60250
|
+
const ctx = { tool: tool.name, signal: mcp.mcpReq.signal, mcp };
|
|
60251
|
+
return tool.handler(input2, ctx);
|
|
60252
|
+
}
|
|
60253
|
+
function jsonContents(uri, value) {
|
|
60254
|
+
return { contents: [{ uri: uri.href, mimeType: JSON_MIME, text: JSON.stringify(value ?? null) }] };
|
|
60255
|
+
}
|
|
60256
|
+
function registerResources(server, tools) {
|
|
60257
|
+
const byName = new Map(tools.map((t2) => [t2.name, t2]));
|
|
60258
|
+
const need = (name) => {
|
|
60259
|
+
const t2 = byName.get(name);
|
|
60260
|
+
if (!t2)
|
|
60261
|
+
throw new Error(`MCP resources: tool ${name} is not registered`);
|
|
60262
|
+
return t2;
|
|
60263
|
+
};
|
|
60264
|
+
for (const r2 of RESOURCES) {
|
|
60265
|
+
const tool = need(r2.tool);
|
|
60266
|
+
server.registerResource(r2.name, r2.uri, { title: r2.title, description: r2.description, mimeType: JSON_MIME }, async (uri, mcp) => {
|
|
60267
|
+
const value = scrubCiphertext(await runTool(tool, r2.args, mcp));
|
|
60268
|
+
return jsonContents(uri, r2.shape ? r2.shape(value) : value);
|
|
60269
|
+
});
|
|
60270
|
+
}
|
|
60271
|
+
const getTransaction = need("get_transaction");
|
|
60272
|
+
server.registerResource("transaction", new ResourceTemplate(TRANSACTION_TEMPLATE, { list: undefined }), {
|
|
60273
|
+
title: "Transaction",
|
|
60274
|
+
description: "One transaction by UUID (same as get_transaction; decrypted in private mode).",
|
|
60275
|
+
mimeType: JSON_MIME
|
|
60276
|
+
}, async (uri, variables, mcp) => {
|
|
60277
|
+
const id = Array.isArray(variables.id) ? variables.id[0] : variables.id;
|
|
60278
|
+
const value = scrubCiphertext(await runTool(getTransaction, { id }, mcp));
|
|
60279
|
+
return jsonContents(uri, value);
|
|
60280
|
+
});
|
|
60281
|
+
}
|
|
60266
60282
|
// package.json
|
|
60267
60283
|
var package_default = {
|
|
60268
60284
|
name: "paisa-mcp",
|
|
60269
|
-
version: "0.
|
|
60285
|
+
version: "0.3.0",
|
|
60270
60286
|
repository: {
|
|
60271
60287
|
type: "git",
|
|
60272
60288
|
url: "git+https://github.com/nimit9/paisa.git",
|
|
@@ -60293,13 +60309,14 @@ var package_default = {
|
|
|
60293
60309
|
devDependencies: {
|
|
60294
60310
|
"@modelcontextprotocol/client": "^2.1.0",
|
|
60295
60311
|
"@modelcontextprotocol/server": "^2.1.0",
|
|
60312
|
+
"@nimit9/signet-ai": "^0.1.11",
|
|
60313
|
+
"@nimit9/signet-lib": "^0.1.14",
|
|
60296
60314
|
"@nimit9/signet-server": "^0.2.2",
|
|
60297
60315
|
"@paisa/parsers": "workspace:*",
|
|
60298
60316
|
"@paisa/reconciliation": "workspace:*",
|
|
60299
60317
|
"@paisa/types": "workspace:*",
|
|
60300
60318
|
"@scure/bip39": "^2.2.0",
|
|
60301
60319
|
"@types/bun": "^1.3.14",
|
|
60302
|
-
"@typesafe-ai/sdk": "^0.6.0",
|
|
60303
60320
|
axios: "^1.16.1",
|
|
60304
60321
|
"hash-wasm": "^4.12.0",
|
|
60305
60322
|
typescript: "^7.0.2",
|
|
@@ -60311,7 +60328,7 @@ var package_default = {
|
|
|
60311
60328
|
};
|
|
60312
60329
|
|
|
60313
60330
|
// src/version.ts
|
|
60314
|
-
var
|
|
60331
|
+
var VERSION2 = package_default.version;
|
|
60315
60332
|
|
|
60316
60333
|
// src/server.ts
|
|
60317
60334
|
function paisaErrorResult(err) {
|
|
@@ -60323,29 +60340,18 @@ function paisaErrorResult(err) {
|
|
|
60323
60340
|
function buildServer(client, crypto3) {
|
|
60324
60341
|
const tools = applyAnnotations(REGISTRATIONS.flatMap((group) => group.tools(client, crypto3)));
|
|
60325
60342
|
for (const t2 of tools)
|
|
60326
|
-
if (t2.input
|
|
60343
|
+
if (t2.input)
|
|
60327
60344
|
withCompactJsonSchema(t2.input);
|
|
60328
60345
|
const server = createMcpServer({
|
|
60329
60346
|
name: "paisa",
|
|
60330
|
-
version:
|
|
60347
|
+
version: VERSION2,
|
|
60331
60348
|
tools,
|
|
60332
60349
|
errorResult: paisaErrorResult
|
|
60333
60350
|
});
|
|
60334
|
-
|
|
60351
|
+
registerResources(server, tools);
|
|
60352
|
+
registerPrompts(server);
|
|
60335
60353
|
return server;
|
|
60336
60354
|
}
|
|
60337
|
-
function hideFromToolsList(server, hidden) {
|
|
60338
|
-
if (hidden.size === 0)
|
|
60339
|
-
return;
|
|
60340
|
-
const inner = server.server;
|
|
60341
|
-
const list = inner._getRequestHandler("tools/list");
|
|
60342
|
-
if (!list)
|
|
60343
|
-
throw new Error("MCP SDK: no tools/list handler to wrap (aliases would be listed)");
|
|
60344
|
-
server.server.setRequestHandler("tools/list", async (request, ctx) => {
|
|
60345
|
-
const result = await list(request, ctx);
|
|
60346
|
-
return { ...result, tools: result.tools.filter((t2) => !hidden.has(t2.name)) };
|
|
60347
|
-
});
|
|
60348
|
-
}
|
|
60349
60355
|
|
|
60350
60356
|
// src/index.ts
|
|
60351
60357
|
async function main() {
|
|
@@ -60368,5 +60374,5 @@ main().catch((err) => {
|
|
|
60368
60374
|
process.exit(1);
|
|
60369
60375
|
});
|
|
60370
60376
|
|
|
60371
|
-
//# debugId=
|
|
60377
|
+
//# debugId=C64445D8B55EDCFA64756E2164756E21
|
|
60372
60378
|
//# sourceMappingURL=index.js.map
|