@elizaos/plugin-finances 2.0.3-beta.5 → 2.0.3-beta.7

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.
Files changed (103) hide show
  1. package/dist/actions/finances.d.ts +38 -0
  2. package/dist/actions/finances.d.ts.map +1 -0
  3. package/dist/actions/finances.js +368 -0
  4. package/dist/actions/finances.js.map +1 -0
  5. package/dist/components/finances/FinancesSpatialView.d.ts +80 -0
  6. package/dist/components/finances/FinancesSpatialView.d.ts.map +1 -0
  7. package/dist/components/finances/FinancesSpatialView.js +157 -0
  8. package/dist/components/finances/FinancesSpatialView.js.map +1 -0
  9. package/dist/components/finances/FinancesView.d.ts +97 -0
  10. package/dist/components/finances/FinancesView.d.ts.map +1 -0
  11. package/dist/components/finances/FinancesView.js +231 -0
  12. package/dist/components/finances/FinancesView.js.map +1 -0
  13. package/dist/components/finances/finances-view-bundle.d.ts +10 -0
  14. package/dist/components/finances/finances-view-bundle.d.ts.map +1 -0
  15. package/dist/components/finances/finances-view-bundle.js +5 -0
  16. package/dist/components/finances/finances-view-bundle.js.map +1 -0
  17. package/dist/db/finances-repository.d.ts +51 -0
  18. package/dist/db/finances-repository.d.ts.map +1 -0
  19. package/dist/db/finances-repository.js +521 -0
  20. package/dist/db/finances-repository.js.map +1 -0
  21. package/dist/db/index.d.ts +3 -0
  22. package/dist/db/index.d.ts.map +1 -0
  23. package/dist/db/index.js +6 -0
  24. package/dist/db/index.js.map +1 -0
  25. package/dist/db/schema.d.ts +2615 -0
  26. package/dist/db/schema.d.ts.map +1 -0
  27. package/dist/db/schema.js +133 -0
  28. package/dist/db/schema.js.map +1 -0
  29. package/dist/db/sql.d.ts +65 -0
  30. package/dist/db/sql.d.ts.map +1 -0
  31. package/dist/db/sql.js +182 -0
  32. package/dist/db/sql.js.map +1 -0
  33. package/dist/finance-normalize.d.ts +24 -0
  34. package/dist/finance-normalize.d.ts.map +1 -0
  35. package/dist/finance-normalize.js +66 -0
  36. package/dist/finance-normalize.js.map +1 -0
  37. package/dist/finances-service.d.ts +179 -0
  38. package/dist/finances-service.d.ts.map +1 -0
  39. package/dist/finances-service.js +1122 -0
  40. package/dist/finances-service.js.map +1 -0
  41. package/dist/index.d.ts +32 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +109 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/payment-csv-import.d.ts +23 -0
  46. package/dist/payment-csv-import.d.ts.map +1 -0
  47. package/dist/payment-csv-import.js +271 -0
  48. package/dist/payment-csv-import.js.map +1 -0
  49. package/dist/payment-recurrence.d.ts +14 -0
  50. package/dist/payment-recurrence.d.ts.map +1 -0
  51. package/dist/payment-recurrence.js +190 -0
  52. package/dist/payment-recurrence.js.map +1 -0
  53. package/dist/payment-types.d.ts +158 -0
  54. package/dist/payment-types.d.ts.map +1 -0
  55. package/dist/payment-types.js +1 -0
  56. package/dist/payment-types.js.map +1 -0
  57. package/dist/plugin.d.ts +15 -0
  58. package/dist/plugin.d.ts.map +1 -0
  59. package/dist/plugin.js +31 -0
  60. package/dist/plugin.js.map +1 -0
  61. package/dist/register-terminal-view.d.ts +15 -0
  62. package/dist/register-terminal-view.d.ts.map +1 -0
  63. package/dist/register-terminal-view.js +21 -0
  64. package/dist/register-terminal-view.js.map +1 -0
  65. package/dist/register.d.ts +9 -0
  66. package/dist/register.d.ts.map +1 -0
  67. package/dist/register.js +5 -0
  68. package/dist/register.js.map +1 -0
  69. package/dist/services/browser-bridge-seam.d.ts +40 -0
  70. package/dist/services/browser-bridge-seam.d.ts.map +1 -0
  71. package/dist/services/browser-bridge-seam.js +39 -0
  72. package/dist/services/browser-bridge-seam.js.map +1 -0
  73. package/dist/services/gmail-seam.d.ts +40 -0
  74. package/dist/services/gmail-seam.d.ts.map +1 -0
  75. package/dist/services/gmail-seam.js +208 -0
  76. package/dist/services/gmail-seam.js.map +1 -0
  77. package/dist/services/migration.d.ts +65 -0
  78. package/dist/services/migration.d.ts.map +1 -0
  79. package/dist/services/migration.js +116 -0
  80. package/dist/services/migration.js.map +1 -0
  81. package/dist/services/subscriptions-service.d.ts +76 -0
  82. package/dist/services/subscriptions-service.d.ts.map +1 -0
  83. package/dist/services/subscriptions-service.js +1002 -0
  84. package/dist/services/subscriptions-service.js.map +1 -0
  85. package/dist/subscriptions-playbooks.d.ts +79 -0
  86. package/dist/subscriptions-playbooks.d.ts.map +1 -0
  87. package/dist/subscriptions-playbooks.js +871 -0
  88. package/dist/subscriptions-playbooks.js.map +1 -0
  89. package/dist/subscriptions-types.d.ts +80 -0
  90. package/dist/subscriptions-types.d.ts.map +1 -0
  91. package/dist/subscriptions-types.js +1 -0
  92. package/dist/subscriptions-types.js.map +1 -0
  93. package/dist/token-encryption.d.ts +42 -0
  94. package/dist/token-encryption.d.ts.map +1 -0
  95. package/dist/token-encryption.js +96 -0
  96. package/dist/token-encryption.js.map +1 -0
  97. package/dist/types.d.ts +55 -0
  98. package/dist/types.d.ts.map +1 -0
  99. package/dist/types.js +18 -0
  100. package/dist/types.js.map +1 -0
  101. package/dist/views/bundle.js +411 -0
  102. package/dist/views/bundle.js.map +1 -0
  103. package/package.json +11 -11
@@ -0,0 +1,157 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { Button, Card, HStack, List, Text, VStack } from "@elizaos/ui/spatial";
3
+ const EMPTY_BALANCE = {
4
+ net: "",
5
+ negative: false,
6
+ income: "",
7
+ outflow: "",
8
+ asOf: ""
9
+ };
10
+ const EMPTY_FINANCES_SNAPSHOT = {
11
+ state: "loading",
12
+ balance: EMPTY_BALANCE,
13
+ transactions: [],
14
+ recurring: [],
15
+ note: ""
16
+ };
17
+ function FinancesSpatialView({
18
+ snapshot,
19
+ onAction
20
+ }) {
21
+ const dispatch = (action) => () => onAction?.(action);
22
+ return /* @__PURE__ */ jsx(Card, { gap: 1, padding: 1, children: snapshot.state === "loading" ? /* @__PURE__ */ jsx(Text, { tone: "muted", align: "center", style: "caption", children: "Loading" }) : snapshot.state === "error" ? /* @__PURE__ */ jsx(FinancesErrorBody, { snapshot, dispatch }) : snapshot.state === "empty" ? /* @__PURE__ */ jsx(FinancesEmptyBody, { dispatch }) : /* @__PURE__ */ jsx(FinancesReadyBody, { snapshot, dispatch }) });
23
+ }
24
+ function FinancesErrorBody({
25
+ snapshot,
26
+ dispatch
27
+ }) {
28
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
29
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "Could not load finances" }),
30
+ /* @__PURE__ */ jsx(Text, { tone: "danger", style: "caption", children: snapshot.error ?? "Could not load finances." }),
31
+ /* @__PURE__ */ jsx(HStack, { gap: 1, children: /* @__PURE__ */ jsx(Button, { agent: "retry", onPress: dispatch("retry"), children: "Retry" }) })
32
+ ] });
33
+ }
34
+ function FinancesEmptyBody({
35
+ dispatch
36
+ }) {
37
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
38
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "None" }),
39
+ /* @__PURE__ */ jsx(HStack, { gap: 1, children: /* @__PURE__ */ jsx(Button, { agent: "connect", onPress: dispatch("connect"), children: "Connect" }) })
40
+ ] });
41
+ }
42
+ function FinancesReadyBody({
43
+ snapshot,
44
+ dispatch
45
+ }) {
46
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
47
+ snapshot.note ? /* @__PURE__ */ jsx(Text, { tone: "warning", style: "caption", children: snapshot.note }) : null,
48
+ /* @__PURE__ */ jsx(BalanceSection, { balance: snapshot.balance }),
49
+ /* @__PURE__ */ jsx(
50
+ TransactionsSection,
51
+ {
52
+ transactions: snapshot.transactions,
53
+ dispatch
54
+ }
55
+ ),
56
+ /* @__PURE__ */ jsx(RecurringSection, { recurring: snapshot.recurring, dispatch })
57
+ ] });
58
+ }
59
+ function BalanceSection({ balance }) {
60
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
61
+ /* @__PURE__ */ jsx(Text, { style: "caption", tone: "muted", children: "Balance" }),
62
+ /* @__PURE__ */ jsx(Text, { bold: true, tone: balance.negative ? "danger" : "primary", wrap: false, children: balance.net }),
63
+ /* @__PURE__ */ jsxs(HStack, { gap: 1, width: "100%", children: [
64
+ /* @__PURE__ */ jsxs(Text, { style: "caption", tone: "muted", wrap: false, children: [
65
+ "In ",
66
+ balance.income
67
+ ] }),
68
+ /* @__PURE__ */ jsxs(Text, { style: "caption", tone: "muted", wrap: false, children: [
69
+ "Out ",
70
+ balance.outflow
71
+ ] })
72
+ ] }),
73
+ balance.asOf ? /* @__PURE__ */ jsxs(Text, { style: "caption", tone: "muted", wrap: false, children: [
74
+ "As of ",
75
+ balance.asOf
76
+ ] }) : null
77
+ ] });
78
+ }
79
+ function TransactionsSection({
80
+ transactions,
81
+ dispatch
82
+ }) {
83
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
84
+ /* @__PURE__ */ jsxs(Text, { style: "caption", tone: "muted", children: [
85
+ "Transactions (",
86
+ transactions.length,
87
+ ")"
88
+ ] }),
89
+ transactions.length === 0 ? /* @__PURE__ */ jsx(Text, { tone: "muted", style: "caption", children: "None" }) : /* @__PURE__ */ jsx(List, { gap: 0, children: transactions.map((tx) => /* @__PURE__ */ jsxs(
90
+ HStack,
91
+ {
92
+ gap: 1,
93
+ align: "center",
94
+ width: "100%",
95
+ agent: `txn-${tx.id}`,
96
+ children: [
97
+ /* @__PURE__ */ jsxs(VStack, { gap: 0, grow: 1, children: [
98
+ /* @__PURE__ */ jsx(Text, { bold: true, wrap: false, children: tx.description }),
99
+ /* @__PURE__ */ jsx(Text, { style: "caption", tone: "muted", wrap: false, children: tx.meta })
100
+ ] }),
101
+ /* @__PURE__ */ jsx(Text, { tone: tx.outflow ? "danger" : "primary", wrap: false, children: tx.amount }),
102
+ /* @__PURE__ */ jsx(
103
+ Button,
104
+ {
105
+ agent: `open-txn-${tx.id}`,
106
+ onPress: dispatch(`txn-${tx.id}`),
107
+ children: "\u203A"
108
+ }
109
+ )
110
+ ]
111
+ },
112
+ tx.id
113
+ )) })
114
+ ] });
115
+ }
116
+ function RecurringSection({
117
+ recurring,
118
+ dispatch
119
+ }) {
120
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
121
+ /* @__PURE__ */ jsxs(Text, { style: "caption", tone: "muted", children: [
122
+ "Recurring (",
123
+ recurring.length,
124
+ ")"
125
+ ] }),
126
+ recurring.length === 0 ? /* @__PURE__ */ jsx(Text, { tone: "muted", style: "caption", children: "None" }) : /* @__PURE__ */ jsx(List, { gap: 0, children: recurring.map((row) => /* @__PURE__ */ jsxs(
127
+ HStack,
128
+ {
129
+ gap: 1,
130
+ align: "center",
131
+ width: "100%",
132
+ agent: `bill-${row.id}`,
133
+ children: [
134
+ /* @__PURE__ */ jsxs(VStack, { gap: 0, grow: 1, children: [
135
+ /* @__PURE__ */ jsx(Text, { bold: true, wrap: false, children: row.label }),
136
+ /* @__PURE__ */ jsx(Text, { style: "caption", tone: "muted", wrap: false, children: row.meta })
137
+ ] }),
138
+ /* @__PURE__ */ jsx(Text, { wrap: false, children: row.amount }),
139
+ /* @__PURE__ */ jsx(
140
+ Button,
141
+ {
142
+ agent: `open-bill-${row.id}`,
143
+ onPress: dispatch(`bill-${row.id}`),
144
+ children: "\u203A"
145
+ }
146
+ )
147
+ ]
148
+ },
149
+ row.id
150
+ )) })
151
+ ] });
152
+ }
153
+ export {
154
+ EMPTY_FINANCES_SNAPSHOT,
155
+ FinancesSpatialView
156
+ };
157
+ //# sourceMappingURL=FinancesSpatialView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/finances/FinancesSpatialView.tsx"],"sourcesContent":["/**\n * FinancesSpatialView — the owner finance dashboard authored once with the\n * spatial vocabulary, so it renders correctly wherever it is displayed:\n *\n * - GUI / XR — mounted in `<SpatialSurface>` (DOM; XR scales up).\n * - TUI — rendered to real terminal lines by the agent terminal, via\n * `registerSpatialTerminalView` (see `register-terminal-view.tsx`).\n *\n * It is purely presentational (a snapshot + an action callback in, primitives\n * out) and imports only the cross-modality primitives, so it is safe to render\n * in the Node agent process where the terminal lives (no browser/client import).\n *\n * The balance, transactions, and recurring charges — including every currency\n * amount — arrive ALREADY FORMATTED as display strings from the data wrapper\n * ({@link ./FinancesView.tsx}); this component never fetches, computes a total,\n * or runs financial math. It displays the snapshot and dispatches actions.\n */\n\nimport { Button, Card, HStack, List, Text, VStack } from \"@elizaos/ui/spatial\";\n\n/** Which render state the dashboard is in. */\nexport type FinancesViewState = \"loading\" | \"error\" | \"empty\" | \"ready\";\n\n/** A balance summary row, already projected to display strings by the wrapper. */\nexport interface FinanceBalanceCard {\n /** Pre-formatted net balance (e.g. \"$2,765.50\"). */\n net: string;\n /** True when the net balance is below zero (drives tone, no math here). */\n negative: boolean;\n /** Pre-formatted money in over the window (e.g. \"$4,000.00\"). */\n income: string;\n /** Pre-formatted money out over the window (e.g. \"$1,234.50\"). */\n outflow: string;\n /** Pre-formatted \"as of\" date label, or empty. */\n asOf: string;\n}\n\n/** One transaction row, already projected to display strings by the wrapper. */\nexport interface FinanceTransactionCard {\n id: string;\n description: string;\n /** Pre-formatted secondary line (date + optional category). */\n meta: string;\n /** Pre-formatted signed amount (e.g. \"-$42.50\"). */\n amount: string;\n /** True when the amount is an outflow (drives tone, no math here). */\n outflow: boolean;\n}\n\n/** One recurring-charge row, already projected to display strings. */\nexport interface FinanceRecurringCard {\n id: string;\n label: string;\n /** Pre-formatted secondary line (cadence + next-charge date). */\n meta: string;\n /** Pre-formatted amount (e.g. \"$15.99\"). */\n amount: string;\n}\n\nexport interface FinancesSnapshot {\n /** The dashboard state machine. */\n state: FinancesViewState;\n /** Balance summary (only meaningful when state === \"ready\"). */\n balance: FinanceBalanceCard;\n /** Recent transactions (only meaningful when state === \"ready\"). */\n transactions: FinanceTransactionCard[];\n /** Recurring charges (only meaningful when state === \"ready\"). */\n recurring: FinanceRecurringCard[];\n /** One quiet proactive line, or empty when there is no genuine signal. */\n note: string;\n /** Error message when state === \"error\". */\n error?: string;\n}\n\nconst EMPTY_BALANCE: FinanceBalanceCard = {\n net: \"\",\n negative: false,\n income: \"\",\n outflow: \"\",\n asOf: \"\",\n};\n\nexport const EMPTY_FINANCES_SNAPSHOT: FinancesSnapshot = {\n state: \"loading\",\n balance: EMPTY_BALANCE,\n transactions: [],\n recurring: [],\n note: \"\",\n};\n\nexport interface FinancesSpatialViewProps {\n snapshot: FinancesSnapshot;\n /**\n * Dispatch by agent id:\n * `retry` reload after an error,\n * `connect` route a connect-a-source request to chat,\n * `txn-<id>` open a transaction,\n * `bill-<id>` open a recurring charge.\n */\n onAction?: (action: string) => void;\n}\n\nexport function FinancesSpatialView({\n snapshot,\n onAction,\n}: FinancesSpatialViewProps) {\n const dispatch = (action: string) => () => onAction?.(action);\n\n return (\n <Card gap={1} padding={1}>\n {snapshot.state === \"loading\" ? (\n <Text tone=\"muted\" align=\"center\" style=\"caption\">\n Loading\n </Text>\n ) : snapshot.state === \"error\" ? (\n <FinancesErrorBody snapshot={snapshot} dispatch={dispatch} />\n ) : snapshot.state === \"empty\" ? (\n <FinancesEmptyBody dispatch={dispatch} />\n ) : (\n <FinancesReadyBody snapshot={snapshot} dispatch={dispatch} />\n )}\n </Card>\n );\n}\n\nfunction FinancesErrorBody({\n snapshot,\n dispatch,\n}: {\n snapshot: FinancesSnapshot;\n dispatch: (action: string) => () => void;\n}) {\n return (\n <>\n <Text bold>Could not load finances</Text>\n <Text tone=\"danger\" style=\"caption\">\n {snapshot.error ?? \"Could not load finances.\"}\n </Text>\n <HStack gap={1}>\n <Button agent=\"retry\" onPress={dispatch(\"retry\")}>\n Retry\n </Button>\n </HStack>\n </>\n );\n}\n\nfunction FinancesEmptyBody({\n dispatch,\n}: {\n dispatch: (action: string) => () => void;\n}) {\n return (\n <>\n <Text bold>None</Text>\n <HStack gap={1}>\n <Button agent=\"connect\" onPress={dispatch(\"connect\")}>\n Connect\n </Button>\n </HStack>\n </>\n );\n}\n\nfunction FinancesReadyBody({\n snapshot,\n dispatch,\n}: {\n snapshot: FinancesSnapshot;\n dispatch: (action: string) => () => void;\n}) {\n return (\n <>\n {snapshot.note ? (\n <Text tone=\"warning\" style=\"caption\">\n {snapshot.note}\n </Text>\n ) : null}\n <BalanceSection balance={snapshot.balance} />\n <TransactionsSection\n transactions={snapshot.transactions}\n dispatch={dispatch}\n />\n <RecurringSection recurring={snapshot.recurring} dispatch={dispatch} />\n </>\n );\n}\n\nfunction BalanceSection({ balance }: { balance: FinanceBalanceCard }) {\n return (\n <>\n <Text style=\"caption\" tone=\"muted\">\n Balance\n </Text>\n <Text bold tone={balance.negative ? \"danger\" : \"primary\"} wrap={false}>\n {balance.net}\n </Text>\n <HStack gap={1} width=\"100%\">\n <Text style=\"caption\" tone=\"muted\" wrap={false}>\n In {balance.income}\n </Text>\n <Text style=\"caption\" tone=\"muted\" wrap={false}>\n Out {balance.outflow}\n </Text>\n </HStack>\n {balance.asOf ? (\n <Text style=\"caption\" tone=\"muted\" wrap={false}>\n As of {balance.asOf}\n </Text>\n ) : null}\n </>\n );\n}\n\nfunction TransactionsSection({\n transactions,\n dispatch,\n}: {\n transactions: FinanceTransactionCard[];\n dispatch: (action: string) => () => void;\n}) {\n return (\n <>\n <Text style=\"caption\" tone=\"muted\">\n Transactions ({transactions.length})\n </Text>\n {transactions.length === 0 ? (\n <Text tone=\"muted\" style=\"caption\">\n None\n </Text>\n ) : (\n <List gap={0}>\n {transactions.map((tx) => (\n <HStack\n key={tx.id}\n gap={1}\n align=\"center\"\n width=\"100%\"\n agent={`txn-${tx.id}`}\n >\n <VStack gap={0} grow={1}>\n <Text bold wrap={false}>\n {tx.description}\n </Text>\n <Text style=\"caption\" tone=\"muted\" wrap={false}>\n {tx.meta}\n </Text>\n </VStack>\n <Text tone={tx.outflow ? \"danger\" : \"primary\"} wrap={false}>\n {tx.amount}\n </Text>\n <Button\n agent={`open-txn-${tx.id}`}\n onPress={dispatch(`txn-${tx.id}`)}\n >\n ›\n </Button>\n </HStack>\n ))}\n </List>\n )}\n </>\n );\n}\n\nfunction RecurringSection({\n recurring,\n dispatch,\n}: {\n recurring: FinanceRecurringCard[];\n dispatch: (action: string) => () => void;\n}) {\n return (\n <>\n <Text style=\"caption\" tone=\"muted\">\n Recurring ({recurring.length})\n </Text>\n {recurring.length === 0 ? (\n <Text tone=\"muted\" style=\"caption\">\n None\n </Text>\n ) : (\n <List gap={0}>\n {recurring.map((row) => (\n <HStack\n key={row.id}\n gap={1}\n align=\"center\"\n width=\"100%\"\n agent={`bill-${row.id}`}\n >\n <VStack gap={0} grow={1}>\n <Text bold wrap={false}>\n {row.label}\n </Text>\n <Text style=\"caption\" tone=\"muted\" wrap={false}>\n {row.meta}\n </Text>\n </VStack>\n <Text wrap={false}>{row.amount}</Text>\n <Button\n agent={`open-bill-${row.id}`}\n onPress={dispatch(`bill-${row.id}`)}\n >\n ›\n </Button>\n </HStack>\n ))}\n </List>\n )}\n </>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * FinancesView — the single GUI/XR data wrapper for the owner finance dashboard.
3
+ *
4
+ * It owns the live money data (the fetcher seams over the four read-only
5
+ * endpoints PA serves, the quiet background poll, wire->display mapping, the
6
+ * USD-float->minor-units boundary, and the proactive signal) and renders the one
7
+ * presentational {@link FinancesSpatialView} inside a {@link SpatialSurface}.
8
+ * Omitting the `modality` prop lets `SpatialSurface` auto-detect GUI vs XR, so
9
+ * the SAME component serves both surfaces; the TUI surface renders the same
10
+ * `FinancesSpatialView` through the terminal registry (see
11
+ * `../../register-terminal-view.tsx`).
12
+ *
13
+ * Data sources (PA owns the persistence; this plugin only reads):
14
+ * GET {base}/api/lifeops/money/dashboard (balance summary)
15
+ * GET {base}/api/lifeops/money/sources (connected-vs-disconnected)
16
+ * GET {base}/api/lifeops/money/transactions (recent transactions)
17
+ * GET {base}/api/lifeops/money/recurring (recurring charges)
18
+ *
19
+ * The client DISPLAYS, never COMPUTES: every total, sign, and currency amount is
20
+ * resolved HERE into a pre-formatted string and handed to the spatial view as a
21
+ * snapshot. The owner actions are `connect` (route a connect-a-source request
22
+ * through the assistant chat — no fabricated balances) and `retry` (reload after
23
+ * an error). This plugin MUST NOT import from @elizaos/plugin-personal-assistant;
24
+ * the wire DTOs below are declared locally to match the JSON shape PA emits.
25
+ */
26
+ import type { ReactNode } from "react";
27
+ interface MoneySpendingWire {
28
+ windowDays: number;
29
+ fromDate: string;
30
+ toDate: string;
31
+ totalSpendUsd: number;
32
+ totalIncomeUsd: number;
33
+ netUsd: number;
34
+ transactionCount: number;
35
+ }
36
+ interface MoneyDashboardWire {
37
+ spending: MoneySpendingWire;
38
+ generatedAt: string;
39
+ }
40
+ type MoneySourceStatusWire = "active" | "disconnected" | "needs_attention";
41
+ interface MoneySourceWire {
42
+ id: string;
43
+ kind: string;
44
+ label: string;
45
+ institution: string | null;
46
+ status: MoneySourceStatusWire;
47
+ }
48
+ interface MoneySourcesWire {
49
+ sources: MoneySourceWire[];
50
+ }
51
+ type MoneyDirectionWire = "debit" | "credit";
52
+ interface MoneyTransactionWire {
53
+ id: string;
54
+ postedAt: string;
55
+ amountUsd: number;
56
+ direction: MoneyDirectionWire;
57
+ merchantDisplay?: string | null;
58
+ merchantNormalized: string;
59
+ merchantRaw: string;
60
+ description: string | null;
61
+ category: string | null;
62
+ currency: string;
63
+ }
64
+ interface MoneyTransactionsWire {
65
+ transactions: MoneyTransactionWire[];
66
+ }
67
+ interface MoneyRecurringWire {
68
+ merchantNormalized: string;
69
+ merchantDisplay: string;
70
+ cadence: string;
71
+ averageAmountUsd: number;
72
+ nextExpectedAt: string | null;
73
+ category: string | null;
74
+ }
75
+ interface MoneyRecurringChargesWire {
76
+ charges: MoneyRecurringWire[];
77
+ }
78
+ export interface FinancesFetchers {
79
+ fetchDashboard: () => Promise<MoneyDashboardWire>;
80
+ fetchSources: () => Promise<MoneySourcesWire>;
81
+ fetchTransactions: () => Promise<MoneyTransactionsWire>;
82
+ fetchRecurring: () => Promise<MoneyRecurringChargesWire>;
83
+ }
84
+ export interface FinancesViewProps {
85
+ /** Owner display name (host injection seam). */
86
+ ownerName?: string;
87
+ /** Test/host injection seam. Defaults to real `/api/lifeops/money/*` GETs. */
88
+ fetchers?: FinancesFetchers;
89
+ }
90
+ /**
91
+ * Load-bearing render boundary: minor units (cents) -> grouped currency string.
92
+ * Kept here (not in a util) because format-minor.test.ts pins it to this file.
93
+ */
94
+ export declare function formatMinor(amountMinor: number, currency: string): string;
95
+ export declare function FinancesView(props?: FinancesViewProps): ReactNode;
96
+ export default FinancesView;
97
+ //# sourceMappingURL=FinancesView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FinancesView.d.ts","sourceRoot":"","sources":["../../../src/components/finances/FinancesView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAqBvC,UAAU,iBAAiB;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,UAAU,kBAAkB;IAC1B,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,KAAK,qBAAqB,GAAG,QAAQ,GAAG,cAAc,GAAG,iBAAiB,CAAC;AAE3E,UAAU,eAAe;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,qBAAqB,CAAC;CAC/B;AAED,UAAU,gBAAgB;IACxB,OAAO,EAAE,eAAe,EAAE,CAAC;CAC5B;AAED,KAAK,kBAAkB,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE7C,UAAU,oBAAoB;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,kBAAkB,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,qBAAqB;IAC7B,YAAY,EAAE,oBAAoB,EAAE,CAAC;CACtC;AAED,UAAU,kBAAkB;IAC1B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,UAAU,yBAAyB;IACjC,OAAO,EAAE,kBAAkB,EAAE,CAAC;CAC/B;AAMD,MAAM,WAAW,gBAAgB;IAC/B,cAAc,EAAE,MAAM,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAClD,YAAY,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC9C,iBAAiB,EAAE,MAAM,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACxD,cAAc,EAAE,MAAM,OAAO,CAAC,yBAAyB,CAAC,CAAC;CAC1D;AAoBD,MAAM,WAAW,iBAAiB;IAChC,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,gBAAgB,CAAC;CAC7B;AA2ED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAUzE;AAuGD,wBAAgB,YAAY,CAAC,KAAK,GAAE,iBAAsB,GAAG,SAAS,CAwGrE;AAED,eAAe,YAAY,CAAC"}
@@ -0,0 +1,231 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { client } from "@elizaos/ui";
3
+ import { SpatialSurface } from "@elizaos/ui/spatial";
4
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
5
+ import {
6
+ EMPTY_FINANCES_SNAPSHOT,
7
+ FinancesSpatialView
8
+ } from "./FinancesSpatialView.js";
9
+ async function getJson(path) {
10
+ const response = await fetch(`${client.getBaseUrl()}${path}`);
11
+ if (!response.ok) {
12
+ throw new Error(`Money request failed (${response.status}): ${path}`);
13
+ }
14
+ return await response.json();
15
+ }
16
+ const defaultFetchers = {
17
+ fetchDashboard: () => getJson("/api/lifeops/money/dashboard"),
18
+ fetchSources: () => getJson("/api/lifeops/money/sources"),
19
+ fetchTransactions: () => getJson("/api/lifeops/money/transactions"),
20
+ fetchRecurring: () => getJson("/api/lifeops/money/recurring")
21
+ };
22
+ const USD = "USD";
23
+ function usdToMinor(amountUsd) {
24
+ return Math.round(amountUsd * 100);
25
+ }
26
+ function mapBalance(dashboard) {
27
+ const { spending } = dashboard;
28
+ return {
29
+ netBalanceMinor: usdToMinor(spending.netUsd),
30
+ currency: USD,
31
+ monthlyIncomeMinor: usdToMinor(spending.totalIncomeUsd),
32
+ monthlyOutflowMinor: usdToMinor(spending.totalSpendUsd),
33
+ asOf: dashboard.generatedAt
34
+ };
35
+ }
36
+ function mapTransaction(tx) {
37
+ const signedUsd = tx.direction === "debit" ? -tx.amountUsd : tx.amountUsd;
38
+ const description = tx.description ?? tx.merchantDisplay ?? tx.merchantNormalized ?? "Transaction";
39
+ return {
40
+ id: tx.id,
41
+ occurredAt: tx.postedAt,
42
+ amountMinor: usdToMinor(signedUsd),
43
+ currency: tx.currency || USD,
44
+ description,
45
+ category: tx.category,
46
+ merchant: tx.merchantDisplay ?? tx.merchantNormalized ?? null,
47
+ status: "posted",
48
+ source: null
49
+ };
50
+ }
51
+ const RECURRING_CADENCES = /* @__PURE__ */ new Set([
52
+ "daily",
53
+ "weekly",
54
+ "monthly",
55
+ "quarterly",
56
+ "yearly"
57
+ ]);
58
+ function mapRecurring(charge) {
59
+ const normalized = charge.cadence === "annual" ? "yearly" : RECURRING_CADENCES.has(charge.cadence) ? charge.cadence : "monthly";
60
+ return {
61
+ id: charge.merchantNormalized,
62
+ label: charge.merchantDisplay || charge.merchantNormalized,
63
+ amountMinor: usdToMinor(charge.averageAmountUsd),
64
+ currency: USD,
65
+ cadence: normalized,
66
+ nextChargeAt: charge.nextExpectedAt,
67
+ merchant: charge.merchantDisplay || charge.merchantNormalized,
68
+ active: true
69
+ };
70
+ }
71
+ function formatMinor(amountMinor, currency) {
72
+ const value = amountMinor / 100;
73
+ try {
74
+ return new Intl.NumberFormat(void 0, {
75
+ style: "currency",
76
+ currency
77
+ }).format(value);
78
+ } catch {
79
+ return `${value.toFixed(2)} ${currency}`;
80
+ }
81
+ }
82
+ function formatDate(value) {
83
+ if (!value) return "";
84
+ const date = new Date(value);
85
+ return Number.isNaN(date.getTime()) ? value : date.toLocaleDateString();
86
+ }
87
+ function proactiveNote(balance, recurring, now = Date.now()) {
88
+ if (balance.netBalanceMinor < 0) {
89
+ return `Balance is negative (${formatMinor(
90
+ balance.netBalanceMinor,
91
+ balance.currency
92
+ )}).`;
93
+ }
94
+ const weekFromNow = now + 7 * 24 * 60 * 60 * 1e3;
95
+ const dueSoon = recurring.filter((row) => {
96
+ if (!row.nextChargeAt) return false;
97
+ const due = new Date(row.nextChargeAt).getTime();
98
+ return !Number.isNaN(due) && due >= now && due <= weekFromNow;
99
+ }).length;
100
+ if (dueSoon > 0) {
101
+ return `${dueSoon} bill${dueSoon === 1 ? "" : "s"} due this week.`;
102
+ }
103
+ return "";
104
+ }
105
+ function toBalanceCard(balance) {
106
+ return {
107
+ net: formatMinor(balance.netBalanceMinor, balance.currency),
108
+ negative: balance.netBalanceMinor < 0,
109
+ income: formatMinor(balance.monthlyIncomeMinor, balance.currency),
110
+ outflow: formatMinor(balance.monthlyOutflowMinor, balance.currency),
111
+ asOf: formatDate(balance.asOf)
112
+ };
113
+ }
114
+ function toTransactionCard(tx) {
115
+ const date = formatDate(tx.occurredAt);
116
+ const meta = tx.category ? `${date} \u2022 ${tx.category}` : date;
117
+ return {
118
+ id: tx.id,
119
+ description: tx.description,
120
+ meta,
121
+ amount: formatMinor(tx.amountMinor, tx.currency),
122
+ outflow: tx.amountMinor < 0
123
+ };
124
+ }
125
+ function toRecurringCard(row) {
126
+ const next = formatDate(row.nextChargeAt);
127
+ const meta = next ? `${row.cadence} \u2022 next ${next}` : row.cadence;
128
+ return {
129
+ id: row.id,
130
+ label: row.label,
131
+ meta,
132
+ amount: formatMinor(row.amountMinor, row.currency)
133
+ };
134
+ }
135
+ const POLL_INTERVAL_MS = 3e4;
136
+ function requestConnectSource() {
137
+ const send = client.sendChatMessage;
138
+ send?.("Connect a payment source so you can track my money.");
139
+ }
140
+ function FinancesView(props = {}) {
141
+ const fetchers = props.fetchers ?? defaultFetchers;
142
+ const [state, setState] = useState({ kind: "loading" });
143
+ const fetchersRef = useRef(fetchers);
144
+ fetchersRef.current = fetchers;
145
+ const load = useCallback((quiet = false) => {
146
+ let cancelled = false;
147
+ if (!quiet) setState({ kind: "loading" });
148
+ Promise.all([
149
+ fetchersRef.current.fetchDashboard(),
150
+ fetchersRef.current.fetchSources(),
151
+ fetchersRef.current.fetchTransactions(),
152
+ fetchersRef.current.fetchRecurring()
153
+ ]).then(([dashboard, sources, transactions, recurring]) => {
154
+ if (cancelled) return;
155
+ const connected = sources.sources.some(
156
+ (source) => source.status !== "disconnected"
157
+ );
158
+ setState({
159
+ kind: "ready",
160
+ data: {
161
+ hasSource: connected,
162
+ balance: mapBalance(dashboard),
163
+ transactions: transactions.transactions.map(mapTransaction),
164
+ recurring: recurring.charges.map(mapRecurring)
165
+ }
166
+ });
167
+ }).catch((error) => {
168
+ if (cancelled || quiet) return;
169
+ setState({
170
+ kind: "error",
171
+ message: error instanceof Error ? error.message : "Could not load finances."
172
+ });
173
+ });
174
+ return () => {
175
+ cancelled = true;
176
+ };
177
+ }, []);
178
+ useEffect(() => load(), [load]);
179
+ useEffect(() => {
180
+ const id = setInterval(() => load(true), POLL_INTERVAL_MS);
181
+ return () => clearInterval(id);
182
+ }, [load]);
183
+ const snapshot = useMemo(() => {
184
+ if (state.kind === "loading") {
185
+ return EMPTY_FINANCES_SNAPSHOT;
186
+ }
187
+ if (state.kind === "error") {
188
+ return {
189
+ ...EMPTY_FINANCES_SNAPSHOT,
190
+ state: "error",
191
+ error: state.message
192
+ };
193
+ }
194
+ const { hasSource, balance, transactions, recurring } = state.data;
195
+ if (!hasSource) {
196
+ return { ...EMPTY_FINANCES_SNAPSHOT, state: "empty" };
197
+ }
198
+ return {
199
+ state: "ready",
200
+ balance: toBalanceCard(balance),
201
+ transactions: transactions.map(toTransactionCard),
202
+ recurring: recurring.map(toRecurringCard),
203
+ note: proactiveNote(balance, recurring)
204
+ };
205
+ }, [state]);
206
+ const onAction = useCallback(
207
+ (action) => {
208
+ if (action === "retry") {
209
+ load();
210
+ return;
211
+ }
212
+ if (action === "connect") {
213
+ requestConnectSource();
214
+ return;
215
+ }
216
+ if (action.startsWith("txn-") || action.startsWith("bill-")) {
217
+ const send = client.sendChatMessage;
218
+ send?.(`Show me the details for ${action}.`);
219
+ }
220
+ },
221
+ [load]
222
+ );
223
+ return /* @__PURE__ */ jsx(SpatialSurface, { children: /* @__PURE__ */ jsx(FinancesSpatialView, { snapshot, onAction }) });
224
+ }
225
+ var FinancesView_default = FinancesView;
226
+ export {
227
+ FinancesView,
228
+ FinancesView_default as default,
229
+ formatMinor
230
+ };
231
+ //# sourceMappingURL=FinancesView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/finances/FinancesView.tsx"],"sourcesContent":["/**\n * FinancesView — the single GUI/XR data wrapper for the owner finance dashboard.\n *\n * It owns the live money data (the fetcher seams over the four read-only\n * endpoints PA serves, the quiet background poll, wire->display mapping, the\n * USD-float->minor-units boundary, and the proactive signal) and renders the one\n * presentational {@link FinancesSpatialView} inside a {@link SpatialSurface}.\n * Omitting the `modality` prop lets `SpatialSurface` auto-detect GUI vs XR, so\n * the SAME component serves both surfaces; the TUI surface renders the same\n * `FinancesSpatialView` through the terminal registry (see\n * `../../register-terminal-view.tsx`).\n *\n * Data sources (PA owns the persistence; this plugin only reads):\n * GET {base}/api/lifeops/money/dashboard (balance summary)\n * GET {base}/api/lifeops/money/sources (connected-vs-disconnected)\n * GET {base}/api/lifeops/money/transactions (recent transactions)\n * GET {base}/api/lifeops/money/recurring (recurring charges)\n *\n * The client DISPLAYS, never COMPUTES: every total, sign, and currency amount is\n * resolved HERE into a pre-formatted string and handed to the spatial view as a\n * snapshot. The owner actions are `connect` (route a connect-a-source request\n * through the assistant chat — no fabricated balances) and `retry` (reload after\n * an error). This plugin MUST NOT import from @elizaos/plugin-personal-assistant;\n * the wire DTOs below are declared locally to match the JSON shape PA emits.\n */\n\nimport { client } from \"@elizaos/ui\";\nimport { SpatialSurface } from \"@elizaos/ui/spatial\";\nimport type { ReactNode } from \"react\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type {\n FinanceBalanceSummaryDTO,\n FinanceTransactionDTO,\n RecurringChargeDTO,\n} from \"../../types.js\";\nimport {\n EMPTY_FINANCES_SNAPSHOT,\n type FinanceBalanceCard,\n type FinanceRecurringCard,\n type FinancesSnapshot,\n FinancesSpatialView,\n type FinanceTransactionCard,\n} from \"./FinancesSpatialView.js\";\n\n// ---------------------------------------------------------------------------\n// Wire DTOs — local mirror of the JSON shape served by the PA money routes.\n// Amounts are USD floats on the wire; never import PA types here.\n// ---------------------------------------------------------------------------\n\ninterface MoneySpendingWire {\n windowDays: number;\n fromDate: string;\n toDate: string;\n totalSpendUsd: number;\n totalIncomeUsd: number;\n netUsd: number;\n transactionCount: number;\n}\n\ninterface MoneyDashboardWire {\n spending: MoneySpendingWire;\n generatedAt: string;\n}\n\ntype MoneySourceStatusWire = \"active\" | \"disconnected\" | \"needs_attention\";\n\ninterface MoneySourceWire {\n id: string;\n kind: string;\n label: string;\n institution: string | null;\n status: MoneySourceStatusWire;\n}\n\ninterface MoneySourcesWire {\n sources: MoneySourceWire[];\n}\n\ntype MoneyDirectionWire = \"debit\" | \"credit\";\n\ninterface MoneyTransactionWire {\n id: string;\n postedAt: string;\n amountUsd: number;\n direction: MoneyDirectionWire;\n merchantDisplay?: string | null;\n merchantNormalized: string;\n merchantRaw: string;\n description: string | null;\n category: string | null;\n currency: string;\n}\n\ninterface MoneyTransactionsWire {\n transactions: MoneyTransactionWire[];\n}\n\ninterface MoneyRecurringWire {\n merchantNormalized: string;\n merchantDisplay: string;\n cadence: string;\n averageAmountUsd: number;\n nextExpectedAt: string | null;\n category: string | null;\n}\n\ninterface MoneyRecurringChargesWire {\n charges: MoneyRecurringWire[];\n}\n\n// ---------------------------------------------------------------------------\n// Fetcher seams — default to real GETs; tests inject offline fakes.\n// ---------------------------------------------------------------------------\n\nexport interface FinancesFetchers {\n fetchDashboard: () => Promise<MoneyDashboardWire>;\n fetchSources: () => Promise<MoneySourcesWire>;\n fetchTransactions: () => Promise<MoneyTransactionsWire>;\n fetchRecurring: () => Promise<MoneyRecurringChargesWire>;\n}\n\nasync function getJson<T>(path: string): Promise<T> {\n const response = await fetch(`${client.getBaseUrl()}${path}`);\n if (!response.ok) {\n throw new Error(`Money request failed (${response.status}): ${path}`);\n }\n return (await response.json()) as T;\n}\n\nconst defaultFetchers: FinancesFetchers = {\n fetchDashboard: () =>\n getJson<MoneyDashboardWire>(\"/api/lifeops/money/dashboard\"),\n fetchSources: () => getJson<MoneySourcesWire>(\"/api/lifeops/money/sources\"),\n fetchTransactions: () =>\n getJson<MoneyTransactionsWire>(\"/api/lifeops/money/transactions\"),\n fetchRecurring: () =>\n getJson<MoneyRecurringChargesWire>(\"/api/lifeops/money/recurring\"),\n};\n\nexport interface FinancesViewProps {\n /** Owner display name (host injection seam). */\n ownerName?: string;\n /** Test/host injection seam. Defaults to real `/api/lifeops/money/*` GETs. */\n fetchers?: FinancesFetchers;\n}\n\n// ---------------------------------------------------------------------------\n// Wire -> display DTO mapping (USD float -> minor units at the boundary).\n// ---------------------------------------------------------------------------\n\nconst USD = \"USD\";\n\nfunction usdToMinor(amountUsd: number): number {\n return Math.round(amountUsd * 100);\n}\n\nfunction mapBalance(dashboard: MoneyDashboardWire): FinanceBalanceSummaryDTO {\n const { spending } = dashboard;\n return {\n netBalanceMinor: usdToMinor(spending.netUsd),\n currency: USD,\n monthlyIncomeMinor: usdToMinor(spending.totalIncomeUsd),\n monthlyOutflowMinor: usdToMinor(spending.totalSpendUsd),\n asOf: dashboard.generatedAt,\n };\n}\n\nfunction mapTransaction(tx: MoneyTransactionWire): FinanceTransactionDTO {\n // A debit is money leaving the account: render as a negative (outflow). The\n // wire amount is unsigned, so the direction carries the sign.\n const signedUsd = tx.direction === \"debit\" ? -tx.amountUsd : tx.amountUsd;\n const description =\n tx.description ??\n tx.merchantDisplay ??\n tx.merchantNormalized ??\n \"Transaction\";\n return {\n id: tx.id,\n occurredAt: tx.postedAt,\n amountMinor: usdToMinor(signedUsd),\n currency: tx.currency || USD,\n description,\n category: tx.category,\n merchant: tx.merchantDisplay ?? tx.merchantNormalized ?? null,\n status: \"posted\",\n source: null,\n };\n}\n\nconst RECURRING_CADENCES = new Set([\n \"daily\",\n \"weekly\",\n \"monthly\",\n \"quarterly\",\n \"yearly\",\n]);\n\nfunction mapRecurring(charge: MoneyRecurringWire): RecurringChargeDTO {\n // The wire cadence has more variants (biweekly/annual/irregular) than the\n // display enum; normalize annual -> yearly and fall back to monthly for the\n // ones the display enum cannot represent. Display only — no math.\n const normalized =\n charge.cadence === \"annual\"\n ? \"yearly\"\n : RECURRING_CADENCES.has(charge.cadence)\n ? charge.cadence\n : \"monthly\";\n return {\n id: charge.merchantNormalized,\n label: charge.merchantDisplay || charge.merchantNormalized,\n amountMinor: usdToMinor(charge.averageAmountUsd),\n currency: USD,\n cadence: normalized as RecurringChargeDTO[\"cadence\"],\n nextChargeAt: charge.nextExpectedAt,\n merchant: charge.merchantDisplay || charge.merchantNormalized,\n active: true,\n };\n}\n\n/**\n * Load-bearing render boundary: minor units (cents) -> grouped currency string.\n * Kept here (not in a util) because format-minor.test.ts pins it to this file.\n */\nexport function formatMinor(amountMinor: number, currency: string): string {\n const value = amountMinor / 100;\n try {\n return new Intl.NumberFormat(undefined, {\n style: \"currency\",\n currency,\n }).format(value);\n } catch {\n return `${value.toFixed(2)} ${currency}`;\n }\n}\n\nfunction formatDate(value: string | null): string {\n if (!value) return \"\";\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? value : date.toLocaleDateString();\n}\n\n/**\n * One quiet line of proactive agent context (design law 10): surface a single\n * genuine, actionable money signal — never a placeholder. Precedence:\n * 1. a negative net balance (overdrawn), then\n * 2. recurring bills landing within the next 7 days.\n * Returns \"\" when neither holds, so the line renders nothing on no signal.\n * Computed entirely from data the view already loads; no new imports.\n */\nfunction proactiveNote(\n balance: FinanceBalanceSummaryDTO,\n recurring: RecurringChargeDTO[],\n now: number = Date.now(),\n): string {\n if (balance.netBalanceMinor < 0) {\n return `Balance is negative (${formatMinor(\n balance.netBalanceMinor,\n balance.currency,\n )}).`;\n }\n const weekFromNow = now + 7 * 24 * 60 * 60 * 1000;\n const dueSoon = recurring.filter((row) => {\n if (!row.nextChargeAt) return false;\n const due = new Date(row.nextChargeAt).getTime();\n return !Number.isNaN(due) && due >= now && due <= weekFromNow;\n }).length;\n if (dueSoon > 0) {\n return `${dueSoon} bill${dueSoon === 1 ? \"\" : \"s\"} due this week.`;\n }\n return \"\";\n}\n\n// ---------------------------------------------------------------------------\n// Display-DTO -> spatial-card projection (pre-format every string HERE).\n// ---------------------------------------------------------------------------\n\nfunction toBalanceCard(balance: FinanceBalanceSummaryDTO): FinanceBalanceCard {\n return {\n net: formatMinor(balance.netBalanceMinor, balance.currency),\n negative: balance.netBalanceMinor < 0,\n income: formatMinor(balance.monthlyIncomeMinor, balance.currency),\n outflow: formatMinor(balance.monthlyOutflowMinor, balance.currency),\n asOf: formatDate(balance.asOf),\n };\n}\n\nfunction toTransactionCard(tx: FinanceTransactionDTO): FinanceTransactionCard {\n const date = formatDate(tx.occurredAt);\n const meta = tx.category ? `${date} • ${tx.category}` : date;\n return {\n id: tx.id,\n description: tx.description,\n meta,\n amount: formatMinor(tx.amountMinor, tx.currency),\n outflow: tx.amountMinor < 0,\n };\n}\n\nfunction toRecurringCard(row: RecurringChargeDTO): FinanceRecurringCard {\n const next = formatDate(row.nextChargeAt);\n const meta = next ? `${row.cadence} • next ${next}` : row.cadence;\n return {\n id: row.id,\n label: row.label,\n meta,\n amount: formatMinor(row.amountMinor, row.currency),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Fetch-driven state machine.\n// ---------------------------------------------------------------------------\n\nconst POLL_INTERVAL_MS = 30_000;\n\ninterface FinancesData {\n hasSource: boolean;\n balance: FinanceBalanceSummaryDTO;\n transactions: FinanceTransactionDTO[];\n recurring: RecurringChargeDTO[];\n}\n\ntype LoadState =\n | { kind: \"loading\" }\n | { kind: \"error\"; message: string }\n | { kind: \"ready\"; data: FinancesData };\n\nfunction requestConnectSource(): void {\n // The connect-a-source affordance routes through the assistant chat. `client`\n // does not type `sendChatMessage`, so read it through a narrow optional-method\n // view and call it only when present — no fabricated balances.\n const send = (client as { sendChatMessage?: (text: string) => void })\n .sendChatMessage;\n send?.(\"Connect a payment source so you can track my money.\");\n}\n\nexport function FinancesView(props: FinancesViewProps = {}): ReactNode {\n const fetchers = props.fetchers ?? defaultFetchers;\n const [state, setState] = useState<LoadState>({ kind: \"loading\" });\n\n const fetchersRef = useRef(fetchers);\n fetchersRef.current = fetchers;\n\n const load = useCallback((quiet = false) => {\n let cancelled = false;\n if (!quiet) setState({ kind: \"loading\" });\n Promise.all([\n fetchersRef.current.fetchDashboard(),\n fetchersRef.current.fetchSources(),\n fetchersRef.current.fetchTransactions(),\n fetchersRef.current.fetchRecurring(),\n ])\n .then(([dashboard, sources, transactions, recurring]) => {\n if (cancelled) return;\n const connected = sources.sources.some(\n (source) => source.status !== \"disconnected\",\n );\n setState({\n kind: \"ready\",\n data: {\n hasSource: connected,\n balance: mapBalance(dashboard),\n transactions: transactions.transactions.map(mapTransaction),\n recurring: recurring.charges.map(mapRecurring),\n },\n });\n })\n .catch((error: unknown) => {\n if (cancelled || quiet) return;\n setState({\n kind: \"error\",\n message:\n error instanceof Error ? error.message : \"Could not load finances.\",\n });\n });\n return () => {\n cancelled = true;\n };\n }, []);\n\n useEffect(() => load(), [load]);\n\n // Poll quietly every 30s so the dashboard stays fresh without a manual\n // refresh. Transient poll failures are ignored — the explicit Retry path is\n // what surfaces errors to the user.\n useEffect(() => {\n const id = setInterval(() => load(true), POLL_INTERVAL_MS);\n return () => clearInterval(id);\n }, [load]);\n\n const snapshot = useMemo<FinancesSnapshot>(() => {\n if (state.kind === \"loading\") {\n return EMPTY_FINANCES_SNAPSHOT;\n }\n if (state.kind === \"error\") {\n return {\n ...EMPTY_FINANCES_SNAPSHOT,\n state: \"error\",\n error: state.message,\n };\n }\n const { hasSource, balance, transactions, recurring } = state.data;\n if (!hasSource) {\n return { ...EMPTY_FINANCES_SNAPSHOT, state: \"empty\" };\n }\n return {\n state: \"ready\",\n balance: toBalanceCard(balance),\n transactions: transactions.map(toTransactionCard),\n recurring: recurring.map(toRecurringCard),\n note: proactiveNote(balance, recurring),\n };\n }, [state]);\n\n const onAction = useCallback(\n (action: string) => {\n if (action === \"retry\") {\n load();\n return;\n }\n if (action === \"connect\") {\n requestConnectSource();\n return;\n }\n // `txn-<id>` / `bill-<id>` open affordances route to chat; PA owns the\n // detail surface, so this view never fabricates a drill-down.\n if (action.startsWith(\"txn-\") || action.startsWith(\"bill-\")) {\n const send = (client as { sendChatMessage?: (text: string) => void })\n .sendChatMessage;\n send?.(`Show me the details for ${action}.`);\n }\n },\n [load],\n );\n\n return (\n <SpatialSurface>\n <FinancesSpatialView snapshot={snapshot} onAction={onAction} />\n </SpatialSurface>\n );\n}\n\nexport default FinancesView;\n"],"mappings":"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","names":[]}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Vite view-bundle entry for @elizaos/plugin-finances.
3
+ *
4
+ * The built bundle (dist/views/bundle.js) exposes a named `FinancesView`
5
+ * export so the view loader can resolve it via the componentExport field in
6
+ * the Plugin `views` registration. Kept separate from FinancesView.tsx so
7
+ * that component file stays Fast-Refresh-compatible in dev.
8
+ */
9
+ export { FinancesView } from "./FinancesView.js";
10
+ //# sourceMappingURL=finances-view-bundle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finances-view-bundle.d.ts","sourceRoot":"","sources":["../../../src/components/finances/finances-view-bundle.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { FinancesView } from "./FinancesView.js";
2
+ export {
3
+ FinancesView
4
+ };
5
+ //# sourceMappingURL=finances-view-bundle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/finances/finances-view-bundle.ts"],"sourcesContent":["/**\n * Vite view-bundle entry for @elizaos/plugin-finances.\n *\n * The built bundle (dist/views/bundle.js) exposes a named `FinancesView`\n * export so the view loader can resolve it via the componentExport field in\n * the Plugin `views` registration. Kept separate from FinancesView.tsx so\n * that component file stays Fast-Refresh-compatible in dev.\n */\n\nexport { FinancesView } from \"./FinancesView.js\";\n"],"mappings":"AASA,SAAS,oBAAoB;","names":[]}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Raw-SQL repository for the finance back-end.
3
+ *
4
+ * Owns all reads/writes against the `app_finances` schema (payment sources,
5
+ * payment transactions, subscription audits / candidates / cancellations).
6
+ * Table NAMES are preserved verbatim from the original LifeOps tables
7
+ * (`life_payment_*`, `life_subscription_*`) so the schema-copy migration in
8
+ * {@link ../services/migration.ts} can move existing rows across schemas.
9
+ *
10
+ * Every statement qualifies its table with the `app_finances.` prefix via
11
+ * {@link FINANCE_TABLES}. SQL execution + value encoding go through the
12
+ * self-contained {@link ./sql.ts} helpers (the runtime DB handle), so this
13
+ * repository has no dependency on plugin-personal-assistant.
14
+ */
15
+ import type { IAgentRuntime } from "@elizaos/core";
16
+ import type { LifeOpsPaymentSource, LifeOpsPaymentTransaction } from "../payment-types.js";
17
+ import type { LifeOpsSubscriptionAudit, LifeOpsSubscriptionCancellation, LifeOpsSubscriptionCandidate } from "../subscriptions-types.js";
18
+ export declare function createLifeOpsSubscriptionAudit(params: Omit<LifeOpsSubscriptionAudit, "id" | "createdAt" | "updatedAt">): LifeOpsSubscriptionAudit;
19
+ export declare function createLifeOpsSubscriptionCandidate(params: Omit<LifeOpsSubscriptionCandidate, "id" | "createdAt" | "updatedAt">): LifeOpsSubscriptionCandidate;
20
+ export declare function createLifeOpsSubscriptionCancellation(params: Omit<LifeOpsSubscriptionCancellation, "id" | "createdAt" | "updatedAt">): LifeOpsSubscriptionCancellation;
21
+ export declare class FinancesRepository {
22
+ readonly runtime: IAgentRuntime;
23
+ constructor(runtime: IAgentRuntime);
24
+ createSubscriptionAudit(audit: LifeOpsSubscriptionAudit): Promise<void>;
25
+ updateSubscriptionAudit(audit: LifeOpsSubscriptionAudit): Promise<void>;
26
+ getSubscriptionAudit(agentId: string, auditId: string): Promise<LifeOpsSubscriptionAudit | null>;
27
+ getLatestSubscriptionAudit(agentId: string): Promise<LifeOpsSubscriptionAudit | null>;
28
+ createSubscriptionCandidate(candidate: LifeOpsSubscriptionCandidate): Promise<void>;
29
+ listSubscriptionCandidatesForAudit(agentId: string, auditId: string): Promise<LifeOpsSubscriptionCandidate[]>;
30
+ getSubscriptionCandidate(agentId: string, candidateId: string): Promise<LifeOpsSubscriptionCandidate | null>;
31
+ createSubscriptionCancellation(cancellation: LifeOpsSubscriptionCancellation): Promise<void>;
32
+ updateSubscriptionCancellation(cancellation: LifeOpsSubscriptionCancellation): Promise<void>;
33
+ getSubscriptionCancellation(agentId: string, cancellationId: string): Promise<LifeOpsSubscriptionCancellation | null>;
34
+ getLatestSubscriptionCancellation(agentId: string, serviceSlug?: string): Promise<LifeOpsSubscriptionCancellation | null>;
35
+ upsertPaymentSource(source: LifeOpsPaymentSource): Promise<void>;
36
+ listPaymentSources(agentId: string): Promise<LifeOpsPaymentSource[]>;
37
+ getPaymentSource(agentId: string, sourceId: string): Promise<LifeOpsPaymentSource | null>;
38
+ deletePaymentSource(agentId: string, sourceId: string): Promise<void>;
39
+ deletePaymentTransactionById(agentId: string, transactionId: string): Promise<void>;
40
+ insertPaymentTransaction(transaction: LifeOpsPaymentTransaction): Promise<boolean>;
41
+ listPaymentTransactions(agentId: string, args?: {
42
+ sourceId?: string | null;
43
+ sinceAt?: string | null;
44
+ untilAt?: string | null;
45
+ limit?: number | null;
46
+ merchantContains?: string | null;
47
+ onlyDebits?: boolean | null;
48
+ }): Promise<LifeOpsPaymentTransaction[]>;
49
+ countPaymentTransactionsForSource(agentId: string, sourceId: string): Promise<number>;
50
+ }
51
+ //# sourceMappingURL=finances-repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finances-repository.d.ts","sourceRoot":"","sources":["../../src/db/finances-repository.ts"],"names":[],"mappings":"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"}