@lchavezpozo/firefly-plugin-openclaw 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Luis Chavez
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # πŸ”₯ Firefly III Plugin for OpenClaw
2
+
3
+ A native OpenClaw plugin for [Firefly III](https://www.firefly-iii.org/) - the self-hosted personal finance manager.
4
+
5
+ ## Features
6
+
7
+ - πŸ’° **Check balances** - View all your asset accounts
8
+ - πŸ“ **Record transactions** - Log expenses, income, and transfers
9
+ - πŸ“Š **View recent transactions** - See your latest activity
10
+ - πŸ—‘οΈ **Delete transactions** - Remove incorrect entries
11
+ - πŸ“ˆ **Monthly summary** - Get spending overview
12
+ - 🏷️ **List categories** - View all your categories
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ openclaw plugins install @lchavezpozo/firefly-plugin-openclaw
18
+ ```
19
+
20
+ Or clone manually:
21
+
22
+ ```bash
23
+ git clone https://github.com/lchavezpozo/firefly-plugin-openclaw.git ~/.openclaw/plugins/firefly-iii
24
+ ```
25
+
26
+ ## Configuration
27
+
28
+ ### 1. Get your Firefly III API Token
29
+
30
+ 1. Go to your Firefly III instance
31
+ 2. Navigate to **Options** β†’ **Profile** β†’ **OAuth**
32
+ 3. Create a new Personal Access Token
33
+ 4. Copy the token
34
+
35
+ ### 2. Configure the plugin
36
+
37
+ Add to your `~/.openclaw/openclaw.json`:
38
+
39
+ ```json
40
+ {
41
+ "plugins": {
42
+ "load": {
43
+ "paths": ["~/.openclaw/plugins/firefly-iii"]
44
+ },
45
+ "entries": {
46
+ "firefly-iii": {
47
+ "enabled": true,
48
+ "config": {
49
+ "url": "http://your-firefly-instance:8080",
50
+ "token": "your-api-token-here"
51
+ }
52
+ }
53
+ }
54
+ }
55
+ }
56
+ ```
57
+
58
+ Or use a credentials file:
59
+
60
+ ```json
61
+ {
62
+ "plugins": {
63
+ "entries": {
64
+ "firefly-iii": {
65
+ "enabled": true,
66
+ "config": {
67
+ "credentialsPath": "/path/to/firefly-credentials.json"
68
+ }
69
+ }
70
+ }
71
+ }
72
+ }
73
+ ```
74
+
75
+ Credentials file format:
76
+ ```json
77
+ {
78
+ "url": "http://your-firefly-instance:8080",
79
+ "token": "your-api-token-here"
80
+ }
81
+ ```
82
+
83
+ ### 3. Restart OpenClaw
84
+
85
+ ```bash
86
+ openclaw gateway restart
87
+ ```
88
+
89
+ ## Usage
90
+
91
+ Once configured, the AI will automatically use these tools when you ask about finances:
92
+
93
+ - "How much money do I have?" β†’ `firefly_accounts`
94
+ - "I spent $50 on groceries" β†’ `firefly_transaction`
95
+ - "Show my recent expenses" β†’ `firefly_recent`
96
+ - "Monthly spending summary" β†’ `firefly_summary`
97
+
98
+ ## Available Tools
99
+
100
+ | Tool | Description |
101
+ |------|-------------|
102
+ | `firefly_accounts` | Get all asset account balances |
103
+ | `firefly_transaction` | Record expense/income/transfer |
104
+ | `firefly_recent` | List recent transactions |
105
+ | `firefly_delete` | Delete a transaction by ID |
106
+ | `firefly_summary` | Get current month summary |
107
+ | `firefly_categories` | List all categories |
108
+
109
+ ## Requirements
110
+
111
+ - OpenClaw 2026.2.0 or later
112
+ - Firefly III instance with API access
113
+ - Personal Access Token from Firefly III
114
+
115
+ ## License
116
+
117
+ MIT Β© Luis Chavez
118
+
119
+ ## Contributing
120
+
121
+ PRs welcome! Feel free to open issues for bugs or feature requests.
package/index.ts ADDED
@@ -0,0 +1,347 @@
1
+ /**
2
+ * Firefly III Plugin for OpenClaw
3
+ *
4
+ * Personal finance management - check balances, record transactions, view spending.
5
+ * https://github.com/lchavezpozo/firefly-plugin-openclaw
6
+ */
7
+
8
+ import { readFileSync } from "fs";
9
+ import { resolve } from "path";
10
+ import { homedir } from "os";
11
+
12
+ interface FireflyConfig {
13
+ url?: string;
14
+ token?: string;
15
+ credentialsPath?: string;
16
+ }
17
+
18
+ interface Credentials {
19
+ url: string;
20
+ token: string;
21
+ }
22
+
23
+ function expandPath(p: string): string {
24
+ if (p.startsWith("~/")) {
25
+ return resolve(homedir(), p.slice(2));
26
+ }
27
+ return resolve(p);
28
+ }
29
+
30
+ function getCredentials(config: FireflyConfig): Credentials {
31
+ // Direct config takes precedence
32
+ if (config.url && config.token) {
33
+ return { url: config.url, token: config.token };
34
+ }
35
+
36
+ // Try credentials file
37
+ if (config.credentialsPath) {
38
+ const credPath = expandPath(config.credentialsPath);
39
+ const creds = JSON.parse(readFileSync(credPath, "utf-8"));
40
+ return { url: creds.url, token: creds.token };
41
+ }
42
+
43
+ throw new Error(
44
+ "Firefly III plugin not configured. Set url+token in config or provide credentialsPath."
45
+ );
46
+ }
47
+
48
+ async function fireflyFetch(creds: Credentials, endpoint: string, options: RequestInit = {}) {
49
+ const url = creds.url.replace(/\/$/, ""); // Remove trailing slash
50
+ const response = await fetch(`${url}${endpoint}`, {
51
+ ...options,
52
+ headers: {
53
+ "Authorization": `Bearer ${creds.token}`,
54
+ "Content-Type": "application/json",
55
+ "Accept": "application/json",
56
+ ...options.headers,
57
+ },
58
+ });
59
+
60
+ if (!response.ok) {
61
+ const text = await response.text();
62
+ throw new Error(`Firefly API error: ${response.status} ${response.statusText} - ${text}`);
63
+ }
64
+
65
+ if (response.status === 204) return null;
66
+ return response.json();
67
+ }
68
+
69
+ export default function (api: any) {
70
+ const config: FireflyConfig = api.config?.plugins?.entries?.["firefly-iii"]?.config || {};
71
+
72
+ // ============================================
73
+ // Tool: Get account balances
74
+ // ============================================
75
+ api.registerTool({
76
+ name: "firefly_accounts",
77
+ description: "Get all asset account balances from Firefly III. Use when user asks about their money, balances, or 'cuΓ‘nto tengo'.",
78
+ parameters: {
79
+ type: "object",
80
+ properties: {},
81
+ required: [],
82
+ },
83
+ async execute() {
84
+ const creds = getCredentials(config);
85
+ const data = await fireflyFetch(creds, "/api/v1/accounts?type=asset");
86
+
87
+ const accounts = data.data.map((acc: any) => ({
88
+ name: acc.attributes.name,
89
+ balance: parseFloat(acc.attributes.current_balance),
90
+ currency: acc.attributes.currency_symbol,
91
+ }));
92
+
93
+ const total = accounts.reduce((sum: number, acc: any) => sum + acc.balance, 0);
94
+
95
+ return {
96
+ content: [{
97
+ type: "text",
98
+ text: JSON.stringify({ accounts, total, currency: accounts[0]?.currency || "$" }, null, 2)
99
+ }]
100
+ };
101
+ },
102
+ });
103
+
104
+ // ============================================
105
+ // Tool: Record a transaction
106
+ // ============================================
107
+ api.registerTool({
108
+ name: "firefly_transaction",
109
+ description: "Record a new transaction (expense, income, or transfer) in Firefly III. Use for 'gastΓ©', 'paguΓ©', 'comprΓ©', 'recibΓ­', 'transferΓ­'.",
110
+ parameters: {
111
+ type: "object",
112
+ properties: {
113
+ type: {
114
+ type: "string",
115
+ enum: ["withdrawal", "deposit", "transfer"],
116
+ description: "Transaction type: withdrawal (expense), deposit (income), transfer"
117
+ },
118
+ amount: {
119
+ type: "number",
120
+ description: "Transaction amount (positive number)"
121
+ },
122
+ description: {
123
+ type: "string",
124
+ description: "What was this transaction for"
125
+ },
126
+ account: {
127
+ type: "string",
128
+ description: "Source account name (e.g., 'Checking', 'Savings')"
129
+ },
130
+ category: {
131
+ type: "string",
132
+ description: "Optional category (e.g., 'Food', 'Transport')"
133
+ },
134
+ destination_account: {
135
+ type: "string",
136
+ description: "Destination account (required for transfers)"
137
+ },
138
+ },
139
+ required: ["type", "amount", "description", "account"],
140
+ },
141
+ async execute(_id: string, params: any) {
142
+ const creds = getCredentials(config);
143
+
144
+ // Get source account ID
145
+ const accountsData = await fireflyFetch(creds, "/api/v1/accounts?type=asset");
146
+ const sourceAccount = accountsData.data.find((acc: any) =>
147
+ acc.attributes.name.toLowerCase() === params.account.toLowerCase()
148
+ );
149
+
150
+ if (!sourceAccount) {
151
+ const available = accountsData.data.map((a: any) => a.attributes.name).join(", ");
152
+ return {
153
+ content: [{
154
+ type: "text",
155
+ text: `Error: Account '${params.account}' not found. Available accounts: ${available}`
156
+ }]
157
+ };
158
+ }
159
+
160
+ const transaction: any = {
161
+ type: params.type,
162
+ date: new Date().toISOString().split("T")[0],
163
+ amount: params.amount.toString(),
164
+ description: params.description,
165
+ source_id: sourceAccount.id,
166
+ };
167
+
168
+ if (params.category) {
169
+ transaction.category_name = params.category;
170
+ }
171
+
172
+ if (params.type === "transfer" && params.destination_account) {
173
+ const destAccount = accountsData.data.find((acc: any) =>
174
+ acc.attributes.name.toLowerCase() === params.destination_account.toLowerCase()
175
+ );
176
+ if (destAccount) {
177
+ transaction.destination_id = destAccount.id;
178
+ } else {
179
+ return {
180
+ content: [{
181
+ type: "text",
182
+ text: `Error: Destination account '${params.destination_account}' not found.`
183
+ }]
184
+ };
185
+ }
186
+ }
187
+
188
+ const result = await fireflyFetch(creds, "/api/v1/transactions", {
189
+ method: "POST",
190
+ body: JSON.stringify({ transactions: [transaction] }),
191
+ });
192
+
193
+ const tx = result.data.attributes.transactions[0];
194
+ return {
195
+ content: [{
196
+ type: "text",
197
+ text: JSON.stringify({
198
+ success: true,
199
+ id: result.data.id,
200
+ type: tx.type,
201
+ amount: tx.amount,
202
+ currency: tx.currency_symbol,
203
+ description: tx.description,
204
+ date: tx.date,
205
+ category: tx.category_name || null,
206
+ }, null, 2)
207
+ }]
208
+ };
209
+ },
210
+ });
211
+
212
+ // ============================================
213
+ // Tool: Get recent transactions
214
+ // ============================================
215
+ api.registerTool({
216
+ name: "firefly_recent",
217
+ description: "Get recent transactions from Firefly III. Use for 'ΓΊltimos gastos', 'transacciones recientes', 'en quΓ© gastΓ©'.",
218
+ parameters: {
219
+ type: "object",
220
+ properties: {
221
+ limit: {
222
+ type: "number",
223
+ description: "Number of transactions to return (default 10)"
224
+ },
225
+ },
226
+ required: [],
227
+ },
228
+ async execute(_id: string, params: any) {
229
+ const creds = getCredentials(config);
230
+ const limit = params.limit || 10;
231
+ const data = await fireflyFetch(creds, `/api/v1/transactions?limit=${limit}`);
232
+
233
+ const transactions = data.data.map((tx: any) => {
234
+ const t = tx.attributes.transactions[0];
235
+ return {
236
+ id: tx.id,
237
+ date: t.date?.split("T")[0],
238
+ type: t.type,
239
+ amount: `${t.currency_symbol}${t.amount}`,
240
+ description: t.description,
241
+ category: t.category_name || null,
242
+ source: t.source_name,
243
+ destination: t.destination_name,
244
+ };
245
+ });
246
+
247
+ return {
248
+ content: [{
249
+ type: "text",
250
+ text: JSON.stringify(transactions, null, 2)
251
+ }]
252
+ };
253
+ },
254
+ });
255
+
256
+ // ============================================
257
+ // Tool: Delete a transaction
258
+ // ============================================
259
+ api.registerTool({
260
+ name: "firefly_delete",
261
+ description: "Delete a transaction from Firefly III by its ID.",
262
+ parameters: {
263
+ type: "object",
264
+ properties: {
265
+ transaction_id: {
266
+ type: "string",
267
+ description: "The transaction ID to delete"
268
+ },
269
+ },
270
+ required: ["transaction_id"],
271
+ },
272
+ async execute(_id: string, params: any) {
273
+ const creds = getCredentials(config);
274
+
275
+ await fireflyFetch(creds, `/api/v1/transactions/${params.transaction_id}`, {
276
+ method: "DELETE",
277
+ });
278
+
279
+ return {
280
+ content: [{
281
+ type: "text",
282
+ text: JSON.stringify({ success: true, deleted: params.transaction_id })
283
+ }]
284
+ };
285
+ },
286
+ });
287
+
288
+ // ============================================
289
+ // Tool: Get monthly summary
290
+ // ============================================
291
+ api.registerTool({
292
+ name: "firefly_summary",
293
+ description: "Get a financial summary for the current month from Firefly III. Use for 'resumen del mes', 'cuΓ‘nto gastΓ© este mes'.",
294
+ parameters: {
295
+ type: "object",
296
+ properties: {},
297
+ required: [],
298
+ },
299
+ async execute() {
300
+ const creds = getCredentials(config);
301
+
302
+ const now = new Date();
303
+ const start = new Date(now.getFullYear(), now.getMonth(), 1).toISOString().split("T")[0];
304
+ const end = now.toISOString().split("T")[0];
305
+
306
+ const data = await fireflyFetch(creds, `/api/v1/summary/basic?start=${start}&end=${end}`);
307
+
308
+ return {
309
+ content: [{
310
+ type: "text",
311
+ text: JSON.stringify(data, null, 2)
312
+ }]
313
+ };
314
+ },
315
+ });
316
+
317
+ // ============================================
318
+ // Tool: Get categories
319
+ // ============================================
320
+ api.registerTool({
321
+ name: "firefly_categories",
322
+ description: "List all spending categories from Firefly III.",
323
+ parameters: {
324
+ type: "object",
325
+ properties: {},
326
+ required: [],
327
+ },
328
+ async execute() {
329
+ const creds = getCredentials(config);
330
+ const data = await fireflyFetch(creds, "/api/v1/categories");
331
+
332
+ const categories = data.data.map((cat: any) => ({
333
+ id: cat.id,
334
+ name: cat.attributes.name,
335
+ spent: cat.attributes.spent || null,
336
+ earned: cat.attributes.earned || null,
337
+ }));
338
+
339
+ return {
340
+ content: [{
341
+ type: "text",
342
+ text: JSON.stringify(categories, null, 2)
343
+ }]
344
+ };
345
+ },
346
+ });
347
+ }
@@ -0,0 +1,39 @@
1
+ {
2
+ "id": "firefly-iii",
3
+ "name": "Firefly III",
4
+ "description": "Personal finance management - check balances, record transactions, view spending",
5
+ "version": "1.0.0",
6
+ "configSchema": {
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "url": {
11
+ "type": "string",
12
+ "description": "Firefly III server URL (e.g., http://localhost:8080)"
13
+ },
14
+ "token": {
15
+ "type": "string",
16
+ "description": "Firefly III Personal Access Token"
17
+ },
18
+ "credentialsPath": {
19
+ "type": "string",
20
+ "description": "Path to JSON file with url and token (alternative to inline config)"
21
+ }
22
+ }
23
+ },
24
+ "uiHints": {
25
+ "token": {
26
+ "sensitive": true,
27
+ "label": "API Token",
28
+ "placeholder": "eyJ0eXAiOiJKV1QiLCJhbGciOi..."
29
+ },
30
+ "url": {
31
+ "label": "Firefly III URL",
32
+ "placeholder": "http://localhost:8080"
33
+ },
34
+ "credentialsPath": {
35
+ "label": "Credentials File Path",
36
+ "placeholder": "~/.openclaw/credentials/firefly.json"
37
+ }
38
+ }
39
+ }
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@lchavezpozo/firefly-plugin-openclaw",
3
+ "version": "1.0.0",
4
+ "description": "OpenClaw plugin for Firefly III personal finance management",
5
+ "main": "index.ts",
6
+ "keywords": [
7
+ "openclaw",
8
+ "openclaw-plugin",
9
+ "firefly-iii",
10
+ "finance",
11
+ "personal-finance",
12
+ "budgeting"
13
+ ],
14
+ "author": "Luis Chavez <lchavezpozo@gmail.com>",
15
+ "license": "MIT",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/lchavezpozo/firefly-plugin-openclaw.git"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/lchavezpozo/firefly-plugin-openclaw/issues"
22
+ },
23
+ "homepage": "https://github.com/lchavezpozo/firefly-plugin-openclaw#readme",
24
+ "engines": {
25
+ "node": ">=18.0.0"
26
+ },
27
+ "files": [
28
+ "index.ts",
29
+ "openclaw.plugin.json",
30
+ "README.md",
31
+ "LICENSE"
32
+ ]
33
+ }