ray-finance 0.3.5 → 0.3.6
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/ai/tools.js +1 -1
- package/dist/cli/commands.js +44 -12
- package/dist/cli/index.js +17 -6
- package/package.json +1 -1
package/dist/ai/tools.js
CHANGED
|
@@ -378,7 +378,7 @@ export async function executeTool(db, toolName, toolInput) {
|
|
|
378
378
|
}
|
|
379
379
|
case "set_budget": {
|
|
380
380
|
db.prepare(`INSERT INTO budgets (category, monthly_limit) VALUES (?, ?)
|
|
381
|
-
ON CONFLICT(category) DO UPDATE SET monthly_limit = excluded.monthly_limit`).run(toolInput.category, toolInput.monthly_limit);
|
|
381
|
+
ON CONFLICT(category, period) DO UPDATE SET monthly_limit = excluded.monthly_limit`).run(toolInput.category, toolInput.monthly_limit);
|
|
382
382
|
return `Budget set: ${categoryLabel(toolInput.category)} at ${formatMoney(toolInput.monthly_limit)}/month`;
|
|
383
383
|
}
|
|
384
384
|
case "get_goals": {
|
package/dist/cli/commands.js
CHANGED
|
@@ -352,26 +352,58 @@ export async function runAdd() {
|
|
|
352
352
|
export async function runRemove() {
|
|
353
353
|
const readline = await import("readline");
|
|
354
354
|
const db = getDb();
|
|
355
|
-
const
|
|
356
|
-
|
|
357
|
-
|
|
355
|
+
const entries = [];
|
|
356
|
+
// Linked institutions (exclude manual-assets)
|
|
357
|
+
const institutions = db.prepare(`SELECT item_id, name FROM institutions WHERE item_id != 'manual-assets' ORDER BY created_at`).all();
|
|
358
|
+
for (const inst of institutions) {
|
|
359
|
+
entries.push({ kind: "institution", item_id: inst.item_id, name: inst.name });
|
|
360
|
+
}
|
|
361
|
+
// Manual accounts
|
|
362
|
+
const manuals = getManualAccounts(db);
|
|
363
|
+
for (const a of manuals) {
|
|
364
|
+
entries.push({ kind: "manual", account_id: a.account_id, name: a.name, balance: a.current_balance, type: a.type, listing_url: a.listing_url });
|
|
365
|
+
}
|
|
366
|
+
if (entries.length === 0) {
|
|
367
|
+
console.log("\nNo accounts to remove. Use 'ray link' or 'ray add' to add one.");
|
|
358
368
|
return;
|
|
359
369
|
}
|
|
360
|
-
console.log(`\n${heading("
|
|
361
|
-
for (let i = 0; i <
|
|
362
|
-
const
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
370
|
+
console.log(`\n${heading("Accounts")}\n`);
|
|
371
|
+
for (let i = 0; i < entries.length; i++) {
|
|
372
|
+
const e = entries[i];
|
|
373
|
+
if (e.kind === "institution") {
|
|
374
|
+
const acctCount = db.prepare(`SELECT COUNT(*) as c FROM accounts WHERE item_id = ?`).get(e.item_id).c;
|
|
375
|
+
console.log(` ${dim(`${i + 1}.`)} ${e.name} ${dim(`(${acctCount} account${acctCount !== 1 ? "s" : ""}, linked)`)}`);
|
|
376
|
+
}
|
|
377
|
+
else {
|
|
378
|
+
const typeLabel = e.type === "loan" || e.type === "credit" ? "liability" : "asset";
|
|
379
|
+
const url = e.listing_url ? dim(` — ${e.listing_url}`) : "";
|
|
380
|
+
console.log(` ${dim(`${i + 1}.`)} ${e.name} ${rawFormatMoney(e.balance)} (${typeLabel})${url}`);
|
|
381
|
+
}
|
|
366
382
|
}
|
|
367
383
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
368
384
|
const answer = (await new Promise(resolve => rl.question(`\n Remove which? (number, or Enter to cancel): `, resolve))).trim();
|
|
369
385
|
rl.close();
|
|
370
386
|
const idx = parseInt(answer, 10) - 1;
|
|
371
|
-
if (isNaN(idx) || idx < 0 || idx >=
|
|
387
|
+
if (isNaN(idx) || idx < 0 || idx >= entries.length)
|
|
372
388
|
return;
|
|
373
|
-
|
|
374
|
-
|
|
389
|
+
const entry = entries[idx];
|
|
390
|
+
if (entry.kind === "manual") {
|
|
391
|
+
removeManualAccount(db, entry.account_id);
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
// Remove all data for this institution
|
|
395
|
+
const accounts = db.prepare(`SELECT account_id FROM accounts WHERE item_id = ?`).all(entry.item_id);
|
|
396
|
+
for (const acct of accounts) {
|
|
397
|
+
db.prepare(`DELETE FROM transactions WHERE account_id = ?`).run(acct.account_id);
|
|
398
|
+
db.prepare(`DELETE FROM holdings WHERE account_id = ?`).run(acct.account_id);
|
|
399
|
+
db.prepare(`DELETE FROM investment_transactions WHERE account_id = ?`).run(acct.account_id);
|
|
400
|
+
db.prepare(`DELETE FROM liabilities WHERE account_id = ?`).run(acct.account_id);
|
|
401
|
+
db.prepare(`DELETE FROM recurring WHERE account_id = ?`).run(acct.account_id);
|
|
402
|
+
}
|
|
403
|
+
db.prepare(`DELETE FROM accounts WHERE item_id = ?`).run(entry.item_id);
|
|
404
|
+
db.prepare(`DELETE FROM institutions WHERE item_id = ?`).run(entry.item_id);
|
|
405
|
+
}
|
|
406
|
+
console.log(chalk.green(`\n Removed ${entry.name}.`));
|
|
375
407
|
console.log();
|
|
376
408
|
}
|
|
377
409
|
export function showAlerts() {
|
package/dist/cli/index.js
CHANGED
|
@@ -70,7 +70,7 @@ program
|
|
|
70
70
|
});
|
|
71
71
|
program
|
|
72
72
|
.command("remove")
|
|
73
|
-
.description("Remove a manual account")
|
|
73
|
+
.description("Remove a linked bank or manual account")
|
|
74
74
|
.action(async () => {
|
|
75
75
|
ensureConfigured();
|
|
76
76
|
const { runRemove } = await import("./commands.js");
|
|
@@ -192,25 +192,36 @@ program
|
|
|
192
192
|
const open = (await import("open")).default;
|
|
193
193
|
console.log("Opening billing portal...");
|
|
194
194
|
try {
|
|
195
|
-
const resp = await fetch(`${RAY_PROXY_BASE
|
|
195
|
+
const resp = await fetch(`${RAY_PROXY_BASE}/stripe/portal`, {
|
|
196
196
|
method: "POST",
|
|
197
197
|
headers: {
|
|
198
198
|
"content-type": "application/json",
|
|
199
199
|
"Authorization": `Bearer ${config.rayApiKey}`,
|
|
200
200
|
},
|
|
201
201
|
});
|
|
202
|
+
if (!resp.ok) {
|
|
203
|
+
const text = await resp.text().catch(() => "");
|
|
204
|
+
const msg = (() => { try {
|
|
205
|
+
return JSON.parse(text).error;
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
return text;
|
|
209
|
+
} })();
|
|
210
|
+
console.error(`Could not open billing portal (${resp.status}): ${msg || "unknown error"}`);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
202
213
|
const { url } = await resp.json();
|
|
203
214
|
// Only open URLs from trusted domains
|
|
204
215
|
const parsed = new URL(url);
|
|
205
216
|
if (!parsed.hostname.endsWith("stripe.com") && !parsed.hostname.endsWith("rayfinance.app")) {
|
|
206
|
-
console.error("Unexpected billing URL.
|
|
217
|
+
console.error("Unexpected billing URL.");
|
|
207
218
|
}
|
|
208
219
|
else {
|
|
209
220
|
await open(url);
|
|
210
221
|
}
|
|
211
222
|
}
|
|
212
|
-
catch {
|
|
213
|
-
console.error("Could not open billing portal
|
|
223
|
+
catch (err) {
|
|
224
|
+
console.error("Could not open billing portal:", err.message);
|
|
214
225
|
}
|
|
215
226
|
});
|
|
216
227
|
program
|
|
@@ -249,7 +260,7 @@ program.configureHelp({
|
|
|
249
260
|
{ name: "setup", desc: "Configure Ray (API keys, preferences)" },
|
|
250
261
|
{ name: "link", desc: "Link a new financial account via Plaid" },
|
|
251
262
|
{ name: "add", desc: "Add a manual account (home, car, crypto, etc.)" },
|
|
252
|
-
{ name: "remove", desc: "Remove a manual account" },
|
|
263
|
+
{ name: "remove", desc: "Remove a linked bank or manual account" },
|
|
253
264
|
{ name: "sync", desc: "Sync transactions from linked banks" },
|
|
254
265
|
{ name: "accounts", desc: "Show linked accounts and balances" },
|
|
255
266
|
{ name: "status", desc: "Show financial overview" },
|