neuronix-node 0.2.0 → 0.6.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.
@@ -0,0 +1,2 @@
1
+ import type { TaskInput, TaskOutput } from "./index.js";
2
+ export declare function handleAPAging(task: TaskInput): Promise<TaskOutput>;
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handleAPAging = handleAPAging;
4
+ async function handleAPAging(task) {
5
+ const start = Date.now();
6
+ const input = task.input_payload;
7
+ const asOf = input.as_of_date ? new Date(input.as_of_date) : new Date();
8
+ const bills = input.bills || [
9
+ { vendor: "Office Depot", bill_number: "BL-100", amount: 2340, date_received: "2026-02-01", date_due: "2026-03-03", amount_paid: 0 },
10
+ { vendor: "AWS", bill_number: "BL-101", amount: 4680, date_received: "2026-03-01", date_due: "2026-03-31", amount_paid: 0 },
11
+ { vendor: "Landlord LLC", bill_number: "BL-102", amount: 8500, date_received: "2026-03-01", date_due: "2026-04-01", amount_paid: 0 },
12
+ { vendor: "Insurance Co", bill_number: "BL-088", amount: 1250, date_received: "2025-12-15", date_due: "2026-01-15", amount_paid: 0 },
13
+ { vendor: "Marketing Agency", bill_number: "BL-095", amount: 5000, date_received: "2026-01-10", date_due: "2026-02-09", amount_paid: 2500 },
14
+ ];
15
+ const buckets = { current: [], days30: [], days60: [], days90: [], over90: [] };
16
+ let totalOwed = 0;
17
+ for (const bill of bills) {
18
+ const outstanding = bill.amount - (bill.amount_paid || 0);
19
+ if (outstanding <= 0)
20
+ continue;
21
+ const dueDate = new Date(bill.date_due);
22
+ const daysOverdue = Math.floor((asOf.getTime() - dueDate.getTime()) / 86400000);
23
+ const entry = { ...bill, outstanding, days_overdue: Math.max(daysOverdue, 0) };
24
+ totalOwed += outstanding;
25
+ if (daysOverdue <= 0)
26
+ buckets.current.push(entry);
27
+ else if (daysOverdue <= 30)
28
+ buckets.days30.push(entry);
29
+ else if (daysOverdue <= 60)
30
+ buckets.days60.push(entry);
31
+ else if (daysOverdue <= 90)
32
+ buckets.days90.push(entry);
33
+ else
34
+ buckets.over90.push(entry);
35
+ }
36
+ const bt = {
37
+ current: round(buckets.current.reduce((s, i) => s + i.outstanding, 0)),
38
+ days_1_30: round(buckets.days30.reduce((s, i) => s + i.outstanding, 0)),
39
+ days_31_60: round(buckets.days60.reduce((s, i) => s + i.outstanding, 0)),
40
+ days_61_90: round(buckets.days90.reduce((s, i) => s + i.outstanding, 0)),
41
+ over_90: round(buckets.over90.reduce((s, i) => s + i.outstanding, 0)),
42
+ };
43
+ const byVendor = {};
44
+ for (const b of [...buckets.over90, ...buckets.days90, ...buckets.days60, ...buckets.days30, ...buckets.current]) {
45
+ if (!byVendor[b.vendor])
46
+ byVendor[b.vendor] = { total: 0, bills: 0, oldest_days: 0 };
47
+ byVendor[b.vendor].total += b.outstanding;
48
+ byVendor[b.vendor].bills += 1;
49
+ byVendor[b.vendor].oldest_days = Math.max(byVendor[b.vendor].oldest_days, b.days_overdue);
50
+ }
51
+ const vendorSummary = Object.entries(byVendor).sort((a, b) => b[1].total - a[1].total).map(([n, d]) => ({ vendor: n, ...d, total: round(d.total) }));
52
+ const csv = [
53
+ "ACCOUNTS PAYABLE AGING REPORT",
54
+ `As of: ${asOf.toISOString().split("T")[0]}`,
55
+ `Total Owed: ${fmt(totalOwed)}`,
56
+ "",
57
+ "AGING SUMMARY",
58
+ "Bucket,Amount,% of Total,Bill Count",
59
+ `Current (not due),${fmt(bt.current)},${pct(bt.current, totalOwed)}%,${buckets.current.length}`,
60
+ `1-30 Days Overdue,${fmt(bt.days_1_30)},${pct(bt.days_1_30, totalOwed)}%,${buckets.days30.length}`,
61
+ `31-60 Days Overdue,${fmt(bt.days_31_60)},${pct(bt.days_31_60, totalOwed)}%,${buckets.days60.length}`,
62
+ `61-90 Days Overdue,${fmt(bt.days_61_90)},${pct(bt.days_61_90, totalOwed)}%,${buckets.days90.length}`,
63
+ `Over 90 Days,${fmt(bt.over_90)},${pct(bt.over_90, totalOwed)}%,${buckets.over90.length}`,
64
+ "",
65
+ "BY VENDOR",
66
+ "Vendor,Total Owed,Bills,Oldest Overdue (days)",
67
+ ...vendorSummary.map(v => `${esc(v.vendor)},${fmt(v.total)},${v.bills},${v.oldest_days}`),
68
+ "",
69
+ "DETAIL — ALL OUTSTANDING BILLS",
70
+ "Vendor,Bill #,Amount,Paid,Outstanding,Date Received,Date Due,Days Overdue",
71
+ ...[...buckets.over90, ...buckets.days90, ...buckets.days60, ...buckets.days30, ...buckets.current]
72
+ .map(b => `${esc(b.vendor)},${b.bill_number || ""},${fmt(b.amount)},${fmt(b.amount_paid || 0)},${fmt(b.outstanding)},${b.date_received},${b.date_due},${b.days_overdue}`),
73
+ "",
74
+ "GRAND TOTAL",
75
+ `Total Owed,${fmt(totalOwed)}`,
76
+ `Total Bills,${bills.filter(b => (b.amount - (b.amount_paid || 0)) > 0).length}`,
77
+ `Total Vendors,${vendorSummary.length}`,
78
+ ];
79
+ return {
80
+ text: `AP Aging Report as of ${asOf.toISOString().split("T")[0]}\nTotal Owed: ${fmt(totalOwed)}`,
81
+ output_csv: csv.join("\n"),
82
+ summary: { total_owed: totalOwed, buckets: bt, vendor_count: vendorSummary.length },
83
+ by_vendor: vendorSummary,
84
+ chart_data: { chart_type: "bar", title: "AP Aging Buckets", labels: ["Current", "1-30", "31-60", "61-90", "90+"], datasets: [{ label: "Owed", data: [bt.current, bt.days_1_30, bt.days_31_60, bt.days_61_90, bt.over_90] }] },
85
+ duration_ms: Date.now() - start,
86
+ };
87
+ }
88
+ function round(n) { return Math.round(n * 100) / 100; }
89
+ function fmt(n) { return "$" + n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); }
90
+ function pct(n, t) { return t > 0 ? (Math.round((n / t) * 1000) / 10).toString() : "0"; }
91
+ function esc(v) { return v.includes(",") ? `"${v}"` : v; }
@@ -0,0 +1,2 @@
1
+ import type { TaskInput, TaskOutput } from "./index.js";
2
+ export declare function handleARAging(task: TaskInput): Promise<TaskOutput>;
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handleARAging = handleARAging;
4
+ async function handleARAging(task) {
5
+ const start = Date.now();
6
+ const input = task.input_payload;
7
+ const asOf = input.as_of_date ? new Date(input.as_of_date) : new Date();
8
+ const invoices = input.invoices || [
9
+ { customer: "Acme Corp", invoice_number: "INV-001", amount: 5000, date_issued: "2026-01-15", date_due: "2026-02-14", amount_paid: 0 },
10
+ { customer: "Acme Corp", invoice_number: "INV-012", amount: 3200, date_issued: "2026-02-20", date_due: "2026-03-22", amount_paid: 0 },
11
+ { customer: "TechStart Inc", invoice_number: "INV-003", amount: 12000, date_issued: "2026-03-01", date_due: "2026-03-31", amount_paid: 6000 },
12
+ { customer: "GlobalTrade LLC", invoice_number: "INV-007", amount: 8500, date_issued: "2025-11-10", date_due: "2025-12-10", amount_paid: 0 },
13
+ { customer: "Smith & Co", invoice_number: "INV-009", amount: 1500, date_issued: "2026-03-15", date_due: "2026-04-14", amount_paid: 0 },
14
+ ];
15
+ // Calculate aging buckets
16
+ const buckets = { current: [], days30: [], days60: [], days90: [], over90: [] };
17
+ let totalOutstanding = 0;
18
+ for (const inv of invoices) {
19
+ const outstanding = inv.amount - (inv.amount_paid || 0);
20
+ if (outstanding <= 0)
21
+ continue;
22
+ const dueDate = new Date(inv.date_due);
23
+ const daysOverdue = Math.floor((asOf.getTime() - dueDate.getTime()) / 86400000);
24
+ const entry = { ...inv, outstanding, days_overdue: Math.max(daysOverdue, 0) };
25
+ totalOutstanding += outstanding;
26
+ if (daysOverdue <= 0)
27
+ buckets.current.push(entry);
28
+ else if (daysOverdue <= 30)
29
+ buckets.days30.push(entry);
30
+ else if (daysOverdue <= 60)
31
+ buckets.days60.push(entry);
32
+ else if (daysOverdue <= 90)
33
+ buckets.days90.push(entry);
34
+ else
35
+ buckets.over90.push(entry);
36
+ }
37
+ const bucketTotals = {
38
+ current: round(buckets.current.reduce((s, i) => s + i.outstanding, 0)),
39
+ days_1_30: round(buckets.days30.reduce((s, i) => s + i.outstanding, 0)),
40
+ days_31_60: round(buckets.days60.reduce((s, i) => s + i.outstanding, 0)),
41
+ days_61_90: round(buckets.days90.reduce((s, i) => s + i.outstanding, 0)),
42
+ over_90: round(buckets.over90.reduce((s, i) => s + i.outstanding, 0)),
43
+ };
44
+ // By customer summary
45
+ const byCustomer = {};
46
+ for (const inv of [...buckets.current, ...buckets.days30, ...buckets.days60, ...buckets.days90, ...buckets.over90]) {
47
+ if (!byCustomer[inv.customer])
48
+ byCustomer[inv.customer] = { total: 0, invoices: 0, oldest_days: 0 };
49
+ byCustomer[inv.customer].total += inv.outstanding;
50
+ byCustomer[inv.customer].invoices += 1;
51
+ byCustomer[inv.customer].oldest_days = Math.max(byCustomer[inv.customer].oldest_days, inv.days_overdue);
52
+ }
53
+ const customerSummary = Object.entries(byCustomer)
54
+ .sort((a, b) => b[1].total - a[1].total)
55
+ .map(([name, data]) => ({ customer: name, ...data, total: round(data.total) }));
56
+ // CSV output
57
+ const csv = [
58
+ "ACCOUNTS RECEIVABLE AGING REPORT",
59
+ `As of: ${asOf.toISOString().split("T")[0]}`,
60
+ `Total Outstanding: ${fmt(totalOutstanding)}`,
61
+ "",
62
+ "AGING SUMMARY",
63
+ "Bucket,Amount,% of Total,Invoice Count",
64
+ `Current (not due),${fmt(bucketTotals.current)},${pct(bucketTotals.current, totalOutstanding)}%,${buckets.current.length}`,
65
+ `1-30 Days,${fmt(bucketTotals.days_1_30)},${pct(bucketTotals.days_1_30, totalOutstanding)}%,${buckets.days30.length}`,
66
+ `31-60 Days,${fmt(bucketTotals.days_31_60)},${pct(bucketTotals.days_31_60, totalOutstanding)}%,${buckets.days60.length}`,
67
+ `61-90 Days,${fmt(bucketTotals.days_61_90)},${pct(bucketTotals.days_61_90, totalOutstanding)}%,${buckets.days90.length}`,
68
+ `Over 90 Days,${fmt(bucketTotals.over_90)},${pct(bucketTotals.over_90, totalOutstanding)}%,${buckets.over90.length}`,
69
+ "",
70
+ "BY CUSTOMER",
71
+ "Customer,Outstanding,Invoices,Oldest Overdue (days)",
72
+ ...customerSummary.map(c => `${esc(c.customer)},${fmt(c.total)},${c.invoices},${c.oldest_days}`),
73
+ "",
74
+ "DETAIL — ALL OUTSTANDING INVOICES",
75
+ "Customer,Invoice #,Amount,Paid,Outstanding,Date Issued,Date Due,Days Overdue",
76
+ ...[...buckets.over90, ...buckets.days90, ...buckets.days60, ...buckets.days30, ...buckets.current]
77
+ .map(i => `${esc(i.customer)},${i.invoice_number || ""},${fmt(i.amount)},${fmt(i.amount_paid || 0)},${fmt(i.outstanding)},${i.date_issued},${i.date_due},${i.days_overdue}`),
78
+ "",
79
+ "GRAND TOTAL",
80
+ `Total Outstanding,${fmt(totalOutstanding)}`,
81
+ `Total Invoices,${invoices.filter(i => (i.amount - (i.amount_paid || 0)) > 0).length}`,
82
+ `Total Customers,${customerSummary.length}`,
83
+ ];
84
+ return {
85
+ text: `AR Aging Report as of ${asOf.toISOString().split("T")[0]}\nTotal Outstanding: ${fmt(totalOutstanding)}\nCurrent: ${fmt(bucketTotals.current)} | 1-30: ${fmt(bucketTotals.days_1_30)} | 31-60: ${fmt(bucketTotals.days_31_60)} | 61-90: ${fmt(bucketTotals.days_61_90)} | 90+: ${fmt(bucketTotals.over_90)}`,
86
+ output_csv: csv.join("\n"),
87
+ summary: { total_outstanding: totalOutstanding, buckets: bucketTotals, customer_count: customerSummary.length },
88
+ by_customer: customerSummary,
89
+ chart_data: {
90
+ chart_type: "bar",
91
+ title: "AR Aging Buckets",
92
+ labels: ["Current", "1-30 Days", "31-60 Days", "61-90 Days", "90+ Days"],
93
+ datasets: [{ label: "Outstanding", data: [bucketTotals.current, bucketTotals.days_1_30, bucketTotals.days_31_60, bucketTotals.days_61_90, bucketTotals.over_90] }],
94
+ },
95
+ duration_ms: Date.now() - start,
96
+ };
97
+ }
98
+ function round(n) { return Math.round(n * 100) / 100; }
99
+ function fmt(n) { return "$" + n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); }
100
+ function pct(n, total) { return total > 0 ? (Math.round((n / total) * 1000) / 10).toString() : "0"; }
101
+ function esc(v) { return v.includes(",") ? `"${v}"` : v; }
@@ -0,0 +1,2 @@
1
+ import type { TaskInput, TaskOutput } from "./index.js";
2
+ export declare function handleBankReconciliation(task: TaskInput): Promise<TaskOutput>;
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handleBankReconciliation = handleBankReconciliation;
4
+ async function handleBankReconciliation(task) {
5
+ const start = Date.now();
6
+ const input = task.input_payload;
7
+ const bankTxns = input.bank_transactions || [];
8
+ const bookTxns = input.book_transactions || [];
9
+ const bankBal = input.bank_balance || 0;
10
+ const bookBal = input.book_balance || 0;
11
+ // Match transactions by amount and approximate date
12
+ const matched = [];
13
+ const unmatchedBank = [];
14
+ const unmatchedBook = [];
15
+ const usedBook = new Set();
16
+ for (const bt of bankTxns) {
17
+ let found = false;
18
+ for (let i = 0; i < bookTxns.length; i++) {
19
+ if (usedBook.has(i))
20
+ continue;
21
+ const bk = bookTxns[i];
22
+ if (Math.abs(bt.amount) === Math.abs(bk.amount) && bt.type === bk.type) {
23
+ const daysDiff = Math.abs(new Date(bt.date).getTime() - new Date(bk.date).getTime()) / 86400000;
24
+ if (daysDiff <= 5) {
25
+ matched.push({ bank: bt, book: bk });
26
+ usedBook.add(i);
27
+ found = true;
28
+ break;
29
+ }
30
+ }
31
+ }
32
+ if (!found)
33
+ unmatchedBank.push(bt);
34
+ }
35
+ for (let i = 0; i < bookTxns.length; i++) {
36
+ if (!usedBook.has(i))
37
+ unmatchedBook.push(bookTxns[i]);
38
+ }
39
+ const outstandingDeposits = unmatchedBook.filter(t => t.type === "credit");
40
+ const outstandingChecks = unmatchedBook.filter(t => t.type === "debit");
41
+ const bankOnlyItems = unmatchedBank;
42
+ const depositTotal = round(outstandingDeposits.reduce((s, t) => s + Math.abs(t.amount), 0));
43
+ const checkTotal = round(outstandingChecks.reduce((s, t) => s + Math.abs(t.amount), 0));
44
+ const bankOnlyTotal = round(bankOnlyItems.reduce((s, t) => s + (t.type === "credit" ? t.amount : -t.amount), 0));
45
+ const adjustedBankBal = round(bankBal + depositTotal - checkTotal);
46
+ const adjustedBookBal = round(bookBal + bankOnlyTotal);
47
+ const difference = round(adjustedBankBal - adjustedBookBal);
48
+ const csv = [
49
+ "BANK RECONCILIATION",
50
+ `Date: ${new Date().toISOString().split("T")[0]}`,
51
+ "",
52
+ "RECONCILIATION SUMMARY",
53
+ `Bank Statement Balance,${fmt(bankBal)}`,
54
+ `Add: Outstanding Deposits,${fmt(depositTotal)}`,
55
+ `Less: Outstanding Checks,${fmt(checkTotal)}`,
56
+ `Adjusted Bank Balance,${fmt(adjustedBankBal)}`,
57
+ "",
58
+ `Book Balance,${fmt(bookBal)}`,
59
+ `Add/Less: Bank-Only Items,${fmt(bankOnlyTotal)}`,
60
+ `Adjusted Book Balance,${fmt(adjustedBookBal)}`,
61
+ "",
62
+ `DIFFERENCE,${fmt(difference)}`,
63
+ `Status,${Math.abs(difference) < 0.01 ? "RECONCILED ✓" : "UNRECONCILED — review needed"}`,
64
+ "",
65
+ `MATCHED TRANSACTIONS (${matched.length})`,
66
+ "Date,Description,Amount,Type",
67
+ ...matched.map(m => `${m.bank.date},${esc(m.bank.description)},${fmt(Math.abs(m.bank.amount))},${m.bank.type}`),
68
+ "",
69
+ `OUTSTANDING DEPOSITS — In Books Not Yet in Bank (${outstandingDeposits.length})`,
70
+ "Date,Description,Amount",
71
+ ...outstandingDeposits.map(t => `${t.date},${esc(t.description)},${fmt(Math.abs(t.amount))}`),
72
+ "",
73
+ `OUTSTANDING CHECKS — In Books Not Yet Cleared (${outstandingChecks.length})`,
74
+ "Date,Description,Amount",
75
+ ...outstandingChecks.map(t => `${t.date},${esc(t.description)},${fmt(Math.abs(t.amount))}`),
76
+ "",
77
+ `BANK-ONLY ITEMS — In Bank Not in Books (${bankOnlyItems.length})`,
78
+ "Date,Description,Amount,Type",
79
+ ...bankOnlyItems.map(t => `${t.date},${esc(t.description)},${fmt(Math.abs(t.amount))},${t.type}`),
80
+ "",
81
+ "TOTALS",
82
+ `Matched Transactions,${matched.length}`,
83
+ `Outstanding Deposits,${outstandingDeposits.length}`,
84
+ `Outstanding Checks,${outstandingChecks.length}`,
85
+ `Bank-Only Items,${bankOnlyItems.length}`,
86
+ ];
87
+ return {
88
+ text: `Bank Reconciliation: ${Math.abs(difference) < 0.01 ? "RECONCILED ✓" : `UNRECONCILED — difference of ${fmt(difference)}`}\nMatched: ${matched.length} | Unmatched Bank: ${unmatchedBank.length} | Unmatched Book: ${unmatchedBook.length}`,
89
+ output_csv: csv.join("\n"),
90
+ summary: { bank_balance: bankBal, book_balance: bookBal, adjusted_bank: adjustedBankBal, adjusted_book: adjustedBookBal, difference, reconciled: Math.abs(difference) < 0.01, matched_count: matched.length },
91
+ duration_ms: Date.now() - start,
92
+ };
93
+ }
94
+ function round(n) { return Math.round(n * 100) / 100; }
95
+ function fmt(n) { return "$" + n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); }
96
+ function esc(v) { return v.includes(",") ? `"${v}"` : v; }
@@ -0,0 +1,2 @@
1
+ import type { TaskInput, TaskOutput } from "./index.js";
2
+ export declare function handleBudgetVsActuals(task: TaskInput): Promise<TaskOutput>;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handleBudgetVsActuals = handleBudgetVsActuals;
4
+ async function handleBudgetVsActuals(task) {
5
+ const start = Date.now();
6
+ const input = task.input_payload;
7
+ const period = input.period || "Current Period";
8
+ const lines = input.lines || [
9
+ { category: "Salaries", budget: 70000, actual: 72800 },
10
+ { category: "Rent", budget: 8500, actual: 8500 },
11
+ { category: "Marketing", budget: 15000, actual: 18200 },
12
+ { category: "Technology", budget: 10000, actual: 12400 },
13
+ { category: "Travel", budget: 5000, actual: 7200 },
14
+ { category: "Office Supplies", budget: 2000, actual: 1650 },
15
+ { category: "Insurance", budget: 3000, actual: 3000 },
16
+ { category: "Utilities", budget: 2500, actual: 2180 },
17
+ ];
18
+ let totalBudget = 0, totalActual = 0;
19
+ const analyzed = lines.map(l => {
20
+ const variance = round(l.actual - l.budget);
21
+ const variancePct = l.budget > 0 ? round((variance / l.budget) * 100) : 0;
22
+ const status = Math.abs(variancePct) <= 5 ? "On Track" : variance > 0 ? "Over Budget" : "Under Budget";
23
+ totalBudget += l.budget;
24
+ totalActual += l.actual;
25
+ return { ...l, variance, variance_pct: variancePct, status };
26
+ });
27
+ totalBudget = round(totalBudget);
28
+ totalActual = round(totalActual);
29
+ const totalVariance = round(totalActual - totalBudget);
30
+ const totalVariancePct = totalBudget > 0 ? round((totalVariance / totalBudget) * 100) : 0;
31
+ const overBudget = analyzed.filter(l => l.variance > 0).sort((a, b) => b.variance - a.variance);
32
+ const underBudget = analyzed.filter(l => l.variance < 0).sort((a, b) => a.variance - b.variance);
33
+ const csv = [
34
+ "BUDGET VS ACTUALS REPORT",
35
+ `Period: ${period}`,
36
+ `Generated: ${new Date().toISOString().split("T")[0]}`,
37
+ "",
38
+ "DETAILED COMPARISON",
39
+ "Category,Budget,Actual,Variance ($),Variance (%),Status",
40
+ ...analyzed.map(l => `${esc(l.category)},${fmt(l.budget)},${fmt(l.actual)},${l.variance >= 0 ? "+" : ""}${fmt(l.variance)},${l.variance_pct >= 0 ? "+" : ""}${l.variance_pct}%,${l.status}`),
41
+ "",
42
+ `TOTAL,${fmt(totalBudget)},${fmt(totalActual)},${totalVariance >= 0 ? "+" : ""}${fmt(totalVariance)},${totalVariancePct >= 0 ? "+" : ""}${totalVariancePct}%,${Math.abs(totalVariancePct) <= 5 ? "On Track" : totalVariance > 0 ? "Over Budget" : "Under Budget"}`,
43
+ "",
44
+ "OVER BUDGET ITEMS",
45
+ "Category,Over By,% Over",
46
+ ...overBudget.map(l => `${esc(l.category)},${fmt(l.variance)},+${l.variance_pct}%`),
47
+ "",
48
+ "UNDER BUDGET ITEMS",
49
+ "Category,Under By,% Under",
50
+ ...underBudget.map(l => `${esc(l.category)},${fmt(Math.abs(l.variance))},${l.variance_pct}%`),
51
+ "",
52
+ "SUMMARY",
53
+ `Total Budget,${fmt(totalBudget)}`,
54
+ `Total Actual,${fmt(totalActual)}`,
55
+ `Net Variance,${totalVariance >= 0 ? "+" : ""}${fmt(totalVariance)} (${totalVariancePct >= 0 ? "+" : ""}${totalVariancePct}%)`,
56
+ `Categories Over Budget,${overBudget.length}`,
57
+ `Categories Under Budget,${underBudget.length}`,
58
+ `Categories On Track,${analyzed.filter(l => l.status === "On Track").length}`,
59
+ ];
60
+ return {
61
+ text: `Budget vs Actuals: ${period}\nBudget: ${fmt(totalBudget)} | Actual: ${fmt(totalActual)} | Variance: ${totalVariance >= 0 ? "+" : ""}${fmt(totalVariance)} (${totalVariancePct}%)`,
62
+ output_csv: csv.join("\n"),
63
+ summary: { total_budget: totalBudget, total_actual: totalActual, total_variance: totalVariance, total_variance_pct: totalVariancePct },
64
+ chart_data: { chart_type: "bar", title: `Budget vs Actuals — ${period}`, labels: analyzed.map(l => l.category), datasets: [{ label: "Budget", data: analyzed.map(l => l.budget) }, { label: "Actual", data: analyzed.map(l => l.actual) }] },
65
+ duration_ms: Date.now() - start,
66
+ };
67
+ }
68
+ function round(n) { return Math.round(n * 100) / 100; }
69
+ function fmt(n) { return "$" + Math.abs(n).toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); }
70
+ function esc(v) { return v.includes(",") ? `"${v}"` : v; }
@@ -0,0 +1,2 @@
1
+ import type { TaskInput, TaskOutput } from "./index.js";
2
+ export declare function handleCashFlow(task: TaskInput): Promise<TaskOutput>;
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handleCashFlow = handleCashFlow;
4
+ async function handleCashFlow(task) {
5
+ const start = Date.now();
6
+ const input = task.input_payload;
7
+ const period = input.period || "Current Period";
8
+ const beginBal = input.beginning_balance || 50000;
9
+ const opIn = input.operating_inflows || [
10
+ { description: "Customer Collections", amount: 125000, category: "Revenue" },
11
+ { description: "Interest Income", amount: 320, category: "Interest" },
12
+ { description: "Tax Refund", amount: 2800, category: "Tax" },
13
+ ];
14
+ const opOut = input.operating_outflows || [
15
+ { description: "Supplier Payments", amount: 45000, category: "COGS" },
16
+ { description: "Payroll", amount: 68500, category: "Payroll" },
17
+ { description: "Rent", amount: 8500, category: "Overhead" },
18
+ { description: "Utilities", amount: 1800, category: "Overhead" },
19
+ { description: "Insurance", amount: 3200, category: "Insurance" },
20
+ { description: "Marketing", amount: 12000, category: "Marketing" },
21
+ { description: "Income Tax Payments", amount: 8500, category: "Tax" },
22
+ ];
23
+ const investing = input.investing || [
24
+ { description: "Equipment Purchase", amount: -15000, category: "CapEx" },
25
+ { description: "Software License (3yr)", amount: -4500, category: "CapEx" },
26
+ ];
27
+ const financing = input.financing || [
28
+ { description: "Loan Repayment", amount: -5000, category: "Debt" },
29
+ { description: "Owner Distribution", amount: -10000, category: "Distribution" },
30
+ ];
31
+ const totalOpIn = round(opIn.reduce((s, i) => s + i.amount, 0));
32
+ const totalOpOut = round(opOut.reduce((s, i) => s + i.amount, 0));
33
+ const netOperating = round(totalOpIn - totalOpOut);
34
+ const netInvesting = round(investing.reduce((s, i) => s + i.amount, 0));
35
+ const netFinancing = round(financing.reduce((s, i) => s + i.amount, 0));
36
+ const netChange = round(netOperating + netInvesting + netFinancing);
37
+ const endingBal = round(beginBal + netChange);
38
+ const csv = [
39
+ "CASH FLOW STATEMENT",
40
+ `Period: ${period}`,
41
+ `Generated: ${new Date().toISOString().split("T")[0]}`,
42
+ "",
43
+ "OPERATING ACTIVITIES",
44
+ "Cash Inflows:",
45
+ "Description,Amount,Category",
46
+ ...opIn.map(i => `${esc(i.description)},${fmt(i.amount)},${i.category}`),
47
+ `Total Cash Inflows,${fmt(totalOpIn)},`,
48
+ "",
49
+ "Cash Outflows:",
50
+ "Description,Amount,Category",
51
+ ...opOut.map(i => `${esc(i.description)},${fmt(i.amount)},${i.category}`),
52
+ `Total Cash Outflows,${fmt(totalOpOut)},`,
53
+ "",
54
+ `Net Cash from Operations,${fmt(netOperating)},`,
55
+ "",
56
+ "INVESTING ACTIVITIES",
57
+ "Description,Amount,Category",
58
+ ...investing.map(i => `${esc(i.description)},${fmt(i.amount)},${i.category}`),
59
+ `Net Cash from Investing,${fmt(netInvesting)},`,
60
+ "",
61
+ "FINANCING ACTIVITIES",
62
+ "Description,Amount,Category",
63
+ ...financing.map(i => `${esc(i.description)},${fmt(i.amount)},${i.category}`),
64
+ `Net Cash from Financing,${fmt(netFinancing)},`,
65
+ "",
66
+ "CASH POSITION",
67
+ `Beginning Balance,${fmt(beginBal)}`,
68
+ `Net Change in Cash,${fmt(netChange)}`,
69
+ `Ending Balance,${fmt(endingBal)}`,
70
+ "",
71
+ "SUMMARY",
72
+ `Operating,${fmt(netOperating)},${netOperating >= 0 ? "Positive" : "Negative"}`,
73
+ `Investing,${fmt(netInvesting)},${netInvesting >= 0 ? "Positive" : "Negative"}`,
74
+ `Financing,${fmt(netFinancing)},${netFinancing >= 0 ? "Positive" : "Negative"}`,
75
+ `Net Change,${fmt(netChange)},${netChange >= 0 ? "Cash Increased" : "Cash Decreased"}`,
76
+ ];
77
+ return {
78
+ text: `Cash Flow: Operating ${fmt(netOperating)} | Investing ${fmt(netInvesting)} | Financing ${fmt(netFinancing)} | Net ${fmt(netChange)} | Ending ${fmt(endingBal)}`,
79
+ output_csv: csv.join("\n"),
80
+ summary: { beginning_balance: beginBal, net_operating: netOperating, net_investing: netInvesting, net_financing: netFinancing, net_change: netChange, ending_balance: endingBal },
81
+ chart_data: { chart_type: "bar", title: `Cash Flow — ${period}`, labels: ["Operating", "Investing", "Financing", "Net Change"], datasets: [{ label: "Cash Flow", data: [netOperating, netInvesting, netFinancing, netChange] }] },
82
+ duration_ms: Date.now() - start,
83
+ };
84
+ }
85
+ function round(n) { return Math.round(n * 100) / 100; }
86
+ function fmt(n) { return "$" + n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 }); }
87
+ function esc(v) { return v.includes(",") ? `"${v}"` : v; }
@@ -0,0 +1,5 @@
1
+ import type { TaskInput, TaskOutput } from "./index.js";
2
+ /**
3
+ * Chat handler — processes conversational messages AND triggers actions.
4
+ */
5
+ export declare function handleChat(task: TaskInput): Promise<TaskOutput>;