@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,205 @@
1
+ /**
2
+ * Account Commands
3
+ *
4
+ * @fileoverview Commands for managing and retrieving account data
5
+ * @module commands/accounts
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
+ printAccountSummary,
20
+ printTransaction,
21
+ formatCurrency,
22
+ formatDateString,
23
+ formatStatus,
24
+ } from '../lib/output.js';
25
+
26
+ export const accountsCommand = new Command('accounts')
27
+ .description('Account information and data retrieval')
28
+ .alias('acc');
29
+
30
+ // Get account metadata
31
+ accountsCommand
32
+ .command('get')
33
+ .description('Get account metadata')
34
+ .argument('<account-id>', 'Account UUID')
35
+ .option('-j, --json', 'Output as JSON')
36
+ .action(async (accountId, options) => {
37
+ const spinner = ora('Fetching account...').start();
38
+
39
+ try {
40
+ await ensureAuth();
41
+ const api = createClient();
42
+ const account = await api.getAccount(accountId);
43
+
44
+ spinner.succeed('Account retrieved');
45
+
46
+ if (options.json) {
47
+ printJSON(account);
48
+ } else {
49
+ printAccountSummary(account);
50
+ }
51
+ } catch (error) {
52
+ spinner.fail('Failed to fetch account');
53
+ printError(error.message, error);
54
+ process.exit(1);
55
+ }
56
+ });
57
+
58
+ // Get account balances
59
+ accountsCommand
60
+ .command('balances')
61
+ .description('Get account balances')
62
+ .argument('<account-id>', 'Account UUID')
63
+ .option('-j, --json', 'Output as JSON')
64
+ .action(async (accountId, options) => {
65
+ const spinner = ora('Fetching balances...').start();
66
+
67
+ try {
68
+ await ensureAuth();
69
+ const api = createClient();
70
+ const data = await api.getAccountBalances(accountId);
71
+
72
+ spinner.succeed('Balances retrieved');
73
+
74
+ if (options.json) {
75
+ printJSON(data);
76
+ } else {
77
+ printSection('Account Balances');
78
+
79
+ if (data.balances && data.balances.length > 0) {
80
+ data.balances.forEach((balance) => {
81
+ const amount = balance.balanceAmount || {};
82
+ const type = balance.balanceType || 'N/A';
83
+ console.log(
84
+ chalk.cyan(type.padEnd(20)),
85
+ formatCurrency(amount.amount, amount.currency),
86
+ balance.referenceDate ? chalk.gray(`(${formatDateString(balance.referenceDate)})`) : ''
87
+ );
88
+ });
89
+ } else {
90
+ console.log(chalk.yellow('No balance information available'));
91
+ }
92
+ }
93
+ } catch (error) {
94
+ spinner.fail('Failed to fetch balances');
95
+ printError(error.message, error);
96
+ process.exit(1);
97
+ }
98
+ });
99
+
100
+ // Get account details
101
+ accountsCommand
102
+ .command('details')
103
+ .description('Get account details')
104
+ .argument('<account-id>', 'Account UUID')
105
+ .option('-j, --json', 'Output as JSON')
106
+ .action(async (accountId, options) => {
107
+ const spinner = ora('Fetching account details...').start();
108
+
109
+ try {
110
+ await ensureAuth();
111
+ const api = createClient();
112
+ const data = await api.getAccountDetails(accountId);
113
+
114
+ spinner.succeed('Account details retrieved');
115
+
116
+ if (options.json) {
117
+ printJSON(data);
118
+ } else {
119
+ printSection('Account Details');
120
+
121
+ const account = data.account || {};
122
+ if (account.iban) printRow('IBAN', account.iban);
123
+ if (account.bban) printRow('BBAN', account.bban);
124
+ if (account.name) printRow('Name', account.name);
125
+ if (account.product) printRow('Product', account.product);
126
+ if (account.cashAccountType) printRow('Type', account.cashAccountType);
127
+ if (account.currency) printRow('Currency', account.currency);
128
+ if (account.ownerName) printRow('Owner', account.ownerName);
129
+ }
130
+ } catch (error) {
131
+ spinner.fail('Failed to fetch account details');
132
+ printError(error.message, error);
133
+ process.exit(1);
134
+ }
135
+ });
136
+
137
+ // Get account transactions
138
+ accountsCommand
139
+ .command('transactions')
140
+ .description('Get account transactions')
141
+ .argument('<account-id>', 'Account UUID')
142
+ .option('--from <date>', 'Start date (YYYY-MM-DD)')
143
+ .option('--to <date>', 'End date (YYYY-MM-DD)')
144
+ .option('--premium', 'Use premium endpoint')
145
+ .option('--country <code>', 'Country code (required for premium)')
146
+ .option('-j, --json', 'Output as JSON')
147
+ .action(async (accountId, options) => {
148
+ const spinner = ora('Fetching transactions...').start();
149
+
150
+ try {
151
+ await ensureAuth();
152
+ const api = createClient();
153
+
154
+ const params = {};
155
+ if (options.from) params.date_from = options.from;
156
+ if (options.to) params.date_to = options.to;
157
+
158
+ let data;
159
+ if (options.premium) {
160
+ if (!options.country) {
161
+ spinner.fail('Country code required for premium transactions');
162
+ printError('Use --country <code> to specify country');
163
+ process.exit(1);
164
+ }
165
+ data = await api.getPremiumTransactions(accountId, options.country, params);
166
+ } else {
167
+ data = await api.getAccountTransactions(accountId, params);
168
+ }
169
+
170
+ spinner.succeed('Transactions retrieved');
171
+
172
+ if (options.json) {
173
+ printJSON(data);
174
+ } else {
175
+ printSection('Transactions');
176
+
177
+ const transactions = data.transactions?.booked || [];
178
+ const pending = data.transactions?.pending || [];
179
+
180
+ if (transactions.length > 0) {
181
+ console.log(chalk.bold('Booked Transactions:'));
182
+ console.log();
183
+ transactions.forEach((tx) => printTransaction(tx));
184
+ }
185
+
186
+ if (pending.length > 0) {
187
+ console.log();
188
+ console.log(chalk.bold('Pending Transactions:'));
189
+ console.log();
190
+ pending.forEach((tx) => printTransaction(tx));
191
+ }
192
+
193
+ if (transactions.length === 0 && pending.length === 0) {
194
+ console.log(chalk.yellow('No transactions found'));
195
+ }
196
+
197
+ console.log();
198
+ console.log(chalk.gray(`Total: ${transactions.length} booked, ${pending.length} pending`));
199
+ }
200
+ } catch (error) {
201
+ spinner.fail('Failed to fetch transactions');
202
+ printError(error.message, error);
203
+ process.exit(1);
204
+ }
205
+ });
@@ -0,0 +1,241 @@
1
+ /**
2
+ * End User Agreement Commands
3
+ *
4
+ * @fileoverview Commands for managing end user agreements (EUAs)
5
+ * @module commands/agreements
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 agreementsCommand = new Command('agreements')
24
+ .description('Manage end user agreements (EUAs)')
25
+ .alias('eua');
26
+
27
+ // List agreements
28
+ agreementsCommand
29
+ .command('list')
30
+ .description('List all end user agreements')
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 agreements...').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.listAgreements(params);
47
+
48
+ spinner.succeed(`Found ${data.count || data.results?.length || 0} agreements`);
49
+
50
+ if (options.json) {
51
+ printJSON(data);
52
+ } else {
53
+ const agreements = data.results || [];
54
+
55
+ if (agreements.length === 0) {
56
+ console.log(chalk.yellow('No agreements found'));
57
+ return;
58
+ }
59
+
60
+ printSection('End User Agreements');
61
+
62
+ agreements.forEach((agreement, index) => {
63
+ console.log(chalk.cyan(`${index + 1}.`), chalk.bold(agreement.id));
64
+ console.log(' Institution:', agreement.institution_id);
65
+ console.log(' Created:', formatDateString(agreement.created));
66
+ console.log(' Max Historical Days:', agreement.max_historical_days);
67
+ console.log(' Access Valid For:', agreement.access_valid_for_days, 'days');
68
+ console.log(' Status:', formatStatus(agreement.accepted ? 'ACCEPTED' : 'PENDING'));
69
+
70
+ if (agreement.access_scope && agreement.access_scope.length > 0) {
71
+ console.log(' Access Scope:', agreement.access_scope.join(', '));
72
+ }
73
+
74
+ console.log();
75
+ });
76
+
77
+ if (data.next) {
78
+ console.log(chalk.gray('More results available. Use --offset to paginate.'));
79
+ }
80
+ }
81
+ } catch (error) {
82
+ spinner.fail('Failed to fetch agreements');
83
+ printError(error.message, error);
84
+ process.exit(1);
85
+ }
86
+ });
87
+
88
+ // Create agreement
89
+ agreementsCommand
90
+ .command('create')
91
+ .description('Create a new end user agreement')
92
+ .requiredOption('-i, --institution-id <id>', 'Institution ID')
93
+ .option('--max-days <days>', 'Maximum historical days', '90')
94
+ .option('--valid-days <days>', 'Access valid for days', '90')
95
+ .option('--scope <scopes...>', 'Access scope (balances, details, transactions)')
96
+ .option('-j, --json', 'Output as JSON')
97
+ .action(async (options) => {
98
+ const spinner = ora('Creating agreement...').start();
99
+
100
+ try {
101
+ await ensureAuth();
102
+ const api = createClient();
103
+
104
+ const data = {
105
+ institution_id: options.institutionId,
106
+ max_historical_days: parseInt(options.maxDays),
107
+ access_valid_for_days: parseInt(options.validDays),
108
+ };
109
+
110
+ if (options.scope) {
111
+ data.access_scope = options.scope;
112
+ }
113
+
114
+ const agreement = await api.createAgreement(data);
115
+
116
+ spinner.succeed('Agreement created');
117
+
118
+ if (options.json) {
119
+ printJSON(agreement);
120
+ } else {
121
+ printSection('Agreement Created');
122
+ printRow('Agreement ID', agreement.id);
123
+ printRow('Institution', agreement.institution_id);
124
+ printRow('Max Historical Days', agreement.max_historical_days);
125
+ printRow('Access Valid For', `${agreement.access_valid_for_days} days`);
126
+
127
+ if (agreement.access_scope) {
128
+ printRow('Access Scope', agreement.access_scope.join(', '));
129
+ }
130
+
131
+ printSuccess('Agreement created successfully');
132
+ }
133
+ } catch (error) {
134
+ spinner.fail('Failed to create agreement');
135
+ printError(error.message, error);
136
+ process.exit(1);
137
+ }
138
+ });
139
+
140
+ // Get agreement
141
+ agreementsCommand
142
+ .command('get')
143
+ .description('Get agreement details')
144
+ .argument('<agreement-id>', 'Agreement UUID')
145
+ .option('-j, --json', 'Output as JSON')
146
+ .action(async (agreementId, options) => {
147
+ const spinner = ora('Fetching agreement...').start();
148
+
149
+ try {
150
+ await ensureAuth();
151
+ const api = createClient();
152
+ const agreement = await api.getAgreement(agreementId);
153
+
154
+ spinner.succeed('Agreement retrieved');
155
+
156
+ if (options.json) {
157
+ printJSON(agreement);
158
+ } else {
159
+ printSection('Agreement Details');
160
+ printRow('Agreement ID', agreement.id);
161
+ printRow('Institution', agreement.institution_id);
162
+ printRow('Created', formatDateString(agreement.created));
163
+ printRow('Max Historical Days', agreement.max_historical_days);
164
+ printRow('Access Valid For', `${agreement.access_valid_for_days} days`);
165
+ printRow('Status', formatStatus(agreement.accepted ? 'ACCEPTED' : 'PENDING'));
166
+
167
+ if (agreement.access_scope) {
168
+ printRow('Access Scope', agreement.access_scope.join(', '));
169
+ }
170
+ }
171
+ } catch (error) {
172
+ spinner.fail('Failed to fetch agreement');
173
+ printError(error.message, error);
174
+ process.exit(1);
175
+ }
176
+ });
177
+
178
+ // Delete agreement
179
+ agreementsCommand
180
+ .command('delete')
181
+ .description('Delete an end user agreement')
182
+ .argument('<agreement-id>', 'Agreement UUID')
183
+ .option('-y, --yes', 'Skip confirmation')
184
+ .action(async (agreementId, options) => {
185
+ if (!options.yes) {
186
+ console.log(chalk.yellow('Warning: This will permanently delete the agreement'));
187
+ console.log('Use --yes to confirm deletion');
188
+ process.exit(0);
189
+ }
190
+
191
+ const spinner = ora('Deleting agreement...').start();
192
+
193
+ try {
194
+ await ensureAuth();
195
+ const api = createClient();
196
+ await api.deleteAgreement(agreementId);
197
+
198
+ spinner.succeed('Agreement deleted');
199
+ printSuccess('Agreement deleted successfully');
200
+ } catch (error) {
201
+ spinner.fail('Failed to delete agreement');
202
+ printError(error.message, error);
203
+ process.exit(1);
204
+ }
205
+ });
206
+
207
+ // Accept agreement
208
+ agreementsCommand
209
+ .command('accept')
210
+ .description('Accept an end user agreement')
211
+ .argument('<agreement-id>', 'Agreement UUID')
212
+ .requiredOption('--user-agent <ua>', 'User agent string')
213
+ .requiredOption('--ip <address>', 'User IP address')
214
+ .option('-j, --json', 'Output as JSON')
215
+ .action(async (agreementId, options) => {
216
+ const spinner = ora('Accepting agreement...').start();
217
+
218
+ try {
219
+ await ensureAuth();
220
+ const api = createClient();
221
+
222
+ const data = {
223
+ user_agent: options.userAgent,
224
+ ip_address: options.ip,
225
+ };
226
+
227
+ const agreement = await api.acceptAgreement(agreementId, data);
228
+
229
+ spinner.succeed('Agreement accepted');
230
+
231
+ if (options.json) {
232
+ printJSON(agreement);
233
+ } else {
234
+ printSuccess('Agreement accepted successfully');
235
+ }
236
+ } catch (error) {
237
+ spinner.fail('Failed to accept agreement');
238
+ printError(error.message, error);
239
+ process.exit(1);
240
+ }
241
+ });
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Authentication Commands
3
+ *
4
+ * @fileoverview Commands for authentication and token management
5
+ * @module commands/auth
6
+ */
7
+
8
+ import { Command } from 'commander';
9
+ import chalk from 'chalk';
10
+ import ora from 'ora';
11
+ import { login, logout, getAuthStatus } from '../lib/auth.js';
12
+ import { printSuccess, printError, printSection, printRow, formatDuration } from '../lib/output.js';
13
+
14
+ export const authCommand = new Command('auth')
15
+ .description('Authentication and token management');
16
+
17
+ // Login command
18
+ authCommand
19
+ .command('login')
20
+ .description('Authenticate with Nordigen API')
21
+ .requiredOption('--secret-id <id>', 'Secret ID from Nordigen dashboard')
22
+ .requiredOption('--secret-key <key>', 'Secret Key from Nordigen dashboard')
23
+ .action(async (options) => {
24
+ const spinner = ora('Authenticating...').start();
25
+
26
+ try {
27
+ const result = await login(options.secretId, options.secretKey);
28
+ spinner.succeed('Authentication successful');
29
+
30
+ printSection('Token Information');
31
+ printRow('Access Token Expires In', formatDuration(result.access_expires));
32
+ printRow('Refresh Token Expires In', formatDuration(result.refresh_expires));
33
+
34
+ printSuccess('Credentials saved securely');
35
+ } catch (error) {
36
+ spinner.fail('Authentication failed');
37
+ printError(error.message, error);
38
+ process.exit(1);
39
+ }
40
+ });
41
+
42
+ // Logout command
43
+ authCommand
44
+ .command('logout')
45
+ .description('Clear stored credentials')
46
+ .action(() => {
47
+ try {
48
+ logout();
49
+ printSuccess('Logged out successfully');
50
+ } catch (error) {
51
+ printError('Logout failed', error);
52
+ process.exit(1);
53
+ }
54
+ });
55
+
56
+ // Status command
57
+ authCommand
58
+ .command('status')
59
+ .description('Show authentication status')
60
+ .action(() => {
61
+ try {
62
+ const status = getAuthStatus();
63
+
64
+ printSection('Authentication Status');
65
+
66
+ if (status.authenticated) {
67
+ printRow('Status', chalk.green('Authenticated'));
68
+ printRow('Access Token', status.accessTokenValid ? chalk.green('Valid') : chalk.yellow('Expired'));
69
+ printRow('Refresh Token', status.refreshTokenValid ? chalk.green('Valid') : chalk.yellow('Expired'));
70
+
71
+ if (status.accessTokenValid) {
72
+ printRow('Access Expires In', formatDuration(status.accessExpiresIn));
73
+ }
74
+ if (status.refreshTokenValid) {
75
+ printRow('Refresh Expires In', formatDuration(status.refreshExpiresIn));
76
+ }
77
+ } else {
78
+ printRow('Status', chalk.red('Not Authenticated'));
79
+ console.log();
80
+ console.log('Login with:', chalk.cyan('nordigen auth login --secret-id <id> --secret-key <key>'));
81
+ }
82
+ } catch (error) {
83
+ printError('Failed to get auth status', error);
84
+ process.exit(1);
85
+ }
86
+ });
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Configuration Commands
3
+ *
4
+ * @fileoverview Commands for managing CLI configuration
5
+ * @module commands/config
6
+ */
7
+
8
+ import { Command } from 'commander';
9
+ import chalk from 'chalk';
10
+ import { getConfig, getConfigPath, clear } from '../lib/config.js';
11
+ import { printJSON, printSuccess, printError, printSection, printRow } from '../lib/output.js';
12
+
13
+ export const configCommand = new Command('config')
14
+ .description('Manage CLI configuration');
15
+
16
+ // Show config
17
+ configCommand
18
+ .command('show')
19
+ .description('Show current configuration')
20
+ .option('-j, --json', 'Output as JSON')
21
+ .option('--show-secrets', 'Show secret values (WARNING: sensitive)')
22
+ .action((options) => {
23
+ try {
24
+ const config = getConfig();
25
+ const data = config.store;
26
+
27
+ if (options.json) {
28
+ if (options.showSecrets) {
29
+ printJSON(data);
30
+ } else {
31
+ // Redact secrets
32
+ const safe = JSON.parse(JSON.stringify(data));
33
+ if (safe.auth) {
34
+ if (safe.auth.secret_key) safe.auth.secret_key = '***REDACTED***';
35
+ if (safe.auth.access_token) safe.auth.access_token = '***REDACTED***';
36
+ if (safe.auth.refresh_token) safe.auth.refresh_token = '***REDACTED***';
37
+ }
38
+ printJSON(safe);
39
+ }
40
+ } else {
41
+ printSection('Configuration');
42
+ printRow('Config File', getConfigPath());
43
+
44
+ console.log();
45
+ console.log(chalk.bold('Authentication:'));
46
+ if (data.auth) {
47
+ printRow(' Secret ID', data.auth.secret_id || 'Not set');
48
+ printRow(' Secret Key', options.showSecrets ? data.auth.secret_key : '***REDACTED***');
49
+ printRow(' Access Token', data.auth.access_token ? (options.showSecrets ? data.auth.access_token : '***REDACTED***') : 'Not set');
50
+ printRow(' Refresh Token', data.auth.refresh_token ? (options.showSecrets ? data.auth.refresh_token : '***REDACTED***') : 'Not set');
51
+ } else {
52
+ console.log(chalk.gray(' Not configured'));
53
+ }
54
+
55
+ console.log();
56
+ console.log(chalk.bold('Defaults:'));
57
+ if (data.defaults) {
58
+ printRow(' Country', data.defaults.country || 'Not set');
59
+ printRow(' Institution ID', data.defaults.institution_id || 'Not set');
60
+ } else {
61
+ console.log(chalk.gray(' Not configured'));
62
+ }
63
+ }
64
+ } catch (error) {
65
+ printError('Failed to show config', error);
66
+ process.exit(1);
67
+ }
68
+ });
69
+
70
+ // Get config value
71
+ configCommand
72
+ .command('get')
73
+ .description('Get configuration value')
74
+ .argument('<key>', 'Configuration key (dot notation, e.g., auth.secret_id)')
75
+ .action((key) => {
76
+ try {
77
+ const config = getConfig();
78
+ const value = config.get(key);
79
+
80
+ if (value === undefined) {
81
+ console.log(chalk.yellow(`Key '${key}' not found`));
82
+ process.exit(1);
83
+ }
84
+
85
+ if (typeof value === 'object') {
86
+ printJSON(value);
87
+ } else {
88
+ console.log(value);
89
+ }
90
+ } catch (error) {
91
+ printError('Failed to get config value', error);
92
+ process.exit(1);
93
+ }
94
+ });
95
+
96
+ // Set config value
97
+ configCommand
98
+ .command('set')
99
+ .description('Set configuration value')
100
+ .argument('<key>', 'Configuration key (dot notation)')
101
+ .argument('<value>', 'Value to set')
102
+ .action((key, value) => {
103
+ try {
104
+ const config = getConfig();
105
+
106
+ // Try to parse as JSON, otherwise use as string
107
+ let parsedValue = value;
108
+ try {
109
+ parsedValue = JSON.parse(value);
110
+ } catch {
111
+ // Keep as string
112
+ }
113
+
114
+ config.set(key, parsedValue);
115
+ printSuccess(`Set ${key} = ${value}`);
116
+ } catch (error) {
117
+ printError('Failed to set config value', error);
118
+ process.exit(1);
119
+ }
120
+ });
121
+
122
+ // Delete config value
123
+ configCommand
124
+ .command('delete')
125
+ .description('Delete configuration value')
126
+ .argument('<key>', 'Configuration key')
127
+ .action((key) => {
128
+ try {
129
+ const config = getConfig();
130
+ config.delete(key);
131
+ printSuccess(`Deleted ${key}`);
132
+ } catch (error) {
133
+ printError('Failed to delete config value', error);
134
+ process.exit(1);
135
+ }
136
+ });
137
+
138
+ // Clear all config
139
+ configCommand
140
+ .command('clear')
141
+ .description('Clear all configuration')
142
+ .option('-y, --yes', 'Skip confirmation')
143
+ .action((options) => {
144
+ if (!options.yes) {
145
+ console.log(chalk.yellow('Warning: This will clear all configuration including credentials'));
146
+ console.log('Use --yes to confirm');
147
+ process.exit(0);
148
+ }
149
+
150
+ try {
151
+ clear();
152
+ printSuccess('Configuration cleared');
153
+ } catch (error) {
154
+ printError('Failed to clear config', error);
155
+ process.exit(1);
156
+ }
157
+ });
158
+
159
+ // Set default country
160
+ configCommand
161
+ .command('set-country')
162
+ .description('Set default country code')
163
+ .argument('<code>', 'ISO 3166 country code (e.g., GB, DE, FR)')
164
+ .action((code) => {
165
+ try {
166
+ const config = getConfig();
167
+ config.set('defaults.country', code.toUpperCase());
168
+ printSuccess(`Default country set to ${code.toUpperCase()}`);
169
+ } catch (error) {
170
+ printError('Failed to set country', error);
171
+ process.exit(1);
172
+ }
173
+ });