@ktmcp-cli/nordigen 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/.env.example +11 -0
- package/.eslintrc.json +17 -0
- package/AGENT.md +480 -0
- package/CHANGELOG.md +69 -0
- package/CONTRIBUTING.md +198 -0
- package/EXAMPLES.md +561 -0
- package/INDEX.md +193 -0
- package/LICENSE +21 -0
- package/OPENCLAW.md +468 -0
- package/PROJECT.md +366 -0
- package/QUICKREF.md +231 -0
- package/README.md +424 -0
- package/SETUP.md +259 -0
- package/SUMMARY.md +419 -0
- package/banner.png +0 -0
- package/bin/nordigen.js +84 -0
- package/logo.png +0 -0
- package/package.json +40 -0
- package/scripts/quickstart.sh +110 -0
- package/src/commands/accounts.js +205 -0
- package/src/commands/agreements.js +241 -0
- package/src/commands/auth.js +86 -0
- package/src/commands/config.js +173 -0
- package/src/commands/institutions.js +181 -0
- package/src/commands/payments.js +228 -0
- package/src/commands/requisitions.js +239 -0
- package/src/lib/api.js +491 -0
- package/src/lib/auth.js +113 -0
- package/src/lib/config.js +145 -0
- package/src/lib/output.js +255 -0
- package/test/api.test.js +88 -0
package/.env.example
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Nordigen API Credentials
|
|
2
|
+
# Get these from https://nordigen.com dashboard
|
|
3
|
+
|
|
4
|
+
NORDIGEN_SECRET_ID=your_secret_id_here
|
|
5
|
+
NORDIGEN_SECRET_KEY=your_secret_key_here
|
|
6
|
+
|
|
7
|
+
# Optional: Default country for commands
|
|
8
|
+
NORDIGEN_DEFAULT_COUNTRY=GB
|
|
9
|
+
|
|
10
|
+
# Optional: Enable debug mode
|
|
11
|
+
# DEBUG=1
|
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"env": {
|
|
3
|
+
"es2021": true,
|
|
4
|
+
"node": true
|
|
5
|
+
},
|
|
6
|
+
"extends": "eslint:recommended",
|
|
7
|
+
"parserOptions": {
|
|
8
|
+
"ecmaVersion": "latest",
|
|
9
|
+
"sourceType": "module"
|
|
10
|
+
},
|
|
11
|
+
"rules": {
|
|
12
|
+
"indent": ["error", 2],
|
|
13
|
+
"linebreak-style": ["error", "unix"],
|
|
14
|
+
"quotes": ["error", "single"],
|
|
15
|
+
"semi": ["error", "always"]
|
|
16
|
+
}
|
|
17
|
+
}
|
package/AGENT.md
ADDED
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
# Nordigen CLI - AI Agent Integration Guide
|
|
2
|
+
|
|
3
|
+
This guide shows how AI agents can effectively use the Nordigen CLI to access open banking data.
|
|
4
|
+
|
|
5
|
+
## Agent Capabilities
|
|
6
|
+
|
|
7
|
+
The Nordigen CLI enables AI agents to:
|
|
8
|
+
|
|
9
|
+
1. Browse and search financial institutions across Europe
|
|
10
|
+
2. Create and manage end user agreements
|
|
11
|
+
3. Initiate bank account connections
|
|
12
|
+
4. Retrieve account balances and details
|
|
13
|
+
5. Fetch and analyze transaction history
|
|
14
|
+
6. Process payment information
|
|
15
|
+
|
|
16
|
+
## Basic Agent Patterns
|
|
17
|
+
|
|
18
|
+
### Pattern 1: Institution Discovery
|
|
19
|
+
|
|
20
|
+
```javascript
|
|
21
|
+
// Agent discovers banks in a specific country
|
|
22
|
+
async function findInstitutions(country, query = null) {
|
|
23
|
+
if (query) {
|
|
24
|
+
// Search for specific institution
|
|
25
|
+
const result = await exec(`nordigen institutions search "${query}" --country ${country} --json`);
|
|
26
|
+
return JSON.parse(result.stdout);
|
|
27
|
+
} else {
|
|
28
|
+
// List all institutions
|
|
29
|
+
const result = await exec(`nordigen institutions list --country ${country} --json`);
|
|
30
|
+
return JSON.parse(result.stdout);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Usage
|
|
35
|
+
const banks = await findInstitutions('GB', 'Barclays');
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Pattern 2: Account Connection Flow
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
// Agent guides user through bank connection
|
|
42
|
+
async function connectBankAccount(institutionId, redirectUrl) {
|
|
43
|
+
// 1. Create end user agreement
|
|
44
|
+
const agreementCmd = `nordigen agreements create --institution-id ${institutionId} --max-days 90 --json`;
|
|
45
|
+
const agreement = JSON.parse((await exec(agreementCmd)).stdout);
|
|
46
|
+
|
|
47
|
+
// 2. Create requisition
|
|
48
|
+
const reqCmd = `nordigen requisitions create --institution-id ${institutionId} --redirect ${redirectUrl} --agreement ${agreement.id} --json`;
|
|
49
|
+
const requisition = JSON.parse((await exec(reqCmd)).stdout);
|
|
50
|
+
|
|
51
|
+
// 3. Return authentication link for user
|
|
52
|
+
return {
|
|
53
|
+
requisitionId: requisition.id,
|
|
54
|
+
authLink: requisition.link,
|
|
55
|
+
status: requisition.status
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Pattern 3: Transaction Analysis
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
// Agent retrieves and analyzes transactions
|
|
64
|
+
async function analyzeTransactions(accountId, dateFrom, dateTo) {
|
|
65
|
+
const cmd = `nordigen accounts transactions ${accountId} --from ${dateFrom} --to ${dateTo} --json`;
|
|
66
|
+
const result = await exec(cmd);
|
|
67
|
+
const data = JSON.parse(result.stdout);
|
|
68
|
+
|
|
69
|
+
const transactions = data.transactions.booked || [];
|
|
70
|
+
|
|
71
|
+
// Analyze spending patterns
|
|
72
|
+
const analysis = {
|
|
73
|
+
totalSpent: 0,
|
|
74
|
+
totalIncome: 0,
|
|
75
|
+
categories: {},
|
|
76
|
+
transactionCount: transactions.length
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
transactions.forEach(tx => {
|
|
80
|
+
const amount = parseFloat(tx.transactionAmount.amount);
|
|
81
|
+
if (amount < 0) {
|
|
82
|
+
analysis.totalSpent += Math.abs(amount);
|
|
83
|
+
} else {
|
|
84
|
+
analysis.totalIncome += amount;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
return analysis;
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Pattern 4: Multi-Account Balance Check
|
|
93
|
+
|
|
94
|
+
```javascript
|
|
95
|
+
// Agent checks balances across multiple accounts
|
|
96
|
+
async function checkAllBalances(accountIds) {
|
|
97
|
+
const balances = await Promise.all(
|
|
98
|
+
accountIds.map(async (accountId) => {
|
|
99
|
+
try {
|
|
100
|
+
const cmd = `nordigen accounts balances ${accountId} --json`;
|
|
101
|
+
const result = await exec(cmd);
|
|
102
|
+
const data = JSON.parse(result.stdout);
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
accountId,
|
|
106
|
+
balances: data.balances,
|
|
107
|
+
success: true
|
|
108
|
+
};
|
|
109
|
+
} catch (error) {
|
|
110
|
+
return {
|
|
111
|
+
accountId,
|
|
112
|
+
error: error.message,
|
|
113
|
+
success: false
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
return balances;
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Advanced Agent Workflows
|
|
124
|
+
|
|
125
|
+
### Workflow 1: Automated Financial Health Check
|
|
126
|
+
|
|
127
|
+
```javascript
|
|
128
|
+
async function financialHealthCheck(userId) {
|
|
129
|
+
// 1. Get all user's requisitions
|
|
130
|
+
const reqList = await exec('nordigen requisitions list --json');
|
|
131
|
+
const requisitions = JSON.parse(reqList.stdout).results;
|
|
132
|
+
|
|
133
|
+
// 2. Extract all account IDs
|
|
134
|
+
const accountIds = requisitions.flatMap(req => req.accounts);
|
|
135
|
+
|
|
136
|
+
// 3. Check balances
|
|
137
|
+
const balances = await checkAllBalances(accountIds);
|
|
138
|
+
|
|
139
|
+
// 4. Get recent transactions (last 30 days)
|
|
140
|
+
const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)
|
|
141
|
+
.toISOString().split('T')[0];
|
|
142
|
+
const today = new Date().toISOString().split('T')[0];
|
|
143
|
+
|
|
144
|
+
const transactionAnalysis = await Promise.all(
|
|
145
|
+
accountIds.map(id => analyzeTransactions(id, thirtyDaysAgo, today))
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
// 5. Generate report
|
|
149
|
+
return {
|
|
150
|
+
totalAccounts: accountIds.length,
|
|
151
|
+
totalBalance: balances.reduce((sum, b) => {
|
|
152
|
+
const balance = b.balances?.find(bal => bal.balanceType === 'expected')?.balanceAmount?.amount || 0;
|
|
153
|
+
return sum + parseFloat(balance);
|
|
154
|
+
}, 0),
|
|
155
|
+
monthlySpending: transactionAnalysis.reduce((sum, a) => sum + a.totalSpent, 0),
|
|
156
|
+
monthlyIncome: transactionAnalysis.reduce((sum, a) => sum + a.totalIncome, 0)
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Workflow 2: Smart Institution Recommendation
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
async function recommendInstitution(country, requirements) {
|
|
165
|
+
// Get all institutions
|
|
166
|
+
const cmd = `nordigen institutions list --country ${country} --json`;
|
|
167
|
+
const institutions = JSON.parse((await exec(cmd)).stdout);
|
|
168
|
+
|
|
169
|
+
// Score institutions based on requirements
|
|
170
|
+
const scored = institutions.map(inst => {
|
|
171
|
+
let score = 0;
|
|
172
|
+
|
|
173
|
+
// Prefer institutions with longer transaction history
|
|
174
|
+
if (inst.transaction_total_days >= 730) score += 3;
|
|
175
|
+
else if (inst.transaction_total_days >= 365) score += 2;
|
|
176
|
+
else if (inst.transaction_total_days >= 90) score += 1;
|
|
177
|
+
|
|
178
|
+
// Check payment support if required
|
|
179
|
+
if (requirements.payments && inst.payments_enabled) score += 2;
|
|
180
|
+
|
|
181
|
+
// Check account selection if required
|
|
182
|
+
if (requirements.accountSelection && inst.account_selection_supported) score += 1;
|
|
183
|
+
|
|
184
|
+
return { ...inst, score };
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// Sort by score and return top recommendations
|
|
188
|
+
return scored
|
|
189
|
+
.sort((a, b) => b.score - a.score)
|
|
190
|
+
.slice(0, 5);
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Workflow 3: Transaction Categorization
|
|
195
|
+
|
|
196
|
+
```javascript
|
|
197
|
+
async function categorizeTransactions(accountId, dateFrom, dateTo) {
|
|
198
|
+
const cmd = `nordigen accounts transactions ${accountId} --from ${dateFrom} --to ${dateTo} --json`;
|
|
199
|
+
const result = await exec(cmd);
|
|
200
|
+
const data = JSON.parse(result.stdout);
|
|
201
|
+
|
|
202
|
+
const transactions = data.transactions.booked || [];
|
|
203
|
+
|
|
204
|
+
// Simple rule-based categorization
|
|
205
|
+
const categories = {
|
|
206
|
+
groceries: /tesco|sainsbury|asda|morrisons|aldi|lidl/i,
|
|
207
|
+
transport: /uber|lyft|tfl|national rail|trainline/i,
|
|
208
|
+
utilities: /electricity|gas|water|internet|broadband/i,
|
|
209
|
+
entertainment: /netflix|spotify|amazon prime|cinema/i,
|
|
210
|
+
restaurants: /restaurant|cafe|pizza|burger|mcdonald/i
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const categorized = transactions.map(tx => {
|
|
214
|
+
const description = tx.remittanceInformationUnstructured || '';
|
|
215
|
+
let category = 'other';
|
|
216
|
+
|
|
217
|
+
for (const [cat, pattern] of Object.entries(categories)) {
|
|
218
|
+
if (pattern.test(description)) {
|
|
219
|
+
category = cat;
|
|
220
|
+
break;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
...tx,
|
|
226
|
+
category,
|
|
227
|
+
amount: parseFloat(tx.transactionAmount.amount)
|
|
228
|
+
};
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// Summarize by category
|
|
232
|
+
const summary = {};
|
|
233
|
+
categorized.forEach(tx => {
|
|
234
|
+
if (!summary[tx.category]) {
|
|
235
|
+
summary[tx.category] = { count: 0, total: 0 };
|
|
236
|
+
}
|
|
237
|
+
summary[tx.category].count++;
|
|
238
|
+
summary[tx.category].total += Math.abs(tx.amount);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
return { transactions: categorized, summary };
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Natural Language Interface
|
|
246
|
+
|
|
247
|
+
### Example: Agent Conversation Flow
|
|
248
|
+
|
|
249
|
+
```
|
|
250
|
+
User: "Connect my Barclays account"
|
|
251
|
+
|
|
252
|
+
Agent Analysis:
|
|
253
|
+
1. User wants to connect a bank account
|
|
254
|
+
2. Bank name: "Barclays"
|
|
255
|
+
3. Need to determine country (ask or infer from context)
|
|
256
|
+
|
|
257
|
+
Agent Response:
|
|
258
|
+
"I'll help you connect your Barclays account. Which country is your Barclays account in? (UK, Germany, etc.)"
|
|
259
|
+
|
|
260
|
+
User: "UK"
|
|
261
|
+
|
|
262
|
+
Agent Actions:
|
|
263
|
+
1. nordigen institutions search "Barclays" --country GB --json
|
|
264
|
+
2. Select appropriate institution ID
|
|
265
|
+
3. nordigen agreements create --institution-id BARCLAYS_BARCGB22 --max-days 90 --json
|
|
266
|
+
4. nordigen requisitions create --institution-id BARCLAYS_BARCGB22 --redirect https://app.example.com/callback --agreement <ID> --json
|
|
267
|
+
|
|
268
|
+
Agent Response:
|
|
269
|
+
"I've created a secure connection request. Please visit this link to authorize access to your Barclays account: [AUTH_LINK]"
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Example: Transaction Query
|
|
273
|
+
|
|
274
|
+
```
|
|
275
|
+
User: "How much did I spend on groceries last month?"
|
|
276
|
+
|
|
277
|
+
Agent Actions:
|
|
278
|
+
1. Calculate date range (last month)
|
|
279
|
+
2. Get user's account IDs from stored requisitions
|
|
280
|
+
3. For each account:
|
|
281
|
+
- nordigen accounts transactions <ID> --from 2024-01-01 --to 2024-01-31 --json
|
|
282
|
+
4. Categorize transactions
|
|
283
|
+
5. Sum amounts in "groceries" category
|
|
284
|
+
|
|
285
|
+
Agent Response:
|
|
286
|
+
"You spent £342.67 on groceries last month across 23 transactions. The main stores were Tesco (£178.42), Sainsbury's (£121.30), and Aldi (£42.95)."
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Error Handling for Agents
|
|
290
|
+
|
|
291
|
+
```javascript
|
|
292
|
+
async function safeExecute(command) {
|
|
293
|
+
try {
|
|
294
|
+
const result = await exec(command);
|
|
295
|
+
return { success: true, data: JSON.parse(result.stdout) };
|
|
296
|
+
} catch (error) {
|
|
297
|
+
// Parse error message
|
|
298
|
+
const errorData = {
|
|
299
|
+
success: false,
|
|
300
|
+
command: command,
|
|
301
|
+
exitCode: error.code
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
// Check for common errors
|
|
305
|
+
if (error.stderr.includes('Not authenticated')) {
|
|
306
|
+
errorData.type = 'AUTH_REQUIRED';
|
|
307
|
+
errorData.message = 'User needs to authenticate first';
|
|
308
|
+
errorData.action = 'Please run: nordigen auth login --secret-id <id> --secret-key <key>';
|
|
309
|
+
} else if (error.stderr.includes('Token is invalid or expired')) {
|
|
310
|
+
errorData.type = 'TOKEN_EXPIRED';
|
|
311
|
+
errorData.message = 'Authentication token expired';
|
|
312
|
+
errorData.action = 'Re-authenticating automatically...';
|
|
313
|
+
} else if (error.stderr.includes('Rate limit')) {
|
|
314
|
+
errorData.type = 'RATE_LIMIT';
|
|
315
|
+
errorData.message = 'API rate limit exceeded';
|
|
316
|
+
errorData.action = 'Waiting before retry...';
|
|
317
|
+
} else {
|
|
318
|
+
errorData.type = 'UNKNOWN';
|
|
319
|
+
errorData.message = error.stderr;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return errorData;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Security Best Practices
|
|
328
|
+
|
|
329
|
+
1. **Never log sensitive data**: Avoid logging full API responses that may contain IBANs or account details
|
|
330
|
+
2. **Use environment variables**: Store credentials in env vars, not code
|
|
331
|
+
3. **Implement access controls**: Ensure agents can only access data for authorized users
|
|
332
|
+
4. **Audit agent actions**: Log all CLI commands executed by agents
|
|
333
|
+
5. **Time-limited access**: Regularly refresh agreements with minimal access duration
|
|
334
|
+
|
|
335
|
+
## Agent State Management
|
|
336
|
+
|
|
337
|
+
```javascript
|
|
338
|
+
class NordigenAgent {
|
|
339
|
+
constructor(userId) {
|
|
340
|
+
this.userId = userId;
|
|
341
|
+
this.state = {
|
|
342
|
+
authenticated: false,
|
|
343
|
+
activeRequisitions: [],
|
|
344
|
+
accountIds: [],
|
|
345
|
+
lastSync: null
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async initialize() {
|
|
350
|
+
// Check authentication
|
|
351
|
+
const authStatus = await exec('nordigen auth status --json');
|
|
352
|
+
this.state.authenticated = JSON.parse(authStatus.stdout).authenticated;
|
|
353
|
+
|
|
354
|
+
if (this.state.authenticated) {
|
|
355
|
+
await this.syncRequisitions();
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
async syncRequisitions() {
|
|
360
|
+
const result = await exec('nordigen requisitions list --json');
|
|
361
|
+
const data = JSON.parse(result.stdout);
|
|
362
|
+
this.state.activeRequisitions = data.results;
|
|
363
|
+
this.state.accountIds = data.results.flatMap(r => r.accounts);
|
|
364
|
+
this.state.lastSync = new Date().toISOString();
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
async handleQuery(userInput) {
|
|
368
|
+
// Parse user intent
|
|
369
|
+
const intent = this.parseIntent(userInput);
|
|
370
|
+
|
|
371
|
+
switch (intent.type) {
|
|
372
|
+
case 'GET_BALANCE':
|
|
373
|
+
return await this.getBalances();
|
|
374
|
+
case 'GET_TRANSACTIONS':
|
|
375
|
+
return await this.getTransactions(intent.params);
|
|
376
|
+
case 'CONNECT_ACCOUNT':
|
|
377
|
+
return await this.connectAccount(intent.params);
|
|
378
|
+
default:
|
|
379
|
+
return { error: 'Unknown intent' };
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## Performance Optimization
|
|
386
|
+
|
|
387
|
+
1. **Cache institution lists**: Store institution data locally, refresh periodically
|
|
388
|
+
2. **Batch operations**: Use Promise.all() for parallel account queries
|
|
389
|
+
3. **Pagination**: Use --limit and --offset for large result sets
|
|
390
|
+
4. **Conditional fetching**: Only fetch data when needed, not proactively
|
|
391
|
+
|
|
392
|
+
## Testing Agent Integration
|
|
393
|
+
|
|
394
|
+
```javascript
|
|
395
|
+
// Mock CLI responses for testing
|
|
396
|
+
const mockCLI = {
|
|
397
|
+
'nordigen auth status': { authenticated: true },
|
|
398
|
+
'nordigen institutions list --country GB --json': [
|
|
399
|
+
{ id: 'BARCLAYS_BARCGB22', name: 'Barclays' }
|
|
400
|
+
]
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
async function testAgentFlow() {
|
|
404
|
+
// Test institution discovery
|
|
405
|
+
const banks = await findInstitutions('GB', 'Barclays');
|
|
406
|
+
assert(banks.length > 0);
|
|
407
|
+
|
|
408
|
+
// Test error handling
|
|
409
|
+
const result = await safeExecute('nordigen accounts get invalid-id --json');
|
|
410
|
+
assert(result.success === false);
|
|
411
|
+
|
|
412
|
+
// Test transaction analysis
|
|
413
|
+
const analysis = await analyzeTransactions('test-account', '2024-01-01', '2024-01-31');
|
|
414
|
+
assert(analysis.transactionCount >= 0);
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
## Example: Claude/GPT Integration
|
|
419
|
+
|
|
420
|
+
```python
|
|
421
|
+
import subprocess
|
|
422
|
+
import json
|
|
423
|
+
|
|
424
|
+
def run_nordigen_command(command):
|
|
425
|
+
"""Execute Nordigen CLI command and return JSON output."""
|
|
426
|
+
result = subprocess.run(
|
|
427
|
+
command,
|
|
428
|
+
shell=True,
|
|
429
|
+
capture_output=True,
|
|
430
|
+
text=True
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
if result.returncode != 0:
|
|
434
|
+
raise Exception(f"Command failed: {result.stderr}")
|
|
435
|
+
|
|
436
|
+
return json.loads(result.stdout)
|
|
437
|
+
|
|
438
|
+
# Agent function
|
|
439
|
+
def get_account_summary(account_id):
|
|
440
|
+
"""Get comprehensive account summary."""
|
|
441
|
+
|
|
442
|
+
# Get balances
|
|
443
|
+
balances = run_nordigen_command(
|
|
444
|
+
f"nordigen accounts balances {account_id} --json"
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
# Get transactions (last 30 days)
|
|
448
|
+
from datetime import datetime, timedelta
|
|
449
|
+
end_date = datetime.now().strftime('%Y-%m-%d')
|
|
450
|
+
start_date = (datetime.now() - timedelta(days=30)).strftime('%Y-%m-%d')
|
|
451
|
+
|
|
452
|
+
transactions = run_nordigen_command(
|
|
453
|
+
f"nordigen accounts transactions {account_id} --from {start_date} --to {end_date} --json"
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
# Format for LLM context
|
|
457
|
+
return {
|
|
458
|
+
"account_id": account_id,
|
|
459
|
+
"balances": balances["balances"],
|
|
460
|
+
"recent_transactions": len(transactions["transactions"]["booked"]),
|
|
461
|
+
"transaction_data": transactions
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
# Use in prompt
|
|
465
|
+
account_summary = get_account_summary("abc-123")
|
|
466
|
+
prompt = f"""
|
|
467
|
+
Analyze this bank account and provide insights:
|
|
468
|
+
|
|
469
|
+
{json.dumps(account_summary, indent=2)}
|
|
470
|
+
|
|
471
|
+
Please provide:
|
|
472
|
+
1. Current financial position
|
|
473
|
+
2. Spending patterns
|
|
474
|
+
3. Recommendations
|
|
475
|
+
"""
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
## Conclusion
|
|
479
|
+
|
|
480
|
+
The Nordigen CLI provides a powerful, simple interface for AI agents to interact with open banking APIs. By following these patterns and best practices, agents can provide sophisticated financial analysis and account management capabilities.
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the Nordigen CLI will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.0] - 2024-02-16
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Initial release of Nordigen CLI
|
|
12
|
+
- Complete coverage of Nordigen API v2 endpoints
|
|
13
|
+
- Authentication commands (login, logout, status)
|
|
14
|
+
- Institution commands (list, search, get)
|
|
15
|
+
- Account commands (get, balances, details, transactions)
|
|
16
|
+
- Agreement commands (list, create, get, delete, accept)
|
|
17
|
+
- Requisition commands (list, create, get, delete)
|
|
18
|
+
- Payment commands (list, get, delete, creditors, fields)
|
|
19
|
+
- Configuration management commands
|
|
20
|
+
- JWT token authentication with automatic refresh
|
|
21
|
+
- Secure credential storage using Conf
|
|
22
|
+
- Rich terminal output with colors and formatting
|
|
23
|
+
- JSON output mode for all commands
|
|
24
|
+
- Comprehensive error handling with detailed messages
|
|
25
|
+
- Support for all European banking institutions
|
|
26
|
+
- Premium transaction endpoint support
|
|
27
|
+
- Date range filtering for transactions
|
|
28
|
+
- Pagination support for list commands
|
|
29
|
+
- Environment variable support for credentials
|
|
30
|
+
- Debug mode with detailed stack traces
|
|
31
|
+
- Alias support for common commands (acc, inst, eua, req, pay)
|
|
32
|
+
|
|
33
|
+
### Documentation
|
|
34
|
+
- Complete README with installation and usage
|
|
35
|
+
- AGENT.md with AI agent integration patterns
|
|
36
|
+
- OPENCLAW.md with OpenClaw integration guide
|
|
37
|
+
- EXAMPLES.md with practical usage examples
|
|
38
|
+
- CONTRIBUTING.md with development guidelines
|
|
39
|
+
- Comprehensive JSDoc comments throughout codebase
|
|
40
|
+
|
|
41
|
+
### Testing
|
|
42
|
+
- Unit tests for API client
|
|
43
|
+
- Test infrastructure with Node.js test runner
|
|
44
|
+
- ESLint configuration for code quality
|
|
45
|
+
|
|
46
|
+
### Security
|
|
47
|
+
- Secure config file with 0600 permissions
|
|
48
|
+
- Credential redaction in output
|
|
49
|
+
- Token expiry handling
|
|
50
|
+
- Automatic token refresh
|
|
51
|
+
|
|
52
|
+
## [Unreleased]
|
|
53
|
+
|
|
54
|
+
### Planned Features
|
|
55
|
+
- Transaction export to multiple formats (CSV, Excel, QIF)
|
|
56
|
+
- Account balance tracking over time
|
|
57
|
+
- Transaction categorization with custom rules
|
|
58
|
+
- Multi-account aggregation commands
|
|
59
|
+
- Budget tracking and alerts
|
|
60
|
+
- Recurring transaction detection
|
|
61
|
+
- Data visualization commands
|
|
62
|
+
- Shell completion (bash, zsh, fish)
|
|
63
|
+
- Interactive mode
|
|
64
|
+
- Configuration profiles for multiple users
|
|
65
|
+
- Webhook support for real-time updates
|
|
66
|
+
- Integration with accounting software
|
|
67
|
+
- OpenClaw server mode
|
|
68
|
+
- Docker container support
|
|
69
|
+
- Binary releases for all platforms
|