simplefin-axi 0.0.0-stage → 0.1.1
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/bin.js +389 -0
- package/dist/bin.js.map +1 -0
- package/package.json +28 -4
- package/README.md +0 -3
package/dist/bin.js
ADDED
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cred.ts
|
|
4
|
+
import { execSync } from "child_process";
|
|
5
|
+
function resolveAccessUrl() {
|
|
6
|
+
const envUrl = process.env.SIMPLEFIN_ACCESS_URL;
|
|
7
|
+
if (envUrl) return envUrl;
|
|
8
|
+
try {
|
|
9
|
+
const result = execSync(
|
|
10
|
+
"bws secret list fe9eea09-9eaf-45e6-a72f-b45e0150b29d 2>/dev/null",
|
|
11
|
+
{ encoding: "utf-8", timeout: 1e4 }
|
|
12
|
+
);
|
|
13
|
+
const secrets = JSON.parse(result);
|
|
14
|
+
const match = secrets.find(
|
|
15
|
+
(s) => s.key === "SIMPLEFIN_ACCESS_URL"
|
|
16
|
+
);
|
|
17
|
+
if (match?.value) return match.value;
|
|
18
|
+
} catch {
|
|
19
|
+
}
|
|
20
|
+
return "";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// src/d1.ts
|
|
24
|
+
import { execSync as execSync2 } from "child_process";
|
|
25
|
+
import { writeFileSync, mkdtempSync, rmSync } from "fs";
|
|
26
|
+
import { join } from "path";
|
|
27
|
+
import { tmpdir } from "os";
|
|
28
|
+
var DB_NAME = "simplefin-store";
|
|
29
|
+
var REMOTE = process.env.SIMPLEFIN_D1_LOCAL ? "" : "--remote";
|
|
30
|
+
function wranglerD1(sql, json = true) {
|
|
31
|
+
const jsonFlag = json ? "--json" : "";
|
|
32
|
+
const cmd = `wrangler d1 execute ${DB_NAME} ${REMOTE} ${jsonFlag} --command '${sql.replace(/'/g, "'\\''")}' 2>/dev/null`;
|
|
33
|
+
return execSync2(cmd, { encoding: "utf-8", timeout: 3e4 });
|
|
34
|
+
}
|
|
35
|
+
function queryRows(sql) {
|
|
36
|
+
const raw = wranglerD1(sql);
|
|
37
|
+
try {
|
|
38
|
+
const parsed = JSON.parse(raw);
|
|
39
|
+
return parsed?.[0]?.results ?? [];
|
|
40
|
+
} catch {
|
|
41
|
+
return [];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function executeBulk(statements) {
|
|
45
|
+
const tmpDir = mkdtempSync(join(tmpdir(), "sfin-axi-"));
|
|
46
|
+
const tmpFile = join(tmpDir, "bulk.sql");
|
|
47
|
+
writeFileSync(tmpFile, statements.join(";\n"));
|
|
48
|
+
try {
|
|
49
|
+
execSync2(
|
|
50
|
+
`wrangler d1 execute ${DB_NAME} ${REMOTE} --file ${tmpFile} 2>/dev/null`,
|
|
51
|
+
{ encoding: "utf-8", timeout: 6e4 }
|
|
52
|
+
);
|
|
53
|
+
} finally {
|
|
54
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/store.ts
|
|
59
|
+
function parseAccessUrl(raw) {
|
|
60
|
+
const url = new URL(raw);
|
|
61
|
+
const user = decodeURIComponent(url.username);
|
|
62
|
+
const pass = decodeURIComponent(url.password);
|
|
63
|
+
const baseUrl = `${url.protocol}//${url.host}${url.pathname.replace(/\/$/, "")}`;
|
|
64
|
+
const authHeader = "Basic " + Buffer.from(`${user}:${pass}`).toString("base64");
|
|
65
|
+
return { baseUrl, authHeader };
|
|
66
|
+
}
|
|
67
|
+
async function fetchAccounts() {
|
|
68
|
+
const accessUrl = resolveAccessUrl();
|
|
69
|
+
if (!accessUrl) throw new Error("SIMPLEFIN_ACCESS_URL not found (env or BWS)");
|
|
70
|
+
const { baseUrl, authHeader } = parseAccessUrl(accessUrl);
|
|
71
|
+
const startDate = /* @__PURE__ */ new Date();
|
|
72
|
+
startDate.setDate(startDate.getDate() - 90);
|
|
73
|
+
const startTs = Math.floor(startDate.getTime() / 1e3);
|
|
74
|
+
const resp = await fetch(`${baseUrl}/accounts?start-date=${startTs}`, {
|
|
75
|
+
headers: { Accept: "application/json", Authorization: authHeader }
|
|
76
|
+
});
|
|
77
|
+
if (!resp.ok) throw new Error(`SimpleFIN /accounts: ${resp.status} ${await resp.text()}`);
|
|
78
|
+
const data = await resp.json();
|
|
79
|
+
return data.accounts ?? [];
|
|
80
|
+
}
|
|
81
|
+
function listAccounts() {
|
|
82
|
+
const rows = queryRows("SELECT * FROM accounts ORDER BY name");
|
|
83
|
+
return rows;
|
|
84
|
+
}
|
|
85
|
+
function getAccount(accountId) {
|
|
86
|
+
const rows = queryRows(`SELECT * FROM accounts WHERE id = '${accountId}'`);
|
|
87
|
+
return rows[0] ?? null;
|
|
88
|
+
}
|
|
89
|
+
function listTransactions(accountId, days = 30, limit = 100) {
|
|
90
|
+
const since = /* @__PURE__ */ new Date();
|
|
91
|
+
since.setDate(since.getDate() - days);
|
|
92
|
+
const sinceStr = since.toISOString().split("T")[0];
|
|
93
|
+
let sql = `SELECT * FROM transactions WHERE date >= '${sinceStr}'`;
|
|
94
|
+
if (accountId) sql += ` AND account_id = '${accountId}'`;
|
|
95
|
+
sql += ` ORDER BY date DESC LIMIT ${limit}`;
|
|
96
|
+
return queryRows(sql);
|
|
97
|
+
}
|
|
98
|
+
function getBalances() {
|
|
99
|
+
const rows = queryRows(`
|
|
100
|
+
SELECT
|
|
101
|
+
a.id as account_id,
|
|
102
|
+
a.name as account_name,
|
|
103
|
+
a.balance,
|
|
104
|
+
a.available_balance,
|
|
105
|
+
a.currency,
|
|
106
|
+
a.balance_date,
|
|
107
|
+
a.institution
|
|
108
|
+
FROM accounts a
|
|
109
|
+
ORDER BY a.name
|
|
110
|
+
`);
|
|
111
|
+
return rows;
|
|
112
|
+
}
|
|
113
|
+
function esc(s) {
|
|
114
|
+
if (!s) return "''";
|
|
115
|
+
return `'${s.replace(/'/g, "''")}'`;
|
|
116
|
+
}
|
|
117
|
+
function storeAccounts(accounts) {
|
|
118
|
+
if (accounts.length === 0) return 0;
|
|
119
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
120
|
+
const stmts = accounts.map((acct) => {
|
|
121
|
+
const balanceDate = new Date(acct["balance-date"] * 1e3).toISOString().split("T")[0];
|
|
122
|
+
return `INSERT INTO accounts (id, name, currency, balance, available_balance, balance_date, institution, org_domain, last_four, updated_at) VALUES (${esc(acct.id)}, ${esc(acct.name)}, ${esc(acct.currency)}, ${parseFloat(acct.balance) || 0}, ${parseFloat(acct["available-balance"]) || 0}, ${esc(balanceDate)}, ${esc(acct.org?.name || "")}, ${esc(acct.org?.domain || "")}, NULL, '${now}') ON CONFLICT(id) DO UPDATE SET name = excluded.name, balance = excluded.balance, available_balance = excluded.available_balance, balance_date = excluded.balance_date, institution = excluded.institution, updated_at = excluded.updated_at`;
|
|
123
|
+
});
|
|
124
|
+
executeBulk(stmts);
|
|
125
|
+
return accounts.length;
|
|
126
|
+
}
|
|
127
|
+
function storeTransactions(accountId, transactions) {
|
|
128
|
+
if (transactions.length === 0) return 0;
|
|
129
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
130
|
+
const stmts = transactions.map((tx) => {
|
|
131
|
+
const txDate = tx.transacted_at ? new Date(tx.transacted_at * 1e3).toISOString().split("T")[0] : "";
|
|
132
|
+
const postedDate = tx.posted ? new Date(tx.posted * 1e3).toISOString().split("T")[0] : null;
|
|
133
|
+
return `INSERT INTO transactions (id, account_id, amount, description, date, posted_date, payee, memo, mcc, updated_at) VALUES (${esc(tx.id)}, ${esc(accountId)}, ${parseFloat(tx.amount) || 0}, ${esc(tx.description)}, ${esc(txDate)}, ${postedDate ? esc(postedDate) : "NULL"}, ${tx.payee ? esc(tx.payee) : "NULL"}, ${tx.memo ? esc(tx.memo) : "NULL"}, ${tx.mcc ? esc(tx.mcc) : "NULL"}, '${now}') ON CONFLICT(id) DO UPDATE SET amount = excluded.amount, description = excluded.description, date = excluded.date, posted_date = excluded.posted_date, payee = excluded.payee, memo = excluded.memo, updated_at = excluded.updated_at`;
|
|
134
|
+
});
|
|
135
|
+
executeBulk(stmts);
|
|
136
|
+
return transactions.length;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// src/toon.ts
|
|
140
|
+
function toonTable(name, cols, rows) {
|
|
141
|
+
if (rows.length === 0) return `${name}: 0 found`;
|
|
142
|
+
const header = `${name}[${rows.length}]{${cols.join(",")}}:`;
|
|
143
|
+
const dataLines = rows.map(
|
|
144
|
+
(r) => cols.map((c) => formatVal(r[c])).join(",")
|
|
145
|
+
);
|
|
146
|
+
return [header, ...dataLines].join("\n");
|
|
147
|
+
}
|
|
148
|
+
function formatVal(v) {
|
|
149
|
+
if (v === null || v === void 0) return "";
|
|
150
|
+
if (typeof v === "string") {
|
|
151
|
+
if (v.includes(",") || v.includes('"') || v.includes("\n"))
|
|
152
|
+
return `"${v.replace(/"/g, '""')}"`;
|
|
153
|
+
return v;
|
|
154
|
+
}
|
|
155
|
+
return String(v);
|
|
156
|
+
}
|
|
157
|
+
function toonObject(name, fields, truncate) {
|
|
158
|
+
const lines = [`${name}:`];
|
|
159
|
+
for (const [k, v] of Object.entries(fields)) {
|
|
160
|
+
if (truncate && k === truncate.field && typeof v === "string" && v.length > truncate.limit) {
|
|
161
|
+
const shown = v.slice(0, truncate.limit);
|
|
162
|
+
lines.push(` ${k}: ${shown}... (truncated, ${v.length} chars total)`);
|
|
163
|
+
} else {
|
|
164
|
+
lines.push(` ${k}: ${formatVal(v)}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return lines.join("\n");
|
|
168
|
+
}
|
|
169
|
+
function toonHelp(hints) {
|
|
170
|
+
return hints.map((h, i) => `help[${i + 1}]:
|
|
171
|
+
${h}`).join("\n");
|
|
172
|
+
}
|
|
173
|
+
function toonError(msg, hints) {
|
|
174
|
+
return [`error: ${msg}`, toonHelp(hints)].join("\n");
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// src/main.ts
|
|
178
|
+
var VERSION = "0.1.0";
|
|
179
|
+
var DESCRIPTION = "Agent-ergonomic CLI for SimpleFIN financial data on Cloudflare D1";
|
|
180
|
+
function home() {
|
|
181
|
+
const bin = process.argv[1] || "simplefin-axi";
|
|
182
|
+
const parts = [`bin: ${bin.replace(process.env.HOME ?? "", "~")}`, `description: ${DESCRIPTION}`];
|
|
183
|
+
const tables = queryRows("SELECT name FROM sqlite_master WHERE type='table' AND name='accounts'");
|
|
184
|
+
if (tables.length === 0) {
|
|
185
|
+
parts.push("", "db: not initialized", toonHelp(["Run `simplefin-axi init` to create the D1 schema", "Run `simplefin-axi fetch` to pull accounts from SimpleFIN Bridge"]));
|
|
186
|
+
return parts.join("\n");
|
|
187
|
+
}
|
|
188
|
+
const accts = listAccounts();
|
|
189
|
+
if (accts.length === 0) {
|
|
190
|
+
parts.push("", "accounts: 0 (no data yet)", toonHelp(["Run `simplefin-axi fetch` to pull accounts from SimpleFIN Bridge"]));
|
|
191
|
+
return parts.join("\n");
|
|
192
|
+
}
|
|
193
|
+
parts.push("", toonTable("accounts", ["id", "name", "balance", "currency", "institution"], accts.map((a) => ({
|
|
194
|
+
id: a.id.slice(0, 8) + "...",
|
|
195
|
+
name: a.name,
|
|
196
|
+
balance: a.balance,
|
|
197
|
+
currency: a.currency,
|
|
198
|
+
institution: a.institution
|
|
199
|
+
}))));
|
|
200
|
+
parts.push("", toonHelp([
|
|
201
|
+
"Run `simplefin-axi balances` for balance summary",
|
|
202
|
+
"Run `simplefin-axi transactions` for recent transactions",
|
|
203
|
+
"Run `simplefin-axi fetch` to refresh from SimpleFIN Bridge",
|
|
204
|
+
"Run `simplefin-axi account <id>` for account detail"
|
|
205
|
+
]));
|
|
206
|
+
return parts.join("\n");
|
|
207
|
+
}
|
|
208
|
+
function initCmd() {
|
|
209
|
+
const existing = queryRows("SELECT name FROM sqlite_master WHERE type='table' AND name='accounts'");
|
|
210
|
+
if (existing.length > 0) return "db: already initialized (no-op)";
|
|
211
|
+
executeBulk([
|
|
212
|
+
`CREATE TABLE IF NOT EXISTS accounts (id TEXT PRIMARY KEY, name TEXT NOT NULL, currency TEXT NOT NULL DEFAULT 'USD', balance REAL NOT NULL DEFAULT 0, available_balance REAL NOT NULL DEFAULT 0, balance_date TEXT NOT NULL, institution TEXT NOT NULL DEFAULT '', org_domain TEXT NOT NULL DEFAULT '', last_four TEXT, updated_at TEXT NOT NULL)`,
|
|
213
|
+
`CREATE TABLE IF NOT EXISTS transactions (id TEXT PRIMARY KEY, account_id TEXT NOT NULL, amount REAL NOT NULL, description TEXT NOT NULL DEFAULT '', date TEXT NOT NULL, posted_date TEXT, payee TEXT, memo TEXT, mcc TEXT, updated_at TEXT NOT NULL, FOREIGN KEY (account_id) REFERENCES accounts(id))`,
|
|
214
|
+
`CREATE INDEX IF NOT EXISTS idx_transactions_account_date ON transactions(account_id, date DESC)`,
|
|
215
|
+
`CREATE INDEX IF NOT EXISTS idx_transactions_date ON transactions(date DESC)`,
|
|
216
|
+
`CREATE TABLE IF NOT EXISTS sync_log (id INTEGER PRIMARY KEY AUTOINCREMENT, started_at TEXT NOT NULL, completed_at TEXT, accounts_synced INTEGER NOT NULL DEFAULT 0, transactions_upserted INTEGER NOT NULL DEFAULT 0, status TEXT NOT NULL DEFAULT 'running')`
|
|
217
|
+
]);
|
|
218
|
+
return `db: initialized in D1 ${DB_NAME}
|
|
219
|
+
${toonHelp(["Run `simplefin-axi fetch` to pull accounts from SimpleFIN Bridge"])}`;
|
|
220
|
+
}
|
|
221
|
+
async function fetchCmd() {
|
|
222
|
+
const tables = queryRows("SELECT name FROM sqlite_master WHERE type='table' AND name='accounts'");
|
|
223
|
+
if (tables.length === 0) return toonError("Database not initialized", ["Run `simplefin-axi init` first"]);
|
|
224
|
+
const accounts = await fetchAccounts();
|
|
225
|
+
const acctCount = storeAccounts(accounts);
|
|
226
|
+
let txCount = 0;
|
|
227
|
+
for (const acct of accounts) {
|
|
228
|
+
if (acct.transactions && acct.transactions.length > 0) {
|
|
229
|
+
txCount += storeTransactions(acct.id, acct.transactions);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const accts = listAccounts();
|
|
233
|
+
return [
|
|
234
|
+
`synced: ${acctCount} accounts, ${txCount} transactions upserted`,
|
|
235
|
+
"",
|
|
236
|
+
toonTable("accounts", ["id", "name", "balance", "currency"], accts.map((a) => ({
|
|
237
|
+
id: a.id.slice(0, 8) + "...",
|
|
238
|
+
name: a.name,
|
|
239
|
+
balance: a.balance,
|
|
240
|
+
currency: a.currency
|
|
241
|
+
})))
|
|
242
|
+
].join("\n");
|
|
243
|
+
}
|
|
244
|
+
function balancesCmd() {
|
|
245
|
+
const bals = getBalances();
|
|
246
|
+
if (bals.length === 0) return "balances: 0 (no data yet)";
|
|
247
|
+
return toonTable("balances", ["account_name", "balance", "available_balance", "currency", "balance_date", "institution"], bals);
|
|
248
|
+
}
|
|
249
|
+
function transactionsCmd(args) {
|
|
250
|
+
let accountId;
|
|
251
|
+
let days = 30;
|
|
252
|
+
let limit = 100;
|
|
253
|
+
for (let i = 0; i < args.length; i++) {
|
|
254
|
+
if (args[i] === "--account" && args[i + 1]) {
|
|
255
|
+
accountId = args[++i];
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
if (args[i] === "--days" && args[i + 1]) {
|
|
259
|
+
days = parseInt(args[++i], 10);
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
if (args[i] === "--limit" && args[i + 1]) {
|
|
263
|
+
limit = parseInt(args[++i], 10);
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
if (!args[i].startsWith("--")) accountId = args[i];
|
|
267
|
+
}
|
|
268
|
+
const txns = listTransactions(accountId, days, limit);
|
|
269
|
+
if (txns.length === 0) return `transactions: 0 in last ${days} days`;
|
|
270
|
+
return [
|
|
271
|
+
`count: ${txns.length} of ${limit} max, last ${days} days`,
|
|
272
|
+
"",
|
|
273
|
+
toonTable("transactions", ["date", "amount", "description", "payee"], txns.map((t) => ({
|
|
274
|
+
date: t.date,
|
|
275
|
+
amount: t.amount,
|
|
276
|
+
description: (t.description || "").slice(0, 50),
|
|
277
|
+
payee: (t.payee || "").slice(0, 20)
|
|
278
|
+
}))),
|
|
279
|
+
"",
|
|
280
|
+
toonHelp([
|
|
281
|
+
"Run `simplefin-axi transactions --days 90` for a wider window",
|
|
282
|
+
"Run `simplefin-axi transactions --account <id>` to filter by account",
|
|
283
|
+
"Run `simplefin-axi transactions --limit 500` for more rows"
|
|
284
|
+
])
|
|
285
|
+
].join("\n");
|
|
286
|
+
}
|
|
287
|
+
function accountCmd(id) {
|
|
288
|
+
const acct = getAccount(id);
|
|
289
|
+
if (!acct) return toonError(`Account ${id} not found`, ["Run `simplefin-axi accounts` to list accounts"]);
|
|
290
|
+
return toonObject("account", {
|
|
291
|
+
id: acct.id,
|
|
292
|
+
name: acct.name,
|
|
293
|
+
balance: acct.balance,
|
|
294
|
+
available_balance: acct.available_balance,
|
|
295
|
+
currency: acct.currency,
|
|
296
|
+
balance_date: acct.balance_date,
|
|
297
|
+
institution: acct.institution,
|
|
298
|
+
last_four: acct.last_four ?? ""
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
function accountsCmd() {
|
|
302
|
+
const accts = listAccounts();
|
|
303
|
+
if (accts.length === 0) return "accounts: 0 (run `simplefin-axi fetch` to populate)";
|
|
304
|
+
return toonTable("accounts", ["id", "name", "balance", "currency", "institution", "balance_date"], accts.map((a) => ({
|
|
305
|
+
id: a.id.slice(0, 8) + "...",
|
|
306
|
+
name: a.name,
|
|
307
|
+
balance: a.balance,
|
|
308
|
+
currency: a.currency,
|
|
309
|
+
institution: a.institution,
|
|
310
|
+
balance_date: a.balance_date
|
|
311
|
+
})));
|
|
312
|
+
}
|
|
313
|
+
function helpCmd() {
|
|
314
|
+
return [
|
|
315
|
+
`simplefin-axi \u2014 ${DESCRIPTION}`,
|
|
316
|
+
"",
|
|
317
|
+
"usage: simplefin-axi [command] [args] [flags]",
|
|
318
|
+
"",
|
|
319
|
+
"commands[7]:",
|
|
320
|
+
" (none) dashboard \u2014 accounts overview and next steps",
|
|
321
|
+
" init create D1 schema (idempotent)",
|
|
322
|
+
" fetch pull accounts + transactions from SimpleFIN Bridge into D1",
|
|
323
|
+
" accounts list all accounts",
|
|
324
|
+
" account show one account detail",
|
|
325
|
+
" balances balance summary across accounts",
|
|
326
|
+
" transactions recent transactions (--days, --account, --limit)",
|
|
327
|
+
"",
|
|
328
|
+
"flags:",
|
|
329
|
+
" --help, -v/--version",
|
|
330
|
+
"",
|
|
331
|
+
"examples:",
|
|
332
|
+
" simplefin-axi",
|
|
333
|
+
" simplefin-axi init",
|
|
334
|
+
" simplefin-axi fetch",
|
|
335
|
+
" simplefin-axi balances",
|
|
336
|
+
" simplefin-axi transactions --days 90",
|
|
337
|
+
" simplefin-axi account <id>"
|
|
338
|
+
].join("\n");
|
|
339
|
+
}
|
|
340
|
+
async function main(argv) {
|
|
341
|
+
const cmd = argv[0];
|
|
342
|
+
if (cmd === "--help" || cmd === "-h") {
|
|
343
|
+
process.stdout.write(helpCmd() + "\n");
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
if (cmd === "--version" || cmd === "-v") {
|
|
347
|
+
process.stdout.write(`simplefin-axi ${VERSION}
|
|
348
|
+
`);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
switch (cmd) {
|
|
352
|
+
case void 0:
|
|
353
|
+
process.stdout.write(home() + "\n");
|
|
354
|
+
break;
|
|
355
|
+
case "init":
|
|
356
|
+
process.stdout.write(initCmd() + "\n");
|
|
357
|
+
break;
|
|
358
|
+
case "fetch":
|
|
359
|
+
process.stdout.write(await fetchCmd() + "\n");
|
|
360
|
+
break;
|
|
361
|
+
case "accounts":
|
|
362
|
+
process.stdout.write(accountsCmd() + "\n");
|
|
363
|
+
break;
|
|
364
|
+
case "account":
|
|
365
|
+
if (!argv[1]) {
|
|
366
|
+
process.stdout.write(toonError("account id required", ["Run `simplefin-axi accounts` to list accounts"]) + "\n");
|
|
367
|
+
process.exit(2);
|
|
368
|
+
}
|
|
369
|
+
process.stdout.write(accountCmd(argv[1]) + "\n");
|
|
370
|
+
break;
|
|
371
|
+
case "balances":
|
|
372
|
+
process.stdout.write(balancesCmd() + "\n");
|
|
373
|
+
break;
|
|
374
|
+
case "transactions":
|
|
375
|
+
process.stdout.write(transactionsCmd(argv.slice(1)) + "\n");
|
|
376
|
+
break;
|
|
377
|
+
default:
|
|
378
|
+
process.stdout.write(toonError(`unknown command: ${cmd}`, ["Run `simplefin-axi --help` for available commands"]) + "\n");
|
|
379
|
+
process.exit(2);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// bin.ts
|
|
384
|
+
main(process.argv.slice(2)).catch((e) => {
|
|
385
|
+
process.stdout.write(`error: ${e.message}
|
|
386
|
+
`);
|
|
387
|
+
process.exit(1);
|
|
388
|
+
});
|
|
389
|
+
//# sourceMappingURL=bin.js.map
|
package/dist/bin.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cred.ts","../src/d1.ts","../src/store.ts","../src/toon.ts","../src/main.ts","../bin.ts"],"sourcesContent":["import { execSync } from \"node:child_process\";\n\nexport function resolveAccessUrl(): string {\n const envUrl = process.env.SIMPLEFIN_ACCESS_URL;\n if (envUrl) return envUrl;\n\n try {\n const result = execSync(\n \"bws secret list fe9eea09-9eaf-45e6-a72f-b45e0150b29d 2>/dev/null\",\n { encoding: \"utf-8\", timeout: 10000 }\n );\n const secrets = JSON.parse(result);\n const match = secrets.find(\n (s: { key: string }) => s.key === \"SIMPLEFIN_ACCESS_URL\"\n );\n if (match?.value) return match.value;\n } catch {}\n\n return \"\";\n}\n","import { execSync } from \"node:child_process\";\nimport { writeFileSync, mkdtempSync, rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { tmpdir } from \"node:os\";\n\nconst DB_NAME = \"simplefin-store\";\nconst REMOTE = process.env.SIMPLEFIN_D1_LOCAL ? \"\" : \"--remote\";\n\nfunction wranglerD1(sql: string, json = true): string {\n const jsonFlag = json ? \"--json\" : \"\";\n const cmd = `wrangler d1 execute ${DB_NAME} ${REMOTE} ${jsonFlag} --command '${sql.replace(/'/g, \"'\\\\''\")}' 2>/dev/null`;\n return execSync(cmd, { encoding: \"utf-8\", timeout: 30000 });\n}\n\ninterface D1Row {\n [key: string]: unknown;\n}\n\nfunction queryRows(sql: string): D1Row[] {\n const raw = wranglerD1(sql);\n try {\n const parsed = JSON.parse(raw);\n return parsed?.[0]?.results ?? [];\n } catch {\n return [];\n }\n}\n\nfunction execute(sql: string): void {\n wranglerD1(sql, false);\n}\n\nfunction executeBulk(statements: string[]): void {\n const tmpDir = mkdtempSync(join(tmpdir(), \"sfin-axi-\"));\n const tmpFile = join(tmpDir, \"bulk.sql\");\n writeFileSync(tmpFile, statements.join(\";\\n\"));\n try {\n execSync(\n `wrangler d1 execute ${DB_NAME} ${REMOTE} --file ${tmpFile} 2>/dev/null`,\n { encoding: \"utf-8\", timeout: 60000 }\n );\n } finally {\n rmSync(tmpDir, { recursive: true, force: true });\n }\n}\n\nexport { queryRows, execute, executeBulk, DB_NAME };\n","import { resolveAccessUrl } from \"./cred.js\";\nimport { queryRows, executeBulk } from \"./d1.js\";\nimport type { D1Account, D1Transaction, AccountBalance, SimpleFINAccount, SimpleFINTransaction } from \"./types.js\";\n\nfunction parseAccessUrl(raw: string): { baseUrl: string; authHeader: string } {\n const url = new URL(raw);\n const user = decodeURIComponent(url.username);\n const pass = decodeURIComponent(url.password);\n const baseUrl = `${url.protocol}//${url.host}${url.pathname.replace(/\\/$/, \"\")}`;\n const authHeader = \"Basic \" + Buffer.from(`${user}:${pass}`).toString(\"base64\");\n return { baseUrl, authHeader };\n}\n\nexport async function fetchAccounts(): Promise<SimpleFINAccount[]> {\n const accessUrl = resolveAccessUrl();\n if (!accessUrl) throw new Error(\"SIMPLEFIN_ACCESS_URL not found (env or BWS)\");\n\n const { baseUrl, authHeader } = parseAccessUrl(accessUrl);\n const startDate = new Date();\n startDate.setDate(startDate.getDate() - 90);\n const startTs = Math.floor(startDate.getTime() / 1000);\n\n const resp = await fetch(`${baseUrl}/accounts?start-date=${startTs}`, {\n headers: { Accept: \"application/json\", Authorization: authHeader },\n });\n if (!resp.ok) throw new Error(`SimpleFIN /accounts: ${resp.status} ${await resp.text()}`);\n\n const data = await resp.json() as { errors: unknown[]; accounts: SimpleFINAccount[] };\n return data.accounts ?? [];\n}\n\nexport function listAccounts(): D1Account[] {\n const rows = queryRows(\"SELECT * FROM accounts ORDER BY name\");\n return rows as D1Account[];\n}\n\nexport function getAccount(accountId: string): D1Account | null {\n const rows = queryRows(`SELECT * FROM accounts WHERE id = '${accountId}'`);\n return (rows[0] as D1Account) ?? null;\n}\n\nexport function listTransactions(accountId?: string, days = 30, limit = 100): D1Transaction[] {\n const since = new Date();\n since.setDate(since.getDate() - days);\n const sinceStr = since.toISOString().split(\"T\")[0];\n\n let sql = `SELECT * FROM transactions WHERE date >= '${sinceStr}'`;\n if (accountId) sql += ` AND account_id = '${accountId}'`;\n sql += ` ORDER BY date DESC LIMIT ${limit}`;\n\n return queryRows(sql) as D1Transaction[];\n}\n\nexport function getBalances(): AccountBalance[] {\n const rows = queryRows(`\n SELECT\n a.id as account_id,\n a.name as account_name,\n a.balance,\n a.available_balance,\n a.currency,\n a.balance_date,\n a.institution\n FROM accounts a\n ORDER BY a.name\n `);\n return rows as AccountBalance[];\n}\n\nfunction esc(s: string | null | undefined): string {\n if (!s) return \"''\";\n return `'${s.replace(/'/g, \"''\")}'`;\n}\n\nexport function storeAccounts(accounts: SimpleFINAccount[]): number {\n if (accounts.length === 0) return 0;\n const now = new Date().toISOString();\n const stmts = accounts.map((acct) => {\n const balanceDate = new Date(acct[\"balance-date\"] * 1000).toISOString().split(\"T\")[0];\n return `INSERT INTO accounts (id, name, currency, balance, available_balance, balance_date, institution, org_domain, last_four, updated_at) VALUES (${esc(acct.id)}, ${esc(acct.name)}, ${esc(acct.currency)}, ${parseFloat(acct.balance) || 0}, ${parseFloat(acct[\"available-balance\"]) || 0}, ${esc(balanceDate)}, ${esc(acct.org?.name || \"\")}, ${esc(acct.org?.domain || \"\")}, NULL, '${now}') ON CONFLICT(id) DO UPDATE SET name = excluded.name, balance = excluded.balance, available_balance = excluded.available_balance, balance_date = excluded.balance_date, institution = excluded.institution, updated_at = excluded.updated_at`;\n });\n executeBulk(stmts);\n return accounts.length;\n}\n\nexport function storeTransactions(accountId: string, transactions: SimpleFINTransaction[]): number {\n if (transactions.length === 0) return 0;\n const now = new Date().toISOString();\n const stmts = transactions.map((tx) => {\n const txDate = tx.transacted_at ? new Date(tx.transacted_at * 1000).toISOString().split(\"T\")[0] : \"\";\n const postedDate = tx.posted ? new Date(tx.posted * 1000).toISOString().split(\"T\")[0] : null;\n return `INSERT INTO transactions (id, account_id, amount, description, date, posted_date, payee, memo, mcc, updated_at) VALUES (${esc(tx.id)}, ${esc(accountId)}, ${parseFloat(tx.amount) || 0}, ${esc(tx.description)}, ${esc(txDate)}, ${postedDate ? esc(postedDate) : \"NULL\"}, ${tx.payee ? esc(tx.payee) : \"NULL\"}, ${tx.memo ? esc(tx.memo) : \"NULL\"}, ${tx.mcc ? esc(tx.mcc) : \"NULL\"}, '${now}') ON CONFLICT(id) DO UPDATE SET amount = excluded.amount, description = excluded.description, date = excluded.date, posted_date = excluded.posted_date, payee = excluded.payee, memo = excluded.memo, updated_at = excluded.updated_at`;\n });\n executeBulk(stmts);\n return transactions.length;\n}\n","export function toonTable(\n name: string,\n cols: string[],\n rows: Record<string, unknown>[]\n): string {\n if (rows.length === 0) return `${name}: 0 found`;\n const header = `${name}[${rows.length}]{${cols.join(\",\")}}:`;\n const dataLines = rows.map((r) =>\n cols.map((c) => formatVal(r[c])).join(\",\")\n );\n return [header, ...dataLines].join(\"\\n\");\n}\n\nfunction formatVal(v: unknown): string {\n if (v === null || v === undefined) return \"\";\n if (typeof v === \"string\") {\n if (v.includes(\",\") || v.includes('\"') || v.includes(\"\\n\"))\n return `\"${v.replace(/\"/g, '\"\"')}\"`;\n return v;\n }\n return String(v);\n}\n\nexport function toonObject(\n name: string,\n fields: Record<string, unknown>,\n truncate?: { field: string; limit: number }\n): string {\n const lines: string[] = [`${name}:`];\n for (const [k, v] of Object.entries(fields)) {\n if (truncate && k === truncate.field && typeof v === \"string\" && v.length > truncate.limit) {\n const shown = v.slice(0, truncate.limit);\n lines.push(` ${k}: ${shown}... (truncated, ${v.length} chars total)`);\n } else {\n lines.push(` ${k}: ${formatVal(v)}`);\n }\n }\n return lines.join(\"\\n\");\n}\n\nexport function toonHelp(hints: string[]): string {\n return hints.map((h, i) => `help[${i + 1}]:\\n ${h}`).join(\"\\n\");\n}\n\nexport function toonError(msg: string, hints: string[]): string {\n return [`error: ${msg}`, toonHelp(hints)].join(\"\\n\");\n}\n","import { fetchAccounts, listAccounts, getAccount, listTransactions, getBalances, storeAccounts, storeTransactions } from \"./store.js\";\nimport { execute, executeBulk, queryRows, DB_NAME } from \"./d1.js\";\nimport { toonTable, toonObject, toonHelp, toonError } from \"./toon.js\";\n\nconst VERSION = \"0.1.0\";\nconst DESCRIPTION = \"Agent-ergonomic CLI for SimpleFIN financial data on Cloudflare D1\";\n\nfunction home(): string {\n const bin = process.argv[1] || \"simplefin-axi\";\n const parts = [`bin: ${bin.replace(process.env.HOME ?? \"\", \"~\")}`, `description: ${DESCRIPTION}`];\n\n const tables = queryRows(\"SELECT name FROM sqlite_master WHERE type='table' AND name='accounts'\");\n if (tables.length === 0) {\n parts.push(\"\", \"db: not initialized\", toonHelp([\"Run `simplefin-axi init` to create the D1 schema\", \"Run `simplefin-axi fetch` to pull accounts from SimpleFIN Bridge\"]));\n return parts.join(\"\\n\");\n }\n\n const accts = listAccounts();\n if (accts.length === 0) {\n parts.push(\"\", \"accounts: 0 (no data yet)\", toonHelp([\"Run `simplefin-axi fetch` to pull accounts from SimpleFIN Bridge\"]));\n return parts.join(\"\\n\");\n }\n\n parts.push(\"\", toonTable(\"accounts\", [\"id\", \"name\", \"balance\", \"currency\", \"institution\"], accts.map((a) => ({\n id: a.id.slice(0, 8) + \"...\",\n name: a.name,\n balance: a.balance,\n currency: a.currency,\n institution: a.institution,\n }))));\n parts.push(\"\", toonHelp([\n \"Run `simplefin-axi balances` for balance summary\",\n \"Run `simplefin-axi transactions` for recent transactions\",\n \"Run `simplefin-axi fetch` to refresh from SimpleFIN Bridge\",\n \"Run `simplefin-axi account <id>` for account detail\",\n ]));\n return parts.join(\"\\n\");\n}\n\nfunction initCmd(): string {\n const existing = queryRows(\"SELECT name FROM sqlite_master WHERE type='table' AND name='accounts'\");\n if (existing.length > 0) return \"db: already initialized (no-op)\";\n\n executeBulk([\n `CREATE TABLE IF NOT EXISTS accounts (id TEXT PRIMARY KEY, name TEXT NOT NULL, currency TEXT NOT NULL DEFAULT 'USD', balance REAL NOT NULL DEFAULT 0, available_balance REAL NOT NULL DEFAULT 0, balance_date TEXT NOT NULL, institution TEXT NOT NULL DEFAULT '', org_domain TEXT NOT NULL DEFAULT '', last_four TEXT, updated_at TEXT NOT NULL)`,\n `CREATE TABLE IF NOT EXISTS transactions (id TEXT PRIMARY KEY, account_id TEXT NOT NULL, amount REAL NOT NULL, description TEXT NOT NULL DEFAULT '', date TEXT NOT NULL, posted_date TEXT, payee TEXT, memo TEXT, mcc TEXT, updated_at TEXT NOT NULL, FOREIGN KEY (account_id) REFERENCES accounts(id))`,\n `CREATE INDEX IF NOT EXISTS idx_transactions_account_date ON transactions(account_id, date DESC)`,\n `CREATE INDEX IF NOT EXISTS idx_transactions_date ON transactions(date DESC)`,\n `CREATE TABLE IF NOT EXISTS sync_log (id INTEGER PRIMARY KEY AUTOINCREMENT, started_at TEXT NOT NULL, completed_at TEXT, accounts_synced INTEGER NOT NULL DEFAULT 0, transactions_upserted INTEGER NOT NULL DEFAULT 0, status TEXT NOT NULL DEFAULT 'running')`,\n ]);\n\n return `db: initialized in D1 ${DB_NAME}\\n${toonHelp([\"Run `simplefin-axi fetch` to pull accounts from SimpleFIN Bridge\"])}`;\n}\n\nasync function fetchCmd(): Promise<string> {\n const tables = queryRows(\"SELECT name FROM sqlite_master WHERE type='table' AND name='accounts'\");\n if (tables.length === 0) return toonError(\"Database not initialized\", [\"Run `simplefin-axi init` first\"]);\n\n const accounts = await fetchAccounts();\n const acctCount = storeAccounts(accounts);\n\n let txCount = 0;\n for (const acct of accounts) {\n if (acct.transactions && acct.transactions.length > 0) {\n txCount += storeTransactions(acct.id, acct.transactions);\n }\n }\n\n const accts = listAccounts();\n return [\n `synced: ${acctCount} accounts, ${txCount} transactions upserted`,\n \"\",\n toonTable(\"accounts\", [\"id\", \"name\", \"balance\", \"currency\"], accts.map((a) => ({\n id: a.id.slice(0, 8) + \"...\",\n name: a.name,\n balance: a.balance,\n currency: a.currency,\n }))),\n ].join(\"\\n\");\n}\n\nfunction balancesCmd(): string {\n const bals = getBalances();\n if (bals.length === 0) return \"balances: 0 (no data yet)\";\n return toonTable(\"balances\", [\"account_name\", \"balance\", \"available_balance\", \"currency\", \"balance_date\", \"institution\"], bals);\n}\n\nfunction transactionsCmd(args: string[]): string {\n let accountId: string | undefined;\n let days = 30;\n let limit = 100;\n\n for (let i = 0; i < args.length; i++) {\n if (args[i] === \"--account\" && args[i + 1]) { accountId = args[++i]; continue; }\n if (args[i] === \"--days\" && args[i + 1]) { days = parseInt(args[++i], 10); continue; }\n if (args[i] === \"--limit\" && args[i + 1]) { limit = parseInt(args[++i], 10); continue; }\n if (!args[i].startsWith(\"--\")) accountId = args[i];\n }\n\n const txns = listTransactions(accountId, days, limit);\n if (txns.length === 0) return `transactions: 0 in last ${days} days`;\n\n return [\n `count: ${txns.length} of ${limit} max, last ${days} days`,\n \"\",\n toonTable(\"transactions\", [\"date\", \"amount\", \"description\", \"payee\"], txns.map((t) => ({\n date: t.date,\n amount: t.amount,\n description: (t.description || \"\").slice(0, 50),\n payee: (t.payee || \"\").slice(0, 20),\n }))),\n \"\",\n toonHelp([\n \"Run `simplefin-axi transactions --days 90` for a wider window\",\n \"Run `simplefin-axi transactions --account <id>` to filter by account\",\n \"Run `simplefin-axi transactions --limit 500` for more rows\",\n ]),\n ].join(\"\\n\");\n}\n\nfunction accountCmd(id: string): string {\n const acct = getAccount(id);\n if (!acct) return toonError(`Account ${id} not found`, [\"Run `simplefin-axi accounts` to list accounts\"]);\n\n return toonObject(\"account\", {\n id: acct.id,\n name: acct.name,\n balance: acct.balance,\n available_balance: acct.available_balance,\n currency: acct.currency,\n balance_date: acct.balance_date,\n institution: acct.institution,\n last_four: acct.last_four ?? \"\",\n });\n}\n\nfunction accountsCmd(): string {\n const accts = listAccounts();\n if (accts.length === 0) return \"accounts: 0 (run `simplefin-axi fetch` to populate)\";\n return toonTable(\"accounts\", [\"id\", \"name\", \"balance\", \"currency\", \"institution\", \"balance_date\"], accts.map((a) => ({\n id: a.id.slice(0, 8) + \"...\",\n name: a.name,\n balance: a.balance,\n currency: a.currency,\n institution: a.institution,\n balance_date: a.balance_date,\n })));\n}\n\nfunction helpCmd(): string {\n return [\n `simplefin-axi — ${DESCRIPTION}`,\n \"\",\n \"usage: simplefin-axi [command] [args] [flags]\",\n \"\",\n \"commands[7]:\",\n \" (none) dashboard — accounts overview and next steps\",\n \" init create D1 schema (idempotent)\",\n \" fetch pull accounts + transactions from SimpleFIN Bridge into D1\",\n \" accounts list all accounts\",\n \" account show one account detail\",\n \" balances balance summary across accounts\",\n \" transactions recent transactions (--days, --account, --limit)\",\n \"\",\n \"flags:\",\n \" --help, -v/--version\",\n \"\",\n \"examples:\",\n \" simplefin-axi\",\n \" simplefin-axi init\",\n \" simplefin-axi fetch\",\n \" simplefin-axi balances\",\n \" simplefin-axi transactions --days 90\",\n \" simplefin-axi account <id>\",\n ].join(\"\\n\");\n}\n\nexport async function main(argv: string[]): Promise<void> {\n const cmd = argv[0];\n\n if (cmd === \"--help\" || cmd === \"-h\") { process.stdout.write(helpCmd() + \"\\n\"); return; }\n if (cmd === \"--version\" || cmd === \"-v\") { process.stdout.write(`simplefin-axi ${VERSION}\\n`); return; }\n\n switch (cmd) {\n case undefined:\n process.stdout.write(home() + \"\\n\");\n break;\n case \"init\":\n process.stdout.write(initCmd() + \"\\n\");\n break;\n case \"fetch\":\n process.stdout.write((await fetchCmd()) + \"\\n\");\n break;\n case \"accounts\":\n process.stdout.write(accountsCmd() + \"\\n\");\n break;\n case \"account\":\n if (!argv[1]) { process.stdout.write(toonError(\"account id required\", [\"Run `simplefin-axi accounts` to list accounts\"]) + \"\\n\"); process.exit(2); }\n process.stdout.write(accountCmd(argv[1]) + \"\\n\");\n break;\n case \"balances\":\n process.stdout.write(balancesCmd() + \"\\n\");\n break;\n case \"transactions\":\n process.stdout.write(transactionsCmd(argv.slice(1)) + \"\\n\");\n break;\n default:\n process.stdout.write(toonError(`unknown command: ${cmd}`, [\"Run `simplefin-axi --help` for available commands\"]) + \"\\n\");\n process.exit(2);\n }\n}\n","#!/usr/bin/env node\n\nimport { main } from \"./src/main.js\";\nmain(process.argv.slice(2)).catch((e: Error) => {\n process.stdout.write(`error: ${e.message}\\n`);\n process.exit(1);\n});\n"],"mappings":"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","names":["execSync","execSync"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "simplefin-axi",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Agent-ergonomic CLI for SimpleFIN financial data on Cloudflare D1",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"simplefin-axi": "./dist/bin.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsup bin.ts --format esm --sourcemap",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/node": "^26.6.6",
|
|
18
|
+
"tsup": "^8.4.0",
|
|
19
|
+
"typescript": "^6.0.0"
|
|
20
|
+
},
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=20"
|
|
23
|
+
},
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/masculinecache/simplefin-axi"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/masculinecache/simplefin-axi"
|
|
30
|
+
}
|
package/README.md
DELETED