@paylobster/cli 4.0.2 → 4.2.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.
Files changed (64) hide show
  1. package/IMPLEMENTATION_SUMMARY.md +288 -0
  2. package/NEW_FEATURES.md +228 -0
  3. package/dist/src/commands/bridge.d.ts +6 -0
  4. package/dist/src/commands/bridge.d.ts.map +1 -0
  5. package/dist/src/commands/bridge.js +273 -0
  6. package/dist/src/commands/bridge.js.map +1 -0
  7. package/dist/src/commands/cascade.d.ts +3 -0
  8. package/dist/src/commands/cascade.d.ts.map +1 -0
  9. package/dist/src/commands/cascade.js +242 -0
  10. package/dist/src/commands/cascade.js.map +1 -0
  11. package/dist/src/commands/compliance.d.ts +3 -0
  12. package/dist/src/commands/compliance.d.ts.map +1 -0
  13. package/dist/src/commands/compliance.js +121 -0
  14. package/dist/src/commands/compliance.js.map +1 -0
  15. package/dist/src/commands/credit-score.d.ts +3 -0
  16. package/dist/src/commands/credit-score.d.ts.map +1 -0
  17. package/dist/src/commands/credit-score.js +174 -0
  18. package/dist/src/commands/credit-score.js.map +1 -0
  19. package/dist/src/commands/dispute.d.ts +3 -0
  20. package/dist/src/commands/dispute.d.ts.map +1 -0
  21. package/dist/src/commands/dispute.js +241 -0
  22. package/dist/src/commands/dispute.js.map +1 -0
  23. package/dist/src/commands/intent.d.ts +3 -0
  24. package/dist/src/commands/intent.d.ts.map +1 -0
  25. package/dist/src/commands/intent.js +227 -0
  26. package/dist/src/commands/intent.js.map +1 -0
  27. package/dist/src/commands/oracle.d.ts +3 -0
  28. package/dist/src/commands/oracle.d.ts.map +1 -0
  29. package/dist/src/commands/oracle.js +114 -0
  30. package/dist/src/commands/oracle.js.map +1 -0
  31. package/dist/src/commands/portfolio.d.ts +6 -0
  32. package/dist/src/commands/portfolio.d.ts.map +1 -0
  33. package/dist/src/commands/portfolio.js +179 -0
  34. package/dist/src/commands/portfolio.js.map +1 -0
  35. package/dist/src/commands/revenue-share.d.ts +3 -0
  36. package/dist/src/commands/revenue-share.d.ts.map +1 -0
  37. package/dist/src/commands/revenue-share.js +185 -0
  38. package/dist/src/commands/revenue-share.js.map +1 -0
  39. package/dist/src/commands/stream.d.ts +3 -0
  40. package/dist/src/commands/stream.d.ts.map +1 -0
  41. package/dist/src/commands/stream.js +213 -0
  42. package/dist/src/commands/stream.js.map +1 -0
  43. package/dist/src/commands/swap.d.ts +6 -0
  44. package/dist/src/commands/swap.d.ts.map +1 -0
  45. package/dist/src/commands/swap.js +278 -0
  46. package/dist/src/commands/swap.js.map +1 -0
  47. package/dist/src/index.js +23 -1
  48. package/dist/src/index.js.map +1 -1
  49. package/dist/src/lib/types.d.ts +1 -0
  50. package/dist/src/lib/types.d.ts.map +1 -1
  51. package/package.json +1 -1
  52. package/src/commands/bridge.ts +443 -0
  53. package/src/commands/cascade.ts +280 -0
  54. package/src/commands/compliance.ts +123 -0
  55. package/src/commands/credit-score.ts +193 -0
  56. package/src/commands/dispute.ts +274 -0
  57. package/src/commands/intent.ts +261 -0
  58. package/src/commands/oracle.ts +116 -0
  59. package/src/commands/portfolio.ts +227 -0
  60. package/src/commands/revenue-share.ts +213 -0
  61. package/src/commands/stream.ts +244 -0
  62. package/src/commands/swap.ts +365 -0
  63. package/src/index.ts +23 -1
  64. package/src/lib/types.ts +1 -0
@@ -0,0 +1,280 @@
1
+ import { Command } from 'commander';
2
+ import { success, error, info, warning, withSpinner, outputJSON, formatAddress, confirm, createTable } from '../lib/display';
3
+ import type { OutputOptions } from '../lib/types';
4
+
5
+ export function createCascadeCommand(): Command {
6
+ const cmd = new Command('cascade')
7
+ .description('Manage cascading payments');
8
+
9
+ // plob cascade create
10
+ cmd
11
+ .command('create')
12
+ .description('Create a cascading payment')
13
+ .requiredOption('--stages <number>', 'Number of stages')
14
+ .requiredOption('--amount <amount>', 'Total amount in USDC')
15
+ .requiredOption('--recipients <addresses>', 'Comma-separated recipient addresses')
16
+ .option('--json', 'Output as JSON')
17
+ .action(async (options: {
18
+ stages: string;
19
+ amount: string;
20
+ recipients: string;
21
+ } & OutputOptions) => {
22
+ try {
23
+ const stages = parseInt(options.stages);
24
+ if (isNaN(stages) || stages < 2) {
25
+ error('Invalid stages. Must be at least 2');
26
+ process.exit(1);
27
+ }
28
+
29
+ const amount = parseFloat(options.amount);
30
+ if (isNaN(amount) || amount <= 0) {
31
+ error('Invalid amount. Must be a positive number');
32
+ process.exit(1);
33
+ }
34
+
35
+ const recipients = options.recipients.split(',').map(r => r.trim());
36
+
37
+ // Validate all addresses
38
+ for (const recipient of recipients) {
39
+ if (!recipient.startsWith('0x') || recipient.length !== 42) {
40
+ error(`Invalid recipient address: ${recipient}`);
41
+ process.exit(1);
42
+ }
43
+ }
44
+
45
+ if (recipients.length !== stages) {
46
+ error(`Number of recipients (${recipients.length}) must match stages (${stages})`);
47
+ process.exit(1);
48
+ }
49
+
50
+ info(`Creating ${stages}-stage cascade`);
51
+ info(`Total amount: ${options.amount} USDC`);
52
+ info(`Recipients: ${recipients.map(formatAddress).join(' → ')}`);
53
+
54
+ const cascadeId = Math.floor(Math.random() * 1000000);
55
+
56
+ const txHash = await withSpinner(
57
+ 'Creating cascade payment...',
58
+ async () => {
59
+ // Placeholder - implement actual contract call
60
+ await new Promise(resolve => setTimeout(resolve, 2000));
61
+ return '0x' + Math.random().toString(16).substring(2, 66);
62
+ }
63
+ );
64
+
65
+ if (outputJSON({
66
+ cascadeId,
67
+ stages,
68
+ amount: options.amount,
69
+ recipients,
70
+ txHash,
71
+ }, options)) {
72
+ return;
73
+ }
74
+
75
+ success('Cascade payment created!');
76
+ info(`Cascade ID: ${cascadeId}`);
77
+ info(`Transaction: ${txHash}`);
78
+ } catch (err) {
79
+ error(`Failed to create cascade: ${err}`);
80
+ process.exit(1);
81
+ }
82
+ });
83
+
84
+ // plob cascade release
85
+ cmd
86
+ .command('release')
87
+ .description('Release a specific cascade level')
88
+ .argument('<cascadeId>', 'Cascade ID')
89
+ .requiredOption('--level <level>', 'Level to release (1-based)')
90
+ .option('--yes', 'Skip confirmation')
91
+ .option('--json', 'Output as JSON')
92
+ .action(async (cascadeId: string, options: {
93
+ level: string;
94
+ yes?: boolean;
95
+ } & OutputOptions) => {
96
+ try {
97
+ const level = parseInt(options.level);
98
+ if (isNaN(level) || level < 1) {
99
+ error('Invalid level. Must be at least 1');
100
+ process.exit(1);
101
+ }
102
+
103
+ if (!options.yes) {
104
+ const confirmed = await confirm(`Release level ${level} of cascade ${cascadeId}?`);
105
+ if (!confirmed) {
106
+ info('Cancelled');
107
+ process.exit(0);
108
+ }
109
+ }
110
+
111
+ const txHash = await withSpinner(
112
+ `Releasing cascade level ${level}...`,
113
+ async () => {
114
+ // Placeholder - implement actual contract call
115
+ await new Promise(resolve => setTimeout(resolve, 1500));
116
+ return '0x' + Math.random().toString(16).substring(2, 66);
117
+ }
118
+ );
119
+
120
+ if (outputJSON({
121
+ cascadeId,
122
+ level,
123
+ txHash,
124
+ }, options)) {
125
+ return;
126
+ }
127
+
128
+ success(`Cascade level ${level} released!`);
129
+ info(`Cascade ID: ${cascadeId}`);
130
+ info(`Transaction: ${txHash}`);
131
+ } catch (err) {
132
+ error(`Failed to release cascade: ${err}`);
133
+ process.exit(1);
134
+ }
135
+ });
136
+
137
+ // plob cascade release-all
138
+ cmd
139
+ .command('release-all')
140
+ .description('Release all remaining cascade levels')
141
+ .argument('<cascadeId>', 'Cascade ID')
142
+ .option('--yes', 'Skip confirmation')
143
+ .option('--json', 'Output as JSON')
144
+ .action(async (cascadeId: string, options: { yes?: boolean } & OutputOptions) => {
145
+ try {
146
+ if (!options.yes) {
147
+ const confirmed = await confirm(`Release ALL levels of cascade ${cascadeId}?`);
148
+ if (!confirmed) {
149
+ info('Cancelled');
150
+ process.exit(0);
151
+ }
152
+ }
153
+
154
+ const txHash = await withSpinner(
155
+ 'Releasing all cascade levels...',
156
+ async () => {
157
+ // Placeholder - implement actual contract call
158
+ await new Promise(resolve => setTimeout(resolve, 2000));
159
+ return '0x' + Math.random().toString(16).substring(2, 66);
160
+ }
161
+ );
162
+
163
+ if (outputJSON({
164
+ cascadeId,
165
+ txHash,
166
+ }, options)) {
167
+ return;
168
+ }
169
+
170
+ success('All cascade levels released!');
171
+ info(`Cascade ID: ${cascadeId}`);
172
+ info(`Transaction: ${txHash}`);
173
+ } catch (err) {
174
+ error(`Failed to release cascade: ${err}`);
175
+ process.exit(1);
176
+ }
177
+ });
178
+
179
+ // plob cascade get
180
+ cmd
181
+ .command('get')
182
+ .description('Get cascade details')
183
+ .argument('<cascadeId>', 'Cascade ID')
184
+ .option('--json', 'Output as JSON')
185
+ .action(async (cascadeId: string, options: OutputOptions) => {
186
+ try {
187
+ // Placeholder data
188
+ const cascade = {
189
+ id: cascadeId,
190
+ creator: '0x1234567890123456789012345678901234567890',
191
+ totalAmount: '1000 USDC',
192
+ stages: 3,
193
+ currentStage: 1,
194
+ recipients: [
195
+ { address: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb', released: true },
196
+ { address: '0x8626f6940E2eb28930eFb4CeF49B2d1F2C9C1199', released: false },
197
+ { address: '0x1111111111111111111111111111111111111111', released: false },
198
+ ],
199
+ };
200
+
201
+ if (outputJSON(cascade, options)) {
202
+ return;
203
+ }
204
+
205
+ console.log();
206
+ console.log('Cascade ID: ', cascade.id);
207
+ console.log('Creator: ', cascade.creator);
208
+ console.log('Total Amount: ', cascade.totalAmount);
209
+ console.log('Stages: ', cascade.stages);
210
+ console.log('Current Stage: ', cascade.currentStage);
211
+ console.log();
212
+ console.log('Recipients:');
213
+ cascade.recipients.forEach((r, i) => {
214
+ const status = r.released ? '✅ Released' : '⏳ Pending';
215
+ console.log(` ${i + 1}. ${formatAddress(r.address)} - ${status}`);
216
+ });
217
+ console.log();
218
+ } catch (err) {
219
+ error(`Failed to get cascade: ${err}`);
220
+ process.exit(1);
221
+ }
222
+ });
223
+
224
+ // plob cascade list
225
+ cmd
226
+ .command('list')
227
+ .description('List your cascades')
228
+ .option('--json', 'Output as JSON')
229
+ .action(async (options: OutputOptions) => {
230
+ try {
231
+ // Placeholder data
232
+ const cascades = [
233
+ {
234
+ id: 1,
235
+ amount: '1000 USDC',
236
+ stages: 3,
237
+ currentStage: 1,
238
+ status: 'active',
239
+ },
240
+ {
241
+ id: 2,
242
+ amount: '500 USDC',
243
+ stages: 2,
244
+ currentStage: 2,
245
+ status: 'completed',
246
+ },
247
+ ];
248
+
249
+ if (outputJSON(cascades, options)) {
250
+ return;
251
+ }
252
+
253
+ if (cascades.length === 0) {
254
+ info('No cascades found');
255
+ return;
256
+ }
257
+
258
+ const table = createTable({
259
+ head: ['ID', 'Amount', 'Stages', 'Current', 'Status'],
260
+ });
261
+
262
+ for (const cascade of cascades) {
263
+ table.push([
264
+ cascade.id,
265
+ cascade.amount,
266
+ cascade.stages,
267
+ cascade.currentStage,
268
+ cascade.status,
269
+ ]);
270
+ }
271
+
272
+ console.log(table.toString());
273
+ } catch (err) {
274
+ error(`Failed to list cascades: ${err}`);
275
+ process.exit(1);
276
+ }
277
+ });
278
+
279
+ return cmd;
280
+ }
@@ -0,0 +1,123 @@
1
+ import { Command } from 'commander';
2
+ import { success, error, info, warning, outputJSON, formatAddress, formatBoolean } from '../lib/display';
3
+ import type { OutputOptions } from '../lib/types';
4
+ import chalk from 'chalk';
5
+
6
+ export function createComplianceCommand(): Command {
7
+ const cmd = new Command('compliance')
8
+ .description('Check compliance and policy status');
9
+
10
+ // plob compliance check
11
+ cmd
12
+ .command('check')
13
+ .description('Check compliance status for an address')
14
+ .argument('<address>', 'Address to check')
15
+ .option('--json', 'Output as JSON')
16
+ .action(async (address: string, options: OutputOptions) => {
17
+ try {
18
+ if (!address.startsWith('0x') || address.length !== 42) {
19
+ error('Invalid address format');
20
+ process.exit(1);
21
+ }
22
+
23
+ info(`Checking compliance for ${formatAddress(address)}`);
24
+
25
+ // Placeholder data
26
+ const compliance = {
27
+ address,
28
+ approved: true,
29
+ sanctioned: false,
30
+ kycVerified: true,
31
+ riskLevel: 'low',
32
+ jurisdiction: 'US',
33
+ lastChecked: new Date().toISOString(),
34
+ policies: [
35
+ { id: 1, name: 'AML Policy', compliant: true },
36
+ { id: 2, name: 'KYC Policy', compliant: true },
37
+ { id: 3, name: 'Sanctions Screening', compliant: true },
38
+ ],
39
+ };
40
+
41
+ if (outputJSON(compliance, options)) {
42
+ return;
43
+ }
44
+
45
+ console.log();
46
+ console.log(chalk.bold.cyan('🦞 Compliance Check'));
47
+ console.log(chalk.gray('─'.repeat(50)));
48
+ console.log('Address: ', compliance.address);
49
+ console.log('Approved: ', formatBoolean(compliance.approved));
50
+ console.log('Sanctioned: ', formatBoolean(compliance.sanctioned));
51
+ console.log('KYC Verified: ', formatBoolean(compliance.kycVerified));
52
+ console.log('Risk Level: ', compliance.riskLevel === 'low' ? chalk.green(compliance.riskLevel) : chalk.yellow(compliance.riskLevel));
53
+ console.log('Jurisdiction: ', compliance.jurisdiction);
54
+ console.log('Last Checked: ', compliance.lastChecked);
55
+ console.log();
56
+ console.log(chalk.bold('Policy Compliance:'));
57
+ compliance.policies.forEach(policy => {
58
+ console.log(` ${formatBoolean(policy.compliant)} ${policy.name}`);
59
+ });
60
+ console.log();
61
+
62
+ if (!compliance.approved) {
63
+ warning('This address is NOT approved for transactions');
64
+ } else {
65
+ success('Address is compliant');
66
+ }
67
+ } catch (err) {
68
+ error(`Failed to check compliance: ${err}`);
69
+ process.exit(1);
70
+ }
71
+ });
72
+
73
+ // plob compliance policy
74
+ cmd
75
+ .command('policy')
76
+ .description('Get policy details')
77
+ .argument('<policyId>', 'Policy ID')
78
+ .option('--json', 'Output as JSON')
79
+ .action(async (policyId: string, options: OutputOptions) => {
80
+ try {
81
+ // Placeholder data
82
+ const policy = {
83
+ id: policyId,
84
+ name: 'AML Policy',
85
+ version: '1.2.0',
86
+ description: 'Anti-Money Laundering compliance policy',
87
+ requirements: [
88
+ 'Transaction monitoring',
89
+ 'Suspicious activity reporting',
90
+ 'Record keeping (5 years)',
91
+ 'Customer due diligence',
92
+ ],
93
+ effectiveDate: '2024-01-01',
94
+ jurisdiction: 'Global',
95
+ };
96
+
97
+ if (outputJSON(policy, options)) {
98
+ return;
99
+ }
100
+
101
+ console.log();
102
+ console.log(chalk.bold.cyan('🦞 Policy Details'));
103
+ console.log(chalk.gray('─'.repeat(50)));
104
+ console.log('Policy ID: ', policy.id);
105
+ console.log('Name: ', policy.name);
106
+ console.log('Version: ', policy.version);
107
+ console.log('Description: ', policy.description);
108
+ console.log('Effective Date: ', policy.effectiveDate);
109
+ console.log('Jurisdiction: ', policy.jurisdiction);
110
+ console.log();
111
+ console.log(chalk.bold('Requirements:'));
112
+ policy.requirements.forEach(req => {
113
+ console.log(` • ${req}`);
114
+ });
115
+ console.log();
116
+ } catch (err) {
117
+ error(`Failed to get policy: ${err}`);
118
+ process.exit(1);
119
+ }
120
+ });
121
+
122
+ return cmd;
123
+ }
@@ -0,0 +1,193 @@
1
+ import { Command } from 'commander';
2
+ import { success, error, info, warning, withSpinner, outputJSON, formatAddress, printReputation, createTable } from '../lib/display';
3
+ import { getWalletAddress } from '../lib/wallet';
4
+ import type { OutputOptions } from '../lib/types';
5
+ import chalk from 'chalk';
6
+
7
+ export function createCreditScoreCommand(): Command {
8
+ const cmd = new Command('credit-score')
9
+ .description('Check credit scores and manage credit lines')
10
+ .argument('[address]', 'Address to check (optional, defaults to your address)')
11
+ .option('--json', 'Output as JSON')
12
+ .action(async (address: string | undefined, options: OutputOptions) => {
13
+ try {
14
+ const targetAddress = address || getWalletAddress();
15
+
16
+ if (address && (!address.startsWith('0x') || address.length !== 42)) {
17
+ error('Invalid address format');
18
+ process.exit(1);
19
+ }
20
+
21
+ // Placeholder data
22
+ const creditData = {
23
+ address: targetAddress,
24
+ score: 750,
25
+ creditLine: '5000 USDC',
26
+ availableCredit: '3500 USDC',
27
+ usedCredit: '1500 USDC',
28
+ paymentHistory: 'Excellent',
29
+ onTimePayments: 45,
30
+ latePayments: 2,
31
+ totalTransactions: 47,
32
+ };
33
+
34
+ if (outputJSON(creditData, options)) {
35
+ return;
36
+ }
37
+
38
+ console.log();
39
+ console.log(chalk.bold.cyan('🦞 Credit Score Report'));
40
+ console.log(chalk.gray('─'.repeat(50)));
41
+ console.log('Address: ', formatAddress(creditData.address));
42
+ console.log('Credit Score: ', printReputation(creditData.score / 10)); // Convert to 0-100 scale
43
+ console.log();
44
+ console.log(chalk.bold('Credit Line'));
45
+ console.log(' Total: ', creditData.creditLine);
46
+ console.log(' Available: ', chalk.green(creditData.availableCredit));
47
+ console.log(' Used: ', chalk.yellow(creditData.usedCredit));
48
+ console.log();
49
+ console.log(chalk.bold('Payment History'));
50
+ console.log(' On-time: ', chalk.green(creditData.onTimePayments));
51
+ console.log(' Late: ', chalk.red(creditData.latePayments));
52
+ console.log(' Total: ', creditData.totalTransactions);
53
+ console.log(' Rating: ', chalk.green(creditData.paymentHistory));
54
+ console.log();
55
+ } catch (err) {
56
+ error(`Failed to get credit score: ${err}`);
57
+ process.exit(1);
58
+ }
59
+ });
60
+
61
+ // plob credit-score request
62
+ cmd
63
+ .command('request')
64
+ .description('Request a credit line')
65
+ .requiredOption('--amount <amount>', 'Credit line amount in USDC')
66
+ .option('--json', 'Output as JSON')
67
+ .action(async (options: { amount: string } & OutputOptions) => {
68
+ try {
69
+ const amount = parseFloat(options.amount);
70
+ if (isNaN(amount) || amount <= 0) {
71
+ error('Invalid amount. Must be a positive number');
72
+ process.exit(1);
73
+ }
74
+
75
+ info(`Requesting credit line: ${options.amount} USDC`);
76
+
77
+ const requestId = Math.floor(Math.random() * 1000000);
78
+
79
+ const txHash = await withSpinner(
80
+ 'Submitting credit line request...',
81
+ async () => {
82
+ // Placeholder - implement actual contract call
83
+ await new Promise(resolve => setTimeout(resolve, 2000));
84
+ return '0x' + Math.random().toString(16).substring(2, 66);
85
+ }
86
+ );
87
+
88
+ if (outputJSON({
89
+ requestId,
90
+ amount: options.amount,
91
+ txHash,
92
+ }, options)) {
93
+ return;
94
+ }
95
+
96
+ success('Credit line request submitted!');
97
+ info(`Request ID: ${requestId}`);
98
+ info(`Amount: ${options.amount} USDC`);
99
+ info(`Transaction: ${txHash}`);
100
+ console.log();
101
+ warning('Credit requests are subject to approval based on your credit score.');
102
+ } catch (err) {
103
+ error(`Failed to request credit line: ${err}`);
104
+ process.exit(1);
105
+ }
106
+ });
107
+
108
+ // plob credit-score draw
109
+ cmd
110
+ .command('draw')
111
+ .description('Draw from credit line')
112
+ .argument('<creditLineId>', 'Credit line ID')
113
+ .requiredOption('--amount <amount>', 'Amount to draw in USDC')
114
+ .option('--json', 'Output as JSON')
115
+ .action(async (creditLineId: string, options: { amount: string } & OutputOptions) => {
116
+ try {
117
+ const amount = parseFloat(options.amount);
118
+ if (isNaN(amount) || amount <= 0) {
119
+ error('Invalid amount. Must be a positive number');
120
+ process.exit(1);
121
+ }
122
+
123
+ const txHash = await withSpinner(
124
+ `Drawing ${options.amount} USDC from credit line...`,
125
+ async () => {
126
+ // Placeholder - implement actual contract call
127
+ await new Promise(resolve => setTimeout(resolve, 1500));
128
+ return '0x' + Math.random().toString(16).substring(2, 66);
129
+ }
130
+ );
131
+
132
+ if (outputJSON({
133
+ creditLineId,
134
+ amount: options.amount,
135
+ txHash,
136
+ }, options)) {
137
+ return;
138
+ }
139
+
140
+ success('Credit drawn successfully!');
141
+ info(`Credit Line ID: ${creditLineId}`);
142
+ info(`Amount: ${options.amount} USDC`);
143
+ info(`Transaction: ${txHash}`);
144
+ } catch (err) {
145
+ error(`Failed to draw credit: ${err}`);
146
+ process.exit(1);
147
+ }
148
+ });
149
+
150
+ // plob credit-score repay
151
+ cmd
152
+ .command('repay')
153
+ .description('Repay credit line')
154
+ .argument('<creditLineId>', 'Credit line ID')
155
+ .requiredOption('--amount <amount>', 'Amount to repay in USDC')
156
+ .option('--json', 'Output as JSON')
157
+ .action(async (creditLineId: string, options: { amount: string } & OutputOptions) => {
158
+ try {
159
+ const amount = parseFloat(options.amount);
160
+ if (isNaN(amount) || amount <= 0) {
161
+ error('Invalid amount. Must be a positive number');
162
+ process.exit(1);
163
+ }
164
+
165
+ const txHash = await withSpinner(
166
+ `Repaying ${options.amount} USDC to credit line...`,
167
+ async () => {
168
+ // Placeholder - implement actual contract call
169
+ await new Promise(resolve => setTimeout(resolve, 1500));
170
+ return '0x' + Math.random().toString(16).substring(2, 66);
171
+ }
172
+ );
173
+
174
+ if (outputJSON({
175
+ creditLineId,
176
+ amount: options.amount,
177
+ txHash,
178
+ }, options)) {
179
+ return;
180
+ }
181
+
182
+ success('Repayment successful!');
183
+ info(`Credit Line ID: ${creditLineId}`);
184
+ info(`Amount: ${options.amount} USDC`);
185
+ info(`Transaction: ${txHash}`);
186
+ } catch (err) {
187
+ error(`Failed to repay credit: ${err}`);
188
+ process.exit(1);
189
+ }
190
+ });
191
+
192
+ return cmd;
193
+ }