@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.
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Institution Commands
3
+ *
4
+ * @fileoverview Commands for browsing and searching financial institutions
5
+ * @module commands/institutions
6
+ */
7
+
8
+ import { Command } from 'commander';
9
+ import chalk from 'chalk';
10
+ import ora from 'ora';
11
+ import { createClient } from '../lib/api.js';
12
+ import { ensureAuth } from '../lib/auth.js';
13
+ import {
14
+ printJSON,
15
+ printError,
16
+ printSection,
17
+ printRow,
18
+ truncate,
19
+ } from '../lib/output.js';
20
+
21
+ export const institutionsCommand = new Command('institutions')
22
+ .description('Browse and search financial institutions')
23
+ .alias('inst');
24
+
25
+ // List institutions
26
+ institutionsCommand
27
+ .command('list')
28
+ .description('List supported institutions')
29
+ .requiredOption('-c, --country <code>', 'ISO 3166 country code (e.g., GB, DE, FR)')
30
+ .option('--payments', 'Filter institutions supporting payments')
31
+ .option('--account-selection', 'Filter institutions supporting account selection')
32
+ .option('-j, --json', 'Output as JSON')
33
+ .action(async (options) => {
34
+ const spinner = ora('Fetching institutions...').start();
35
+
36
+ try {
37
+ await ensureAuth();
38
+ const api = createClient();
39
+
40
+ const params = {};
41
+ if (options.payments) params.payments_enabled = true;
42
+ if (options.accountSelection) params.account_selection = true;
43
+
44
+ const institutions = await api.listInstitutions(options.country, params);
45
+
46
+ spinner.succeed(`Found ${institutions.length} institutions`);
47
+
48
+ if (options.json) {
49
+ printJSON(institutions);
50
+ } else {
51
+ printSection(`Institutions in ${options.country.toUpperCase()}`);
52
+
53
+ institutions.forEach((inst, index) => {
54
+ console.log(
55
+ chalk.cyan(`${(index + 1).toString().padStart(3)}.`),
56
+ chalk.bold(inst.name),
57
+ chalk.gray(`(${inst.id})`)
58
+ );
59
+
60
+ if (inst.bic) {
61
+ console.log(' BIC:', inst.bic);
62
+ }
63
+
64
+ const features = [];
65
+ if (inst.transaction_total_days) {
66
+ features.push(`${inst.transaction_total_days} days history`);
67
+ }
68
+ if (inst.payments_enabled) {
69
+ features.push('Payments');
70
+ }
71
+ if (inst.account_selection_supported) {
72
+ features.push('Account Selection');
73
+ }
74
+
75
+ if (features.length > 0) {
76
+ console.log(' Features:', chalk.green(features.join(', ')));
77
+ }
78
+
79
+ console.log();
80
+ });
81
+ }
82
+ } catch (error) {
83
+ spinner.fail('Failed to fetch institutions');
84
+ printError(error.message, error);
85
+ process.exit(1);
86
+ }
87
+ });
88
+
89
+ // Get institution details
90
+ institutionsCommand
91
+ .command('get')
92
+ .description('Get institution details')
93
+ .argument('<institution-id>', 'Institution ID')
94
+ .option('-j, --json', 'Output as JSON')
95
+ .action(async (institutionId, options) => {
96
+ const spinner = ora('Fetching institution...').start();
97
+
98
+ try {
99
+ await ensureAuth();
100
+ const api = createClient();
101
+ const institution = await api.getInstitution(institutionId);
102
+
103
+ spinner.succeed('Institution retrieved');
104
+
105
+ if (options.json) {
106
+ printJSON(institution);
107
+ } else {
108
+ printSection('Institution Details');
109
+
110
+ printRow('ID', institution.id);
111
+ printRow('Name', institution.name);
112
+ if (institution.bic) printRow('BIC', institution.bic);
113
+ if (institution.countries) printRow('Countries', institution.countries.join(', '));
114
+ if (institution.logo) printRow('Logo', institution.logo);
115
+
116
+ printSection('Features');
117
+ printRow('Transaction History', institution.transaction_total_days ? `${institution.transaction_total_days} days` : 'N/A');
118
+ printRow('Payments', institution.payments_enabled ? chalk.green('Supported') : chalk.gray('Not supported'));
119
+ printRow('Account Selection', institution.account_selection_supported ? chalk.green('Supported') : chalk.gray('Not supported'));
120
+
121
+ if (institution.supported_payments) {
122
+ printRow('Payment Types', institution.supported_payments.join(', '));
123
+ }
124
+ }
125
+ } catch (error) {
126
+ spinner.fail('Failed to fetch institution');
127
+ printError(error.message, error);
128
+ process.exit(1);
129
+ }
130
+ });
131
+
132
+ // Search institutions
133
+ institutionsCommand
134
+ .command('search')
135
+ .description('Search for institutions by name')
136
+ .argument('<query>', 'Search query')
137
+ .requiredOption('-c, --country <code>', 'ISO 3166 country code')
138
+ .option('-j, --json', 'Output as JSON')
139
+ .action(async (query, options) => {
140
+ const spinner = ora('Searching institutions...').start();
141
+
142
+ try {
143
+ await ensureAuth();
144
+ const api = createClient();
145
+ const institutions = await api.listInstitutions(options.country);
146
+
147
+ // Filter by name
148
+ const results = institutions.filter((inst) =>
149
+ inst.name.toLowerCase().includes(query.toLowerCase())
150
+ );
151
+
152
+ spinner.succeed(`Found ${results.length} matching institutions`);
153
+
154
+ if (options.json) {
155
+ printJSON(results);
156
+ } else {
157
+ if (results.length === 0) {
158
+ console.log(chalk.yellow('No institutions found matching query'));
159
+ return;
160
+ }
161
+
162
+ printSection('Search Results');
163
+
164
+ results.forEach((inst, index) => {
165
+ console.log(
166
+ chalk.cyan(`${(index + 1).toString().padStart(3)}.`),
167
+ chalk.bold(inst.name),
168
+ chalk.gray(`(${inst.id})`)
169
+ );
170
+ if (inst.bic) {
171
+ console.log(' BIC:', inst.bic);
172
+ }
173
+ console.log();
174
+ });
175
+ }
176
+ } catch (error) {
177
+ spinner.fail('Search failed');
178
+ printError(error.message, error);
179
+ process.exit(1);
180
+ }
181
+ });
@@ -0,0 +1,228 @@
1
+ /**
2
+ * Payment Commands
3
+ *
4
+ * @fileoverview Commands for managing payments and creditors
5
+ * @module commands/payments
6
+ */
7
+
8
+ import { Command } from 'commander';
9
+ import chalk from 'chalk';
10
+ import ora from 'ora';
11
+ import { createClient } from '../lib/api.js';
12
+ import { ensureAuth } from '../lib/auth.js';
13
+ import {
14
+ printJSON,
15
+ printSuccess,
16
+ printError,
17
+ printSection,
18
+ printRow,
19
+ formatStatus,
20
+ formatDateString,
21
+ } from '../lib/output.js';
22
+
23
+ export const paymentsCommand = new Command('payments')
24
+ .description('Manage payments and creditors')
25
+ .alias('pay');
26
+
27
+ // List payments
28
+ paymentsCommand
29
+ .command('list')
30
+ .description('List all payments')
31
+ .option('--limit <number>', 'Results per page', '100')
32
+ .option('--offset <number>', 'Pagination offset', '0')
33
+ .option('-j, --json', 'Output as JSON')
34
+ .action(async (options) => {
35
+ const spinner = ora('Fetching payments...').start();
36
+
37
+ try {
38
+ await ensureAuth();
39
+ const api = createClient();
40
+
41
+ const params = {
42
+ limit: parseInt(options.limit),
43
+ offset: parseInt(options.offset),
44
+ };
45
+
46
+ const data = await api.listPayments(params);
47
+
48
+ spinner.succeed(`Found ${data.count || data.results?.length || 0} payments`);
49
+
50
+ if (options.json) {
51
+ printJSON(data);
52
+ } else {
53
+ const payments = data.results || [];
54
+
55
+ if (payments.length === 0) {
56
+ console.log(chalk.yellow('No payments found'));
57
+ return;
58
+ }
59
+
60
+ printSection('Payments');
61
+
62
+ payments.forEach((payment, index) => {
63
+ console.log(chalk.cyan(`${index + 1}.`), chalk.bold(payment.id));
64
+ console.log(' Status:', formatStatus(payment.status));
65
+ console.log(' Created:', formatDateString(payment.created));
66
+
67
+ if (payment.amount && payment.currency) {
68
+ console.log(' Amount:', `${payment.amount} ${payment.currency}`);
69
+ }
70
+
71
+ console.log();
72
+ });
73
+
74
+ if (data.next) {
75
+ console.log(chalk.gray('More results available. Use --offset to paginate.'));
76
+ }
77
+ }
78
+ } catch (error) {
79
+ spinner.fail('Failed to fetch payments');
80
+ printError(error.message, error);
81
+ process.exit(1);
82
+ }
83
+ });
84
+
85
+ // Get payment
86
+ paymentsCommand
87
+ .command('get')
88
+ .description('Get payment details')
89
+ .argument('<payment-id>', 'Payment UUID')
90
+ .option('-j, --json', 'Output as JSON')
91
+ .action(async (paymentId, options) => {
92
+ const spinner = ora('Fetching payment...').start();
93
+
94
+ try {
95
+ await ensureAuth();
96
+ const api = createClient();
97
+ const payment = await api.getPayment(paymentId);
98
+
99
+ spinner.succeed('Payment retrieved');
100
+
101
+ if (options.json) {
102
+ printJSON(payment);
103
+ } else {
104
+ printSection('Payment Details');
105
+ printRow('Payment ID', payment.id);
106
+ printRow('Status', formatStatus(payment.status));
107
+ printRow('Created', formatDateString(payment.created));
108
+
109
+ if (payment.amount && payment.currency) {
110
+ printRow('Amount', `${payment.amount} ${payment.currency}`);
111
+ }
112
+ }
113
+ } catch (error) {
114
+ spinner.fail('Failed to fetch payment');
115
+ printError(error.message, error);
116
+ process.exit(1);
117
+ }
118
+ });
119
+
120
+ // Delete payment
121
+ paymentsCommand
122
+ .command('delete')
123
+ .description('Delete a periodic payment')
124
+ .argument('<payment-id>', 'Payment UUID')
125
+ .option('-y, --yes', 'Skip confirmation')
126
+ .action(async (paymentId, options) => {
127
+ if (!options.yes) {
128
+ console.log(chalk.yellow('Warning: This will permanently delete the payment'));
129
+ console.log('Use --yes to confirm deletion');
130
+ process.exit(0);
131
+ }
132
+
133
+ const spinner = ora('Deleting payment...').start();
134
+
135
+ try {
136
+ await ensureAuth();
137
+ const api = createClient();
138
+ await api.deletePayment(paymentId);
139
+
140
+ spinner.succeed('Payment deleted');
141
+ printSuccess('Payment deleted successfully');
142
+ } catch (error) {
143
+ spinner.fail('Failed to delete payment');
144
+ printError(error.message, error);
145
+ process.exit(1);
146
+ }
147
+ });
148
+
149
+ // List creditors
150
+ paymentsCommand
151
+ .command('creditors')
152
+ .description('List payment creditors')
153
+ .option('-j, --json', 'Output as JSON')
154
+ .action(async (options) => {
155
+ const spinner = ora('Fetching creditors...').start();
156
+
157
+ try {
158
+ await ensureAuth();
159
+ const api = createClient();
160
+ const data = await api.listCreditors();
161
+
162
+ spinner.succeed(`Found ${data.count || data.results?.length || 0} creditors`);
163
+
164
+ if (options.json) {
165
+ printJSON(data);
166
+ } else {
167
+ const creditors = data.results || [];
168
+
169
+ if (creditors.length === 0) {
170
+ console.log(chalk.yellow('No creditors found'));
171
+ return;
172
+ }
173
+
174
+ printSection('Payment Creditors');
175
+
176
+ creditors.forEach((creditor, index) => {
177
+ console.log(chalk.cyan(`${index + 1}.`), chalk.bold(creditor.name || creditor.id));
178
+ console.log(' ID:', creditor.id);
179
+
180
+ if (creditor.account) {
181
+ console.log(' Account:', creditor.account);
182
+ }
183
+
184
+ console.log();
185
+ });
186
+ }
187
+ } catch (error) {
188
+ spinner.fail('Failed to fetch creditors');
189
+ printError(error.message, error);
190
+ process.exit(1);
191
+ }
192
+ });
193
+
194
+ // Get payment fields
195
+ paymentsCommand
196
+ .command('fields')
197
+ .description('Get required payment fields for institution')
198
+ .argument('<institution-id>', 'Institution ID')
199
+ .option('-j, --json', 'Output as JSON')
200
+ .action(async (institutionId, options) => {
201
+ const spinner = ora('Fetching payment fields...').start();
202
+
203
+ try {
204
+ await ensureAuth();
205
+ const api = createClient();
206
+ const fields = await api.getPaymentFields(institutionId);
207
+
208
+ spinner.succeed('Payment fields retrieved');
209
+
210
+ if (options.json) {
211
+ printJSON(fields);
212
+ } else {
213
+ printSection(`Required Payment Fields for ${institutionId}`);
214
+
215
+ if (Array.isArray(fields)) {
216
+ fields.forEach((field, index) => {
217
+ console.log(chalk.cyan(`${index + 1}.`), field);
218
+ });
219
+ } else {
220
+ console.log(chalk.yellow('No specific fields required'));
221
+ }
222
+ }
223
+ } catch (error) {
224
+ spinner.fail('Failed to fetch payment fields');
225
+ printError(error.message, error);
226
+ process.exit(1);
227
+ }
228
+ });
@@ -0,0 +1,239 @@
1
+ /**
2
+ * Requisition Commands
3
+ *
4
+ * @fileoverview Commands for managing account requisitions
5
+ * @module commands/requisitions
6
+ */
7
+
8
+ import { Command } from 'commander';
9
+ import chalk from 'chalk';
10
+ import ora from 'ora';
11
+ import { createClient } from '../lib/api.js';
12
+ import { ensureAuth } from '../lib/auth.js';
13
+ import {
14
+ printJSON,
15
+ printSuccess,
16
+ printError,
17
+ printSection,
18
+ printRow,
19
+ formatDateString,
20
+ formatStatus,
21
+ } from '../lib/output.js';
22
+
23
+ export const requisitionsCommand = new Command('requisitions')
24
+ .description('Manage account requisitions')
25
+ .alias('req');
26
+
27
+ // List requisitions
28
+ requisitionsCommand
29
+ .command('list')
30
+ .description('List all requisitions')
31
+ .option('--limit <number>', 'Results per page', '100')
32
+ .option('--offset <number>', 'Pagination offset', '0')
33
+ .option('-j, --json', 'Output as JSON')
34
+ .action(async (options) => {
35
+ const spinner = ora('Fetching requisitions...').start();
36
+
37
+ try {
38
+ await ensureAuth();
39
+ const api = createClient();
40
+
41
+ const params = {
42
+ limit: parseInt(options.limit),
43
+ offset: parseInt(options.offset),
44
+ };
45
+
46
+ const data = await api.listRequisitions(params);
47
+
48
+ spinner.succeed(`Found ${data.count || data.results?.length || 0} requisitions`);
49
+
50
+ if (options.json) {
51
+ printJSON(data);
52
+ } else {
53
+ const requisitions = data.results || [];
54
+
55
+ if (requisitions.length === 0) {
56
+ console.log(chalk.yellow('No requisitions found'));
57
+ return;
58
+ }
59
+
60
+ printSection('Requisitions');
61
+
62
+ requisitions.forEach((req, index) => {
63
+ console.log(chalk.cyan(`${index + 1}.`), chalk.bold(req.id));
64
+ console.log(' Status:', formatStatus(req.status));
65
+ console.log(' Institution:', req.institution_id);
66
+ console.log(' Created:', formatDateString(req.created));
67
+
68
+ if (req.reference) {
69
+ console.log(' Reference:', req.reference);
70
+ }
71
+
72
+ if (req.accounts && req.accounts.length > 0) {
73
+ console.log(' Accounts:', req.accounts.join(', '));
74
+ }
75
+
76
+ if (req.link) {
77
+ console.log(' Auth Link:', chalk.blue(req.link));
78
+ }
79
+
80
+ console.log();
81
+ });
82
+
83
+ if (data.next) {
84
+ console.log(chalk.gray('More results available. Use --offset to paginate.'));
85
+ }
86
+ }
87
+ } catch (error) {
88
+ spinner.fail('Failed to fetch requisitions');
89
+ printError(error.message, error);
90
+ process.exit(1);
91
+ }
92
+ });
93
+
94
+ // Create requisition
95
+ requisitionsCommand
96
+ .command('create')
97
+ .description('Create a new requisition')
98
+ .requiredOption('-i, --institution-id <id>', 'Institution ID')
99
+ .requiredOption('-r, --redirect <url>', 'Redirect URL after authentication')
100
+ .option('--reference <ref>', 'Custom reference identifier')
101
+ .option('--agreement <id>', 'Agreement UUID')
102
+ .option('--language <code>', 'User language code (e.g., en, de, fr)')
103
+ .option('--account-selection', 'Enable account selection')
104
+ .option('--redirect-immediate', 'Redirect immediately after auth')
105
+ .option('-j, --json', 'Output as JSON')
106
+ .action(async (options) => {
107
+ const spinner = ora('Creating requisition...').start();
108
+
109
+ try {
110
+ await ensureAuth();
111
+ const api = createClient();
112
+
113
+ const data = {
114
+ institution_id: options.institutionId,
115
+ redirect: options.redirect,
116
+ };
117
+
118
+ if (options.reference) data.reference = options.reference;
119
+ if (options.agreement) data.agreement = options.agreement;
120
+ if (options.language) data.user_language = options.language;
121
+ if (options.accountSelection) data.account_selection = true;
122
+ if (options.redirectImmediate) data.redirect_immediate = true;
123
+
124
+ const requisition = await api.createRequisition(data);
125
+
126
+ spinner.succeed('Requisition created');
127
+
128
+ if (options.json) {
129
+ printJSON(requisition);
130
+ } else {
131
+ printSection('Requisition Created');
132
+ printRow('Requisition ID', requisition.id);
133
+ printRow('Status', formatStatus(requisition.status));
134
+ printRow('Institution', requisition.institution_id);
135
+
136
+ if (requisition.reference) {
137
+ printRow('Reference', requisition.reference);
138
+ }
139
+
140
+ console.log();
141
+ printSuccess('Requisition created successfully');
142
+
143
+ if (requisition.link) {
144
+ console.log();
145
+ console.log(chalk.bold('Authentication Link:'));
146
+ console.log(chalk.blue(requisition.link));
147
+ console.log();
148
+ console.log(chalk.gray('Send this link to the end user to authorize access'));
149
+ }
150
+ }
151
+ } catch (error) {
152
+ spinner.fail('Failed to create requisition');
153
+ printError(error.message, error);
154
+ process.exit(1);
155
+ }
156
+ });
157
+
158
+ // Get requisition
159
+ requisitionsCommand
160
+ .command('get')
161
+ .description('Get requisition details')
162
+ .argument('<requisition-id>', 'Requisition UUID')
163
+ .option('-j, --json', 'Output as JSON')
164
+ .action(async (requisitionId, options) => {
165
+ const spinner = ora('Fetching requisition...').start();
166
+
167
+ try {
168
+ await ensureAuth();
169
+ const api = createClient();
170
+ const requisition = await api.getRequisition(requisitionId);
171
+
172
+ spinner.succeed('Requisition retrieved');
173
+
174
+ if (options.json) {
175
+ printJSON(requisition);
176
+ } else {
177
+ printSection('Requisition Details');
178
+ printRow('Requisition ID', requisition.id);
179
+ printRow('Status', formatStatus(requisition.status));
180
+ printRow('Institution', requisition.institution_id);
181
+ printRow('Created', formatDateString(requisition.created));
182
+
183
+ if (requisition.reference) {
184
+ printRow('Reference', requisition.reference);
185
+ }
186
+
187
+ if (requisition.agreement) {
188
+ printRow('Agreement', requisition.agreement);
189
+ }
190
+
191
+ if (requisition.accounts && requisition.accounts.length > 0) {
192
+ console.log();
193
+ console.log(chalk.bold('Connected Accounts:'));
194
+ requisition.accounts.forEach((accountId, index) => {
195
+ console.log(chalk.cyan(` ${index + 1}.`), accountId);
196
+ });
197
+ }
198
+
199
+ if (requisition.link) {
200
+ console.log();
201
+ console.log(chalk.bold('Authentication Link:'));
202
+ console.log(chalk.blue(requisition.link));
203
+ }
204
+ }
205
+ } catch (error) {
206
+ spinner.fail('Failed to fetch requisition');
207
+ printError(error.message, error);
208
+ process.exit(1);
209
+ }
210
+ });
211
+
212
+ // Delete requisition
213
+ requisitionsCommand
214
+ .command('delete')
215
+ .description('Delete a requisition')
216
+ .argument('<requisition-id>', 'Requisition UUID')
217
+ .option('-y, --yes', 'Skip confirmation')
218
+ .action(async (requisitionId, options) => {
219
+ if (!options.yes) {
220
+ console.log(chalk.yellow('Warning: This will permanently delete the requisition'));
221
+ console.log('Use --yes to confirm deletion');
222
+ process.exit(0);
223
+ }
224
+
225
+ const spinner = ora('Deleting requisition...').start();
226
+
227
+ try {
228
+ await ensureAuth();
229
+ const api = createClient();
230
+ await api.deleteRequisition(requisitionId);
231
+
232
+ spinner.succeed('Requisition deleted');
233
+ printSuccess('Requisition deleted successfully');
234
+ } catch (error) {
235
+ spinner.fail('Failed to delete requisition');
236
+ printError(error.message, error);
237
+ process.exit(1);
238
+ }
239
+ });