copilot-money-mcp 1.2.3 → 1.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/cli.js +254 -233
- package/dist/decode-worker.js +15145 -0
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -4,25 +4,43 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
function __accessProp(key) {
|
|
8
|
+
return this[key];
|
|
9
|
+
}
|
|
10
|
+
var __toESMCache_node;
|
|
11
|
+
var __toESMCache_esm;
|
|
7
12
|
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
+
var canCache = mod != null && typeof mod === "object";
|
|
14
|
+
if (canCache) {
|
|
15
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
+
var cached = cache.get(mod);
|
|
17
|
+
if (cached)
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
8
20
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
21
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
22
|
for (let key of __getOwnPropNames(mod))
|
|
11
23
|
if (!__hasOwnProp.call(to, key))
|
|
12
24
|
__defProp(to, key, {
|
|
13
|
-
get: (
|
|
25
|
+
get: __accessProp.bind(mod, key),
|
|
14
26
|
enumerable: true
|
|
15
27
|
});
|
|
28
|
+
if (canCache)
|
|
29
|
+
cache.set(mod, to);
|
|
16
30
|
return to;
|
|
17
31
|
};
|
|
18
32
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
33
|
+
var __returnValue = (v) => v;
|
|
34
|
+
function __exportSetter(name, newValue) {
|
|
35
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
36
|
+
}
|
|
19
37
|
var __export = (target, all) => {
|
|
20
38
|
for (var name in all)
|
|
21
39
|
__defProp(target, name, {
|
|
22
40
|
get: all[name],
|
|
23
41
|
enumerable: true,
|
|
24
42
|
configurable: true,
|
|
25
|
-
set: (
|
|
43
|
+
set: __exportSetter.bind(all, name)
|
|
26
44
|
});
|
|
27
45
|
};
|
|
28
46
|
|
|
@@ -27286,6 +27304,62 @@ class ExperimentalServerTasks {
|
|
|
27286
27304
|
requestStream(request, resultSchema, options) {
|
|
27287
27305
|
return this._server.requestStream(request, resultSchema, options);
|
|
27288
27306
|
}
|
|
27307
|
+
createMessageStream(params, options) {
|
|
27308
|
+
const clientCapabilities = this._server.getClientCapabilities();
|
|
27309
|
+
if ((params.tools || params.toolChoice) && !clientCapabilities?.sampling?.tools) {
|
|
27310
|
+
throw new Error("Client does not support sampling tools capability.");
|
|
27311
|
+
}
|
|
27312
|
+
if (params.messages.length > 0) {
|
|
27313
|
+
const lastMessage = params.messages[params.messages.length - 1];
|
|
27314
|
+
const lastContent = Array.isArray(lastMessage.content) ? lastMessage.content : [lastMessage.content];
|
|
27315
|
+
const hasToolResults = lastContent.some((c) => c.type === "tool_result");
|
|
27316
|
+
const previousMessage = params.messages.length > 1 ? params.messages[params.messages.length - 2] : undefined;
|
|
27317
|
+
const previousContent = previousMessage ? Array.isArray(previousMessage.content) ? previousMessage.content : [previousMessage.content] : [];
|
|
27318
|
+
const hasPreviousToolUse = previousContent.some((c) => c.type === "tool_use");
|
|
27319
|
+
if (hasToolResults) {
|
|
27320
|
+
if (lastContent.some((c) => c.type !== "tool_result")) {
|
|
27321
|
+
throw new Error("The last message must contain only tool_result content if any is present");
|
|
27322
|
+
}
|
|
27323
|
+
if (!hasPreviousToolUse) {
|
|
27324
|
+
throw new Error("tool_result blocks are not matching any tool_use from the previous message");
|
|
27325
|
+
}
|
|
27326
|
+
}
|
|
27327
|
+
if (hasPreviousToolUse) {
|
|
27328
|
+
const toolUseIds = new Set(previousContent.filter((c) => c.type === "tool_use").map((c) => c.id));
|
|
27329
|
+
const toolResultIds = new Set(lastContent.filter((c) => c.type === "tool_result").map((c) => c.toolUseId));
|
|
27330
|
+
if (toolUseIds.size !== toolResultIds.size || ![...toolUseIds].every((id) => toolResultIds.has(id))) {
|
|
27331
|
+
throw new Error("ids of tool_result blocks and tool_use blocks from previous message do not match");
|
|
27332
|
+
}
|
|
27333
|
+
}
|
|
27334
|
+
}
|
|
27335
|
+
return this.requestStream({
|
|
27336
|
+
method: "sampling/createMessage",
|
|
27337
|
+
params
|
|
27338
|
+
}, CreateMessageResultSchema, options);
|
|
27339
|
+
}
|
|
27340
|
+
elicitInputStream(params, options) {
|
|
27341
|
+
const clientCapabilities = this._server.getClientCapabilities();
|
|
27342
|
+
const mode = params.mode ?? "form";
|
|
27343
|
+
switch (mode) {
|
|
27344
|
+
case "url": {
|
|
27345
|
+
if (!clientCapabilities?.elicitation?.url) {
|
|
27346
|
+
throw new Error("Client does not support url elicitation.");
|
|
27347
|
+
}
|
|
27348
|
+
break;
|
|
27349
|
+
}
|
|
27350
|
+
case "form": {
|
|
27351
|
+
if (!clientCapabilities?.elicitation?.form) {
|
|
27352
|
+
throw new Error("Client does not support form elicitation.");
|
|
27353
|
+
}
|
|
27354
|
+
break;
|
|
27355
|
+
}
|
|
27356
|
+
}
|
|
27357
|
+
const normalizedParams = mode === "form" && params.mode === undefined ? { ...params, mode: "form" } : params;
|
|
27358
|
+
return this.requestStream({
|
|
27359
|
+
method: "elicitation/create",
|
|
27360
|
+
params: normalizedParams
|
|
27361
|
+
}, ElicitResultSchema, options);
|
|
27362
|
+
}
|
|
27289
27363
|
async getTask(taskId, options) {
|
|
27290
27364
|
return this._server.getTask({ taskId }, options);
|
|
27291
27365
|
}
|
|
@@ -27764,6 +27838,11 @@ import { existsSync, readdirSync } from "fs";
|
|
|
27764
27838
|
import { homedir } from "os";
|
|
27765
27839
|
import { join } from "path";
|
|
27766
27840
|
|
|
27841
|
+
// src/core/decoder.ts
|
|
27842
|
+
import { Worker } from "node:worker_threads";
|
|
27843
|
+
import { fileURLToPath } from "node:url";
|
|
27844
|
+
import path2 from "node:path";
|
|
27845
|
+
|
|
27767
27846
|
// src/core/leveldb-reader.ts
|
|
27768
27847
|
import { ClassicLevel } from "classic-level";
|
|
27769
27848
|
import fs from "node:fs";
|
|
@@ -28282,7 +28361,6 @@ async function* iterateDocuments(dbPath, options = {}) {
|
|
|
28282
28361
|
key: keyStr,
|
|
28283
28362
|
collection: parsed.collection,
|
|
28284
28363
|
documentId: parsed.documentId,
|
|
28285
|
-
rawValue: value,
|
|
28286
28364
|
fields
|
|
28287
28365
|
};
|
|
28288
28366
|
count++;
|
|
@@ -28506,6 +28584,14 @@ var ItemSchema = exports_external.object({
|
|
|
28506
28584
|
last_successful_update: exports_external.string().optional(),
|
|
28507
28585
|
last_failed_update: exports_external.string().optional(),
|
|
28508
28586
|
consent_expiration_time: exports_external.string().optional(),
|
|
28587
|
+
status_transactions_last_successful_update: exports_external.string().optional(),
|
|
28588
|
+
status_transactions_last_failed_update: exports_external.string().optional(),
|
|
28589
|
+
status_investments_last_successful_update: exports_external.string().optional(),
|
|
28590
|
+
status_investments_last_failed_update: exports_external.string().optional(),
|
|
28591
|
+
latest_fetch: exports_external.string().optional(),
|
|
28592
|
+
latest_investments_fetch: exports_external.string().optional(),
|
|
28593
|
+
login_required: exports_external.boolean().optional(),
|
|
28594
|
+
disconnected: exports_external.boolean().optional(),
|
|
28509
28595
|
error_code: exports_external.string().optional(),
|
|
28510
28596
|
error_message: exports_external.string().optional(),
|
|
28511
28597
|
error_type: exports_external.string().optional(),
|
|
@@ -28518,6 +28604,36 @@ var ItemSchema = exports_external.object({
|
|
|
28518
28604
|
updated_at: exports_external.string().optional(),
|
|
28519
28605
|
webhook: exports_external.string().optional()
|
|
28520
28606
|
}).passthrough();
|
|
28607
|
+
function getItemDisplayName(item) {
|
|
28608
|
+
return item.institution_name ?? item.institution_id ?? item.item_id;
|
|
28609
|
+
}
|
|
28610
|
+
function isItemHealthy(item) {
|
|
28611
|
+
if (item.connection_status) {
|
|
28612
|
+
if (item.connection_status !== "active") {
|
|
28613
|
+
return false;
|
|
28614
|
+
}
|
|
28615
|
+
}
|
|
28616
|
+
if (item.needs_update === true) {
|
|
28617
|
+
return false;
|
|
28618
|
+
}
|
|
28619
|
+
if (item.error_code && item.error_code !== "ITEM_NO_ERROR") {
|
|
28620
|
+
return false;
|
|
28621
|
+
}
|
|
28622
|
+
return true;
|
|
28623
|
+
}
|
|
28624
|
+
function itemNeedsAttention(item) {
|
|
28625
|
+
if (item.needs_update === true) {
|
|
28626
|
+
return true;
|
|
28627
|
+
}
|
|
28628
|
+
if (item.connection_status === "error" || item.connection_status === "disconnected") {
|
|
28629
|
+
return true;
|
|
28630
|
+
}
|
|
28631
|
+
const loginRequiredCodes = ["ITEM_LOGIN_REQUIRED", "INVALID_CREDENTIALS", "INVALID_MFA"];
|
|
28632
|
+
if (item.error_code && loginRequiredCodes.includes(item.error_code)) {
|
|
28633
|
+
return true;
|
|
28634
|
+
}
|
|
28635
|
+
return false;
|
|
28636
|
+
}
|
|
28521
28637
|
|
|
28522
28638
|
// src/models/category.ts
|
|
28523
28639
|
var CategorySchema = exports_external.object({
|
|
@@ -28634,26 +28750,38 @@ function calculateNextDate(lastDate, frequency) {
|
|
|
28634
28750
|
}
|
|
28635
28751
|
return date6.toISOString().split("T")[0];
|
|
28636
28752
|
}
|
|
28637
|
-
|
|
28638
|
-
const transactions = [];
|
|
28639
|
-
for await (const doc2 of iterateDocuments(dbPath, { collection: "transactions" })) {
|
|
28640
|
-
const txn = processTransaction(doc2.fields, doc2.documentId);
|
|
28641
|
-
if (txn)
|
|
28642
|
-
transactions.push(txn);
|
|
28643
|
-
}
|
|
28753
|
+
function deduplicateTransactions(transactions) {
|
|
28644
28754
|
const seen = new Set;
|
|
28645
28755
|
const unique = [];
|
|
28646
28756
|
for (const txn of transactions) {
|
|
28647
|
-
|
|
28648
|
-
|
|
28649
|
-
if (!seen.has(key)) {
|
|
28650
|
-
seen.add(key);
|
|
28757
|
+
if (!seen.has(txn.transaction_id)) {
|
|
28758
|
+
seen.add(txn.transaction_id);
|
|
28651
28759
|
unique.push(txn);
|
|
28652
28760
|
}
|
|
28653
28761
|
}
|
|
28654
|
-
unique.sort((a, b) => a.date > b.date ? -1 : a.date < b.date ? 1 : 0);
|
|
28655
28762
|
return unique;
|
|
28656
28763
|
}
|
|
28764
|
+
function reconcilePendingTransactions(transactions) {
|
|
28765
|
+
const supersededPendingIds = new Set;
|
|
28766
|
+
for (const txn of transactions) {
|
|
28767
|
+
if (!txn.pending && txn.pending_transaction_id) {
|
|
28768
|
+
supersededPendingIds.add(txn.pending_transaction_id);
|
|
28769
|
+
}
|
|
28770
|
+
}
|
|
28771
|
+
return transactions.filter((txn) => !(txn.pending && supersededPendingIds.has(txn.transaction_id)));
|
|
28772
|
+
}
|
|
28773
|
+
async function decodeTransactions(dbPath) {
|
|
28774
|
+
const transactions = [];
|
|
28775
|
+
for await (const doc2 of iterateDocuments(dbPath, { collection: "transactions" })) {
|
|
28776
|
+
const txn = processTransaction(doc2.fields, doc2.documentId);
|
|
28777
|
+
if (txn)
|
|
28778
|
+
transactions.push(txn);
|
|
28779
|
+
}
|
|
28780
|
+
const deduped = deduplicateTransactions(transactions);
|
|
28781
|
+
const reconciled = reconcilePendingTransactions(deduped);
|
|
28782
|
+
reconciled.sort((a, b) => a.date > b.date ? -1 : a.date < b.date ? 1 : 0);
|
|
28783
|
+
return reconciled;
|
|
28784
|
+
}
|
|
28657
28785
|
async function decodeAccounts(dbPath) {
|
|
28658
28786
|
const accounts = [];
|
|
28659
28787
|
for await (const doc2 of iterateDocuments(dbPath, { collection: "accounts" })) {
|
|
@@ -29326,7 +29454,13 @@ function processItem(fields, docId) {
|
|
|
29326
29454
|
"error_type",
|
|
29327
29455
|
"created_at",
|
|
29328
29456
|
"updated_at",
|
|
29329
|
-
"webhook"
|
|
29457
|
+
"webhook",
|
|
29458
|
+
"status_transactions_last_successful_update",
|
|
29459
|
+
"status_transactions_last_failed_update",
|
|
29460
|
+
"status_investments_last_successful_update",
|
|
29461
|
+
"status_investments_last_failed_update",
|
|
29462
|
+
"latest_fetch",
|
|
29463
|
+
"latest_investments_fetch"
|
|
29330
29464
|
];
|
|
29331
29465
|
for (const field of stringFields) {
|
|
29332
29466
|
const value = getString(fields, field);
|
|
@@ -29336,6 +29470,12 @@ function processItem(fields, docId) {
|
|
|
29336
29470
|
const needsUpdateValue = getBoolean(fields, "needs_update");
|
|
29337
29471
|
if (needsUpdateValue !== undefined)
|
|
29338
29472
|
itemData.needs_update = needsUpdateValue;
|
|
29473
|
+
const loginRequiredValue = getBoolean(fields, "login_required");
|
|
29474
|
+
if (loginRequiredValue !== undefined)
|
|
29475
|
+
itemData.login_required = loginRequiredValue;
|
|
29476
|
+
const disconnectedValue = getBoolean(fields, "disconnected");
|
|
29477
|
+
if (disconnectedValue !== undefined)
|
|
29478
|
+
itemData.disconnected = disconnectedValue;
|
|
29339
29479
|
const validated = ItemSchema.safeParse(itemData);
|
|
29340
29480
|
return validated.success ? validated.data : null;
|
|
29341
29481
|
}
|
|
@@ -29387,219 +29527,42 @@ function processUserAccount(fields, docId, collection) {
|
|
|
29387
29527
|
userAccountData.order = order;
|
|
29388
29528
|
return userAccountData;
|
|
29389
29529
|
}
|
|
29390
|
-
function
|
|
29391
|
-
return
|
|
29392
|
-
|
|
29393
|
-
|
|
29394
|
-
|
|
29395
|
-
|
|
29396
|
-
|
|
29397
|
-
|
|
29398
|
-
|
|
29399
|
-
|
|
29400
|
-
|
|
29401
|
-
|
|
29402
|
-
|
|
29403
|
-
|
|
29404
|
-
|
|
29405
|
-
|
|
29406
|
-
const
|
|
29407
|
-
|
|
29408
|
-
|
|
29409
|
-
|
|
29410
|
-
|
|
29411
|
-
}
|
|
29412
|
-
|
|
29413
|
-
if (
|
|
29414
|
-
|
|
29415
|
-
|
|
29416
|
-
|
|
29417
|
-
|
|
29418
|
-
|
|
29419
|
-
|
|
29420
|
-
|
|
29421
|
-
|
|
29422
|
-
|
|
29423
|
-
|
|
29424
|
-
|
|
29425
|
-
if (budget)
|
|
29426
|
-
rawBudgets.push(budget);
|
|
29427
|
-
} else if (collectionMatches(collection, "financial_goals")) {
|
|
29428
|
-
const goal = processGoal(fields, documentId);
|
|
29429
|
-
if (goal)
|
|
29430
|
-
rawGoals.push(goal);
|
|
29431
|
-
} else if (collection.endsWith("/financial_goal_history")) {
|
|
29432
|
-
const history = processGoalHistory(fields, documentId, collection);
|
|
29433
|
-
if (history)
|
|
29434
|
-
rawGoalHistory.push(history);
|
|
29435
|
-
} else if (collectionMatches(collection, "investment_prices") || collection.includes("investment_prices/")) {
|
|
29436
|
-
const price = processInvestmentPrice(fields, documentId, key);
|
|
29437
|
-
if (price)
|
|
29438
|
-
rawInvestmentPrices.push(price);
|
|
29439
|
-
} else if (collectionMatches(collection, "investment_splits")) {
|
|
29440
|
-
const split = processInvestmentSplit(fields, documentId);
|
|
29441
|
-
if (split)
|
|
29442
|
-
rawInvestmentSplits.push(split);
|
|
29443
|
-
} else if (collectionMatches(collection, "items")) {
|
|
29444
|
-
const item = processItem(fields, documentId);
|
|
29445
|
-
if (item)
|
|
29446
|
-
rawItems.push(item);
|
|
29447
|
-
} else if (collectionMatches(collection, "categories")) {
|
|
29448
|
-
const category = processCategory(fields, documentId);
|
|
29449
|
-
if (category)
|
|
29450
|
-
rawCategories.push(category);
|
|
29451
|
-
}
|
|
29452
|
-
}
|
|
29453
|
-
const txnSeen = new Set;
|
|
29454
|
-
const transactions = [];
|
|
29455
|
-
for (const txn of rawTransactions) {
|
|
29456
|
-
const displayName = getTransactionDisplayName(txn);
|
|
29457
|
-
const key = `${displayName}|${txn.amount}|${txn.date}`;
|
|
29458
|
-
if (!txnSeen.has(key)) {
|
|
29459
|
-
txnSeen.add(key);
|
|
29460
|
-
transactions.push(txn);
|
|
29461
|
-
}
|
|
29462
|
-
}
|
|
29463
|
-
transactions.sort((a, b) => a.date > b.date ? -1 : a.date < b.date ? 1 : 0);
|
|
29464
|
-
const accSeen = new Set;
|
|
29465
|
-
const accounts = [];
|
|
29466
|
-
for (const acc of rawAccounts) {
|
|
29467
|
-
const displayName = getAccountDisplayName(acc);
|
|
29468
|
-
const key = `${displayName}|${acc.mask ?? ""}`;
|
|
29469
|
-
if (!accSeen.has(key)) {
|
|
29470
|
-
accSeen.add(key);
|
|
29471
|
-
accounts.push(acc);
|
|
29472
|
-
}
|
|
29473
|
-
}
|
|
29474
|
-
const recSeen = new Set;
|
|
29475
|
-
const recurring = [];
|
|
29476
|
-
for (const rec of rawRecurring) {
|
|
29477
|
-
if (!recSeen.has(rec.recurring_id)) {
|
|
29478
|
-
recSeen.add(rec.recurring_id);
|
|
29479
|
-
recurring.push(rec);
|
|
29480
|
-
}
|
|
29481
|
-
}
|
|
29482
|
-
const budgetSeen = new Set;
|
|
29483
|
-
const budgets = [];
|
|
29484
|
-
for (const budget of rawBudgets) {
|
|
29485
|
-
if (!budgetSeen.has(budget.budget_id)) {
|
|
29486
|
-
budgetSeen.add(budget.budget_id);
|
|
29487
|
-
budgets.push(budget);
|
|
29488
|
-
}
|
|
29489
|
-
}
|
|
29490
|
-
const goalSeen = new Set;
|
|
29491
|
-
const goals = [];
|
|
29492
|
-
for (const goal of rawGoals) {
|
|
29493
|
-
if (!goalSeen.has(goal.goal_id)) {
|
|
29494
|
-
goalSeen.add(goal.goal_id);
|
|
29495
|
-
goals.push(goal);
|
|
29496
|
-
}
|
|
29497
|
-
}
|
|
29498
|
-
const histSeen = new Set;
|
|
29499
|
-
const goalHistory = [];
|
|
29500
|
-
for (const history of rawGoalHistory) {
|
|
29501
|
-
const key = `${history.goal_id}:${history.month}`;
|
|
29502
|
-
if (!histSeen.has(key)) {
|
|
29503
|
-
histSeen.add(key);
|
|
29504
|
-
goalHistory.push(history);
|
|
29505
|
-
}
|
|
29506
|
-
}
|
|
29507
|
-
goalHistory.sort((a, b) => {
|
|
29508
|
-
if (a.goal_id !== b.goal_id) {
|
|
29509
|
-
return a.goal_id.localeCompare(b.goal_id);
|
|
29510
|
-
}
|
|
29511
|
-
return b.month.localeCompare(a.month);
|
|
29512
|
-
});
|
|
29513
|
-
const priceSeen = new Set;
|
|
29514
|
-
const investmentPrices = [];
|
|
29515
|
-
for (const price of rawInvestmentPrices) {
|
|
29516
|
-
const key = `${price.investment_id}-${price.date || price.month || "unknown"}`;
|
|
29517
|
-
if (!priceSeen.has(key)) {
|
|
29518
|
-
priceSeen.add(key);
|
|
29519
|
-
investmentPrices.push(price);
|
|
29520
|
-
}
|
|
29521
|
-
}
|
|
29522
|
-
investmentPrices.sort((a, b) => {
|
|
29523
|
-
if (a.investment_id !== b.investment_id) {
|
|
29524
|
-
return a.investment_id.localeCompare(b.investment_id);
|
|
29525
|
-
}
|
|
29526
|
-
const dateA = a.date || a.month || "";
|
|
29527
|
-
const dateB = b.date || b.month || "";
|
|
29528
|
-
return dateB.localeCompare(dateA);
|
|
29529
|
-
});
|
|
29530
|
-
const splitSeen = new Set;
|
|
29531
|
-
const investmentSplits = [];
|
|
29532
|
-
for (const split of rawInvestmentSplits) {
|
|
29533
|
-
if (!splitSeen.has(split.split_id)) {
|
|
29534
|
-
splitSeen.add(split.split_id);
|
|
29535
|
-
investmentSplits.push(split);
|
|
29536
|
-
}
|
|
29537
|
-
}
|
|
29538
|
-
investmentSplits.sort((a, b) => {
|
|
29539
|
-
const tickerA = a.ticker_symbol || "";
|
|
29540
|
-
const tickerB = b.ticker_symbol || "";
|
|
29541
|
-
if (tickerA !== tickerB) {
|
|
29542
|
-
return tickerA.localeCompare(tickerB);
|
|
29543
|
-
}
|
|
29544
|
-
const dateA = a.split_date || "";
|
|
29545
|
-
const dateB = b.split_date || "";
|
|
29546
|
-
return dateB.localeCompare(dateA);
|
|
29547
|
-
});
|
|
29548
|
-
const itemSeen = new Set;
|
|
29549
|
-
const items = [];
|
|
29550
|
-
for (const item of rawItems) {
|
|
29551
|
-
if (!itemSeen.has(item.item_id)) {
|
|
29552
|
-
itemSeen.add(item.item_id);
|
|
29553
|
-
items.push(item);
|
|
29554
|
-
}
|
|
29555
|
-
}
|
|
29556
|
-
items.sort((a, b) => {
|
|
29557
|
-
const nameA = a.institution_name || "";
|
|
29558
|
-
const nameB = b.institution_name || "";
|
|
29559
|
-
if (nameA !== nameB) {
|
|
29560
|
-
return nameA.localeCompare(nameB);
|
|
29561
|
-
}
|
|
29562
|
-
return a.item_id.localeCompare(b.item_id);
|
|
29563
|
-
});
|
|
29564
|
-
const catSeen = new Set;
|
|
29565
|
-
const categories = [];
|
|
29566
|
-
for (const category of rawCategories) {
|
|
29567
|
-
if (!catSeen.has(category.category_id)) {
|
|
29568
|
-
catSeen.add(category.category_id);
|
|
29569
|
-
categories.push(category);
|
|
29570
|
-
}
|
|
29571
|
-
}
|
|
29572
|
-
categories.sort((a, b) => {
|
|
29573
|
-
const orderA = a.order ?? 999;
|
|
29574
|
-
const orderB = b.order ?? 999;
|
|
29575
|
-
if (orderA !== orderB) {
|
|
29576
|
-
return orderA - orderB;
|
|
29577
|
-
}
|
|
29578
|
-
const nameA = a.name || "";
|
|
29579
|
-
const nameB = b.name || "";
|
|
29580
|
-
return nameA.localeCompare(nameB);
|
|
29530
|
+
function decodeAllCollectionsIsolated(dbPath, timeoutMs = 30000) {
|
|
29531
|
+
return new Promise((resolve, reject) => {
|
|
29532
|
+
const selfUrl = import.meta.url;
|
|
29533
|
+
const workerExt = selfUrl.endsWith(".ts") ? ".ts" : ".js";
|
|
29534
|
+
const workerPath = path2.resolve(path2.dirname(fileURLToPath(selfUrl)), "decode-worker" + workerExt);
|
|
29535
|
+
const worker = new Worker(workerPath, {
|
|
29536
|
+
workerData: { dbPath }
|
|
29537
|
+
});
|
|
29538
|
+
let settled = false;
|
|
29539
|
+
const settle = (fn) => {
|
|
29540
|
+
if (!settled) {
|
|
29541
|
+
settled = true;
|
|
29542
|
+
clearTimeout(timer);
|
|
29543
|
+
fn();
|
|
29544
|
+
}
|
|
29545
|
+
};
|
|
29546
|
+
const timer = setTimeout(() => {
|
|
29547
|
+
settle(() => {
|
|
29548
|
+
worker.terminate();
|
|
29549
|
+
reject(new Error(`Decode worker timed out after ${timeoutMs}ms`));
|
|
29550
|
+
});
|
|
29551
|
+
}, timeoutMs);
|
|
29552
|
+
worker.on("message", (msg) => {
|
|
29553
|
+
if (msg.type === "result" && msg.data) {
|
|
29554
|
+
settle(() => resolve(msg.data));
|
|
29555
|
+
} else if (msg.type === "error") {
|
|
29556
|
+
settle(() => reject(new Error(msg.message ?? "Worker decoding failed")));
|
|
29557
|
+
}
|
|
29558
|
+
});
|
|
29559
|
+
worker.on("error", (err) => {
|
|
29560
|
+
settle(() => reject(err));
|
|
29561
|
+
});
|
|
29562
|
+
worker.on("exit", (code) => {
|
|
29563
|
+
settle(() => reject(new Error(code === 0 ? "Decode worker exited without sending result" : `Decode worker exited with code ${code}`)));
|
|
29564
|
+
});
|
|
29581
29565
|
});
|
|
29582
|
-
const userAccSeen = new Set;
|
|
29583
|
-
const userAccounts = [];
|
|
29584
|
-
for (const userAccount of rawUserAccounts) {
|
|
29585
|
-
if (!userAccSeen.has(userAccount.account_id)) {
|
|
29586
|
-
userAccSeen.add(userAccount.account_id);
|
|
29587
|
-
userAccounts.push(userAccount);
|
|
29588
|
-
}
|
|
29589
|
-
}
|
|
29590
|
-
return {
|
|
29591
|
-
transactions,
|
|
29592
|
-
accounts,
|
|
29593
|
-
recurring,
|
|
29594
|
-
budgets,
|
|
29595
|
-
goals,
|
|
29596
|
-
goalHistory,
|
|
29597
|
-
investmentPrices,
|
|
29598
|
-
investmentSplits,
|
|
29599
|
-
items,
|
|
29600
|
-
categories,
|
|
29601
|
-
userAccounts
|
|
29602
|
-
};
|
|
29603
29566
|
}
|
|
29604
29567
|
// src/models/category-full.ts
|
|
29605
29568
|
var ROOT_CATEGORIES = [
|
|
@@ -30796,12 +30759,12 @@ function findCopilotDatabase() {
|
|
|
30796
30759
|
}
|
|
30797
30760
|
} catch {}
|
|
30798
30761
|
}
|
|
30799
|
-
for (const
|
|
30762
|
+
for (const path3 of possiblePaths) {
|
|
30800
30763
|
try {
|
|
30801
|
-
if (existsSync(
|
|
30802
|
-
const files = readdirSync(
|
|
30764
|
+
if (existsSync(path3)) {
|
|
30765
|
+
const files = readdirSync(path3);
|
|
30803
30766
|
if (files.some((file2) => file2.endsWith(".ldb") || file2.startsWith("MANIFEST-"))) {
|
|
30804
|
-
return
|
|
30767
|
+
return path3;
|
|
30805
30768
|
}
|
|
30806
30769
|
}
|
|
30807
30770
|
} catch {}
|
|
@@ -30938,7 +30901,7 @@ class CopilotDatabase {
|
|
|
30938
30901
|
await this._loadingAllCollections;
|
|
30939
30902
|
return;
|
|
30940
30903
|
}
|
|
30941
|
-
this._loadingAllCollections =
|
|
30904
|
+
this._loadingAllCollections = decodeAllCollectionsIsolated(this.requireDbPath());
|
|
30942
30905
|
try {
|
|
30943
30906
|
const result = await this._loadingAllCollections;
|
|
30944
30907
|
this._transactions = result.transactions;
|
|
@@ -31935,6 +31898,50 @@ class CopilotMoneyTools {
|
|
|
31935
31898
|
accounts
|
|
31936
31899
|
};
|
|
31937
31900
|
}
|
|
31901
|
+
async getConnectionStatus() {
|
|
31902
|
+
const items = await this.db.getItems();
|
|
31903
|
+
const connections = items.map((item) => {
|
|
31904
|
+
let status;
|
|
31905
|
+
if (item.disconnected === true || item.connection_status === "disconnected") {
|
|
31906
|
+
status = "disconnected";
|
|
31907
|
+
} else if (item.error_code && item.error_code !== "ITEM_NO_ERROR" || item.connection_status === "error") {
|
|
31908
|
+
status = "error";
|
|
31909
|
+
} else if (item.login_required === true || itemNeedsAttention(item)) {
|
|
31910
|
+
status = "login_required";
|
|
31911
|
+
} else if (!isItemHealthy(item)) {
|
|
31912
|
+
status = "error";
|
|
31913
|
+
} else {
|
|
31914
|
+
status = "connected";
|
|
31915
|
+
}
|
|
31916
|
+
return {
|
|
31917
|
+
item_id: item.item_id,
|
|
31918
|
+
institution_name: getItemDisplayName(item),
|
|
31919
|
+
institution_id: item.institution_id,
|
|
31920
|
+
status,
|
|
31921
|
+
products: item.billed_products ?? [],
|
|
31922
|
+
last_transactions_update: item.status_transactions_last_successful_update ?? null,
|
|
31923
|
+
last_transactions_failed: item.status_transactions_last_failed_update ?? null,
|
|
31924
|
+
last_investments_update: item.status_investments_last_successful_update ?? null,
|
|
31925
|
+
last_investments_failed: item.status_investments_last_failed_update ?? null,
|
|
31926
|
+
latest_fetch: item.latest_fetch ?? null,
|
|
31927
|
+
latest_investments_fetch: item.latest_investments_fetch ?? null,
|
|
31928
|
+
login_required: item.login_required ?? false,
|
|
31929
|
+
disconnected: item.disconnected ?? false,
|
|
31930
|
+
consent_expires: item.consent_expiration_time || null,
|
|
31931
|
+
error_code: item.error_code ?? null,
|
|
31932
|
+
error_message: item.error_message ?? null
|
|
31933
|
+
};
|
|
31934
|
+
});
|
|
31935
|
+
const needsAttention = connections.filter((c) => c.status !== "connected").length;
|
|
31936
|
+
return {
|
|
31937
|
+
connections,
|
|
31938
|
+
summary: {
|
|
31939
|
+
total: connections.length,
|
|
31940
|
+
connected: connections.length - needsAttention,
|
|
31941
|
+
needs_attention: needsAttention
|
|
31942
|
+
}
|
|
31943
|
+
};
|
|
31944
|
+
}
|
|
31938
31945
|
async getCategories(options = {}) {
|
|
31939
31946
|
const { view = "list", parent_id, query, type, period } = options;
|
|
31940
31947
|
let start_date = validateDate(options.start_date, "start_date");
|
|
@@ -32593,6 +32600,17 @@ function createToolSchemas() {
|
|
|
32593
32600
|
readOnlyHint: true
|
|
32594
32601
|
}
|
|
32595
32602
|
},
|
|
32603
|
+
{
|
|
32604
|
+
name: "get_connection_status",
|
|
32605
|
+
description: "Get connection status for all linked financial institutions. " + "Shows per-institution sync health including last successful update timestamps " + "for transactions and investments, login requirements, and error states. " + "Use this to check when accounts were last synced or to identify connections needing attention.",
|
|
32606
|
+
inputSchema: {
|
|
32607
|
+
type: "object",
|
|
32608
|
+
properties: {}
|
|
32609
|
+
},
|
|
32610
|
+
annotations: {
|
|
32611
|
+
readOnlyHint: true
|
|
32612
|
+
}
|
|
32613
|
+
},
|
|
32596
32614
|
{
|
|
32597
32615
|
name: "get_categories",
|
|
32598
32616
|
description: "Unified category retrieval tool. Supports multiple views: " + "list (default) - categories with transaction counts/amounts for a time period; " + "tree - full Plaid category taxonomy as hierarchical tree; " + "search - search categories by keyword. Use parent_id to get subcategories. " + 'For list view, use period (e.g., "this_month") or start_date/end_date to filter by date. ' + "Includes all categories, even those with $0 spent (matching UI behavior).",
|
|
@@ -32773,6 +32791,9 @@ class CopilotMoneyServer {
|
|
|
32773
32791
|
case "get_accounts":
|
|
32774
32792
|
result = await this.tools.getAccounts(typedArgs);
|
|
32775
32793
|
break;
|
|
32794
|
+
case "get_connection_status":
|
|
32795
|
+
result = await this.tools.getConnectionStatus();
|
|
32796
|
+
break;
|
|
32776
32797
|
case "get_categories":
|
|
32777
32798
|
result = await this.tools.getCategories(typedArgs || {});
|
|
32778
32799
|
break;
|