@paylobster/cli 4.6.0 → 4.6.1
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/dist/src/commands/alerts.d.ts +3 -0
- package/dist/src/commands/alerts.d.ts.map +1 -0
- package/dist/src/commands/alerts.js +181 -0
- package/dist/src/commands/alerts.js.map +1 -0
- package/dist/src/commands/init.d.ts +3 -0
- package/dist/src/commands/init.d.ts.map +1 -0
- package/dist/src/commands/init.js +232 -0
- package/dist/src/commands/init.js.map +1 -0
- package/dist/src/commands/link.d.ts +6 -0
- package/dist/src/commands/link.d.ts.map +1 -0
- package/dist/src/commands/link.js +188 -0
- package/dist/src/commands/link.js.map +1 -0
- package/dist/src/commands/refund.d.ts +6 -0
- package/dist/src/commands/refund.d.ts.map +1 -0
- package/dist/src/commands/refund.js +199 -0
- package/dist/src/commands/refund.js.map +1 -0
- package/dist/src/commands/swap.d.ts.map +1 -1
- package/dist/src/commands/swap.js +93 -1
- package/dist/src/commands/swap.js.map +1 -1
- package/dist/src/commands/webhook.d.ts +6 -0
- package/dist/src/commands/webhook.d.ts.map +1 -0
- package/dist/src/commands/webhook.js +208 -0
- package/dist/src/commands/webhook.js.map +1 -0
- package/dist/src/index.js +10 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/lib/contracts.d.ts +23 -161
- package/dist/src/lib/contracts.d.ts.map +1 -1
- package/dist/src/lib/contracts.js +6 -6
- package/dist/src/lib/contracts.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/alerts.ts +210 -0
- package/src/commands/init.ts +256 -0
- package/src/commands/link.ts +240 -0
- package/src/commands/refund.ts +250 -0
- package/src/commands/swap.ts +120 -1
- package/src/commands/webhook.ts +260 -0
- package/src/index.ts +10 -0
- package/src/lib/contracts.ts +6 -6
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { success, error, info, withSpinner, outputJSON, confirm } from '../lib/display';
|
|
3
|
+
import type { OutputOptions } from '../lib/types';
|
|
4
|
+
|
|
5
|
+
const API_URL = process.env.PAYLOBSTER_API_URL || 'https://paylobster.com';
|
|
6
|
+
|
|
7
|
+
interface Refund {
|
|
8
|
+
refundId: string;
|
|
9
|
+
escrowId: number;
|
|
10
|
+
status: 'pending' | 'approved' | 'rejected' | 'processed';
|
|
11
|
+
amount: string;
|
|
12
|
+
reason: string;
|
|
13
|
+
txHash?: string;
|
|
14
|
+
auto?: boolean;
|
|
15
|
+
createdAt: string;
|
|
16
|
+
processedAt?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Refunds command
|
|
21
|
+
*/
|
|
22
|
+
export function createRefundCommand(): Command {
|
|
23
|
+
const cmd = new Command('refund')
|
|
24
|
+
.description('Request and manage refunds');
|
|
25
|
+
|
|
26
|
+
// Request subcommand
|
|
27
|
+
cmd
|
|
28
|
+
.command('request')
|
|
29
|
+
.description('Request a refund for an escrow')
|
|
30
|
+
.requiredOption('--escrow-id <id>', 'Escrow ID')
|
|
31
|
+
.requiredOption('--reason <text>', 'Reason for refund')
|
|
32
|
+
.option('--tx-hash <hash>', 'Transaction hash (if applicable)')
|
|
33
|
+
.option('--yes', 'Skip confirmation')
|
|
34
|
+
.option('--json', 'Output as JSON')
|
|
35
|
+
.action(async (options: {
|
|
36
|
+
escrowId: string;
|
|
37
|
+
reason: string;
|
|
38
|
+
txHash?: string;
|
|
39
|
+
yes?: boolean;
|
|
40
|
+
} & OutputOptions) => {
|
|
41
|
+
try {
|
|
42
|
+
// Validate escrow ID
|
|
43
|
+
const escrowId = parseInt(options.escrowId);
|
|
44
|
+
if (isNaN(escrowId) || escrowId < 0) {
|
|
45
|
+
error('Invalid escrow ID. Must be a non-negative number');
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Confirm refund request
|
|
50
|
+
if (!options.yes) {
|
|
51
|
+
console.log();
|
|
52
|
+
console.log('Refund Request:');
|
|
53
|
+
console.log(' Escrow ID: ', escrowId);
|
|
54
|
+
console.log(' Reason: ', options.reason);
|
|
55
|
+
console.log();
|
|
56
|
+
|
|
57
|
+
const confirmed = await confirm('Submit this refund request?');
|
|
58
|
+
if (!confirmed) {
|
|
59
|
+
info('Cancelled');
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const refund = await withSpinner(
|
|
65
|
+
'Submitting refund request...',
|
|
66
|
+
async () => {
|
|
67
|
+
const response = await fetch(`${API_URL}/api/v3/refunds`, {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: {
|
|
70
|
+
'Content-Type': 'application/json',
|
|
71
|
+
},
|
|
72
|
+
body: JSON.stringify({
|
|
73
|
+
escrowId,
|
|
74
|
+
reason: options.reason,
|
|
75
|
+
txHash: options.txHash,
|
|
76
|
+
}),
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (!response.ok) {
|
|
80
|
+
const err = await response.json() as { error?: string };
|
|
81
|
+
throw new Error(err.error || 'Failed to request refund');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return response.json() as Promise<Refund>;
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
if (outputJSON(refund, options)) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
success('Refund request submitted!');
|
|
93
|
+
console.log();
|
|
94
|
+
console.log(' Refund ID: ', refund.refundId);
|
|
95
|
+
console.log(' Escrow ID: ', refund.escrowId);
|
|
96
|
+
console.log(' Status: ', getStatusEmoji(refund.status), refund.status);
|
|
97
|
+
console.log(' Amount: ', refund.amount);
|
|
98
|
+
console.log(' Reason: ', refund.reason);
|
|
99
|
+
console.log(' Created: ', new Date(refund.createdAt).toLocaleString());
|
|
100
|
+
console.log();
|
|
101
|
+
info('Your refund request will be processed according to the escrow terms');
|
|
102
|
+
} catch (err) {
|
|
103
|
+
error(`Failed to request refund: ${(err as Error).message}`);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Get subcommand
|
|
109
|
+
cmd
|
|
110
|
+
.command('get <refund-id>')
|
|
111
|
+
.description('Get refund status')
|
|
112
|
+
.option('--json', 'Output as JSON')
|
|
113
|
+
.action(async (refundId: string, options: OutputOptions) => {
|
|
114
|
+
try {
|
|
115
|
+
const refund = await withSpinner(
|
|
116
|
+
'Fetching refund details...',
|
|
117
|
+
async () => {
|
|
118
|
+
const response = await fetch(
|
|
119
|
+
`${API_URL}/api/v3/refunds?refundId=${encodeURIComponent(refundId)}`,
|
|
120
|
+
{
|
|
121
|
+
method: 'GET',
|
|
122
|
+
headers: {
|
|
123
|
+
'Content-Type': 'application/json',
|
|
124
|
+
},
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
if (!response.ok) {
|
|
129
|
+
const err = await response.json() as { error?: string };
|
|
130
|
+
throw new Error(err.error || 'Failed to fetch refund');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return response.json() as Promise<Refund>;
|
|
134
|
+
}
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
if (outputJSON(refund, options)) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
console.log();
|
|
142
|
+
console.log('Refund Details:');
|
|
143
|
+
console.log(' Refund ID: ', refund.refundId);
|
|
144
|
+
console.log(' Escrow ID: ', refund.escrowId);
|
|
145
|
+
console.log(' Status: ', getStatusEmoji(refund.status), refund.status);
|
|
146
|
+
console.log(' Amount: ', refund.amount);
|
|
147
|
+
console.log(' Reason: ', refund.reason);
|
|
148
|
+
if (refund.auto) {
|
|
149
|
+
console.log(' Auto: ', '✅ Automatic refund');
|
|
150
|
+
}
|
|
151
|
+
if (refund.txHash) {
|
|
152
|
+
console.log(' TX Hash: ', refund.txHash);
|
|
153
|
+
}
|
|
154
|
+
console.log(' Created: ', new Date(refund.createdAt).toLocaleString());
|
|
155
|
+
if (refund.processedAt) {
|
|
156
|
+
console.log(' Processed: ', new Date(refund.processedAt).toLocaleString());
|
|
157
|
+
}
|
|
158
|
+
console.log();
|
|
159
|
+
} catch (err) {
|
|
160
|
+
error(`Failed to fetch refund: ${(err as Error).message}`);
|
|
161
|
+
process.exit(1);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// List by escrow subcommand
|
|
166
|
+
cmd
|
|
167
|
+
.command('list <escrow-id>')
|
|
168
|
+
.description('List all refunds for an escrow')
|
|
169
|
+
.option('--json', 'Output as JSON')
|
|
170
|
+
.action(async (escrowIdStr: string, options: OutputOptions) => {
|
|
171
|
+
try {
|
|
172
|
+
// Validate escrow ID
|
|
173
|
+
const escrowId = parseInt(escrowIdStr);
|
|
174
|
+
if (isNaN(escrowId) || escrowId < 0) {
|
|
175
|
+
error('Invalid escrow ID. Must be a non-negative number');
|
|
176
|
+
process.exit(1);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const result = await withSpinner(
|
|
180
|
+
'Fetching refunds...',
|
|
181
|
+
async () => {
|
|
182
|
+
const response = await fetch(
|
|
183
|
+
`${API_URL}/api/v3/refunds?escrowId=${escrowId}`,
|
|
184
|
+
{
|
|
185
|
+
method: 'GET',
|
|
186
|
+
headers: {
|
|
187
|
+
'Content-Type': 'application/json',
|
|
188
|
+
},
|
|
189
|
+
}
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
if (!response.ok) {
|
|
193
|
+
const err = await response.json() as { error?: string };
|
|
194
|
+
throw new Error(err.error || 'Failed to fetch refunds');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return response.json() as Promise<{ escrowId: number; refunds: Refund[] }>;
|
|
198
|
+
}
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
if (outputJSON(result, options)) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (result.refunds.length === 0) {
|
|
206
|
+
info(`No refunds found for escrow ID ${escrowId}`);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
console.log();
|
|
211
|
+
console.log(`Found ${result.refunds.length} refund(s) for escrow ${escrowId}:`);
|
|
212
|
+
console.log();
|
|
213
|
+
|
|
214
|
+
for (const refund of result.refunds) {
|
|
215
|
+
console.log(` ${refund.refundId}`);
|
|
216
|
+
console.log(` Status: ${getStatusEmoji(refund.status)} ${refund.status}`);
|
|
217
|
+
console.log(` Amount: ${refund.amount}`);
|
|
218
|
+
console.log(` Reason: ${refund.reason}`);
|
|
219
|
+
if (refund.auto) {
|
|
220
|
+
console.log(` Auto: ✅ Automatic`);
|
|
221
|
+
}
|
|
222
|
+
console.log(` Created: ${new Date(refund.createdAt).toLocaleString()}`);
|
|
223
|
+
if (refund.processedAt) {
|
|
224
|
+
console.log(` Processed: ${new Date(refund.processedAt).toLocaleString()}`);
|
|
225
|
+
}
|
|
226
|
+
console.log();
|
|
227
|
+
}
|
|
228
|
+
} catch (err) {
|
|
229
|
+
error(`Failed to fetch refunds: ${(err as Error).message}`);
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
return cmd;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function getStatusEmoji(status: string): string {
|
|
238
|
+
switch (status) {
|
|
239
|
+
case 'pending':
|
|
240
|
+
return '⏳';
|
|
241
|
+
case 'approved':
|
|
242
|
+
return '✅';
|
|
243
|
+
case 'rejected':
|
|
244
|
+
return '❌';
|
|
245
|
+
case 'processed':
|
|
246
|
+
return '✅';
|
|
247
|
+
default:
|
|
248
|
+
return '❓';
|
|
249
|
+
}
|
|
250
|
+
}
|
package/src/commands/swap.ts
CHANGED
|
@@ -155,12 +155,42 @@ async function getTokenPrice(tokenAddress: string): Promise<any> {
|
|
|
155
155
|
return call0xAPI(endpoint);
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
const API_BASE = process.env.PAYLOBSTER_API_URL || 'https://paylobster.com';
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Get smart route from PayLobster API
|
|
162
|
+
*/
|
|
163
|
+
async function getSmartRoute(
|
|
164
|
+
sellToken: string,
|
|
165
|
+
buyToken: string,
|
|
166
|
+
sellAmount: string,
|
|
167
|
+
takerAddress?: string
|
|
168
|
+
): Promise<any> {
|
|
169
|
+
const res = await fetch(`${API_BASE}/api/v3/swap/route`, {
|
|
170
|
+
method: 'POST',
|
|
171
|
+
headers: { 'Content-Type': 'application/json' },
|
|
172
|
+
body: JSON.stringify({
|
|
173
|
+
tokenIn: sellToken,
|
|
174
|
+
tokenOut: buyToken,
|
|
175
|
+
amountIn: sellAmount,
|
|
176
|
+
takerAddress,
|
|
177
|
+
}),
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
if (!res.ok) {
|
|
181
|
+
const data = await res.json().catch(() => ({}));
|
|
182
|
+
throw new Error(data.error || `Smart route failed: ${res.status}`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return res.json();
|
|
186
|
+
}
|
|
187
|
+
|
|
158
188
|
/**
|
|
159
189
|
* Create swap command
|
|
160
190
|
*/
|
|
161
191
|
export function createSwapCommand(): Command {
|
|
162
192
|
const cmd = new Command('swap')
|
|
163
|
-
.description('Token swaps on Base using 0x');
|
|
193
|
+
.description('Token swaps on Base using 0x (with smart routing)');
|
|
164
194
|
|
|
165
195
|
// Swap quote subcommand
|
|
166
196
|
cmd
|
|
@@ -300,6 +330,95 @@ export function createSwapCommand(): Command {
|
|
|
300
330
|
}
|
|
301
331
|
});
|
|
302
332
|
|
|
333
|
+
// Smart swap subcommand (natural language style)
|
|
334
|
+
cmd
|
|
335
|
+
.command('auto')
|
|
336
|
+
.description('Smart swap with auto-routing (multi-hop if needed)')
|
|
337
|
+
.argument('<amount>', 'Amount to sell')
|
|
338
|
+
.argument('<fromToken>', 'Token to sell')
|
|
339
|
+
.argument('[toKeyword]', '"to" keyword (ignored)')
|
|
340
|
+
.argument('[toToken]', 'Token to buy')
|
|
341
|
+
.option('--usd', 'Interpret amount as USD value')
|
|
342
|
+
.option('--slippage <percent>', 'Slippage tolerance (default: 0.5)', '0.5')
|
|
343
|
+
.option('--yes', 'Skip confirmation')
|
|
344
|
+
.option('--json', 'Output as JSON')
|
|
345
|
+
.action(async (amount: string, fromTokenArg: string, toKeyword: string, toTokenArg: string, options: {
|
|
346
|
+
usd?: boolean;
|
|
347
|
+
slippage?: string;
|
|
348
|
+
yes?: boolean;
|
|
349
|
+
} & OutputOptions) => {
|
|
350
|
+
try {
|
|
351
|
+
const address = getWalletAddress() as `0x${string}`;
|
|
352
|
+
|
|
353
|
+
// Handle "10 USDC to ANTIHUNTER" pattern
|
|
354
|
+
let toToken = toTokenArg;
|
|
355
|
+
if (toKeyword && toKeyword.toLowerCase() !== 'to') {
|
|
356
|
+
// "swap auto 10 USDC ANTIHUNTER" — no "to" keyword
|
|
357
|
+
toToken = toKeyword;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (!toToken) {
|
|
361
|
+
error('Missing destination token. Usage: paylobster swap auto 10 USDC to ANTIHUNTER');
|
|
362
|
+
process.exit(1);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const sellTokenAddr = resolveToken(fromTokenArg);
|
|
366
|
+
const buyTokenAddr = resolveToken(toToken);
|
|
367
|
+
const sellDecimals = getTokenDecimals(fromTokenArg);
|
|
368
|
+
const sellAmount = parseTokenAmount(amount.replace('$', ''), sellDecimals);
|
|
369
|
+
|
|
370
|
+
const routeData = await withSpinner(
|
|
371
|
+
'🧠 Finding best route...',
|
|
372
|
+
async () => getSmartRoute(sellTokenAddr, buyTokenAddr, sellAmount, address)
|
|
373
|
+
);
|
|
374
|
+
|
|
375
|
+
if (!routeData.bestRoute) {
|
|
376
|
+
error('No route found for this swap');
|
|
377
|
+
process.exit(1);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const route = routeData.bestRoute;
|
|
381
|
+
const hopCount = route.hops?.length || 1;
|
|
382
|
+
|
|
383
|
+
if (outputJSON(routeData, options)) return;
|
|
384
|
+
|
|
385
|
+
console.log();
|
|
386
|
+
console.log(chalk.bold.cyan('🧠 Smart Route Found'));
|
|
387
|
+
console.log();
|
|
388
|
+
console.log(' From: ', chalk.white.bold(amount), chalk.gray(fromTokenArg.toUpperCase()));
|
|
389
|
+
console.log(' To: ', chalk.white.bold(toToken.toUpperCase()));
|
|
390
|
+
console.log(' Route: ', chalk.white(route.source || 'Auto'));
|
|
391
|
+
console.log(' Hops: ', chalk.white(hopCount.toString()));
|
|
392
|
+
|
|
393
|
+
// Show each hop
|
|
394
|
+
if (route.hops) {
|
|
395
|
+
for (let i = 0; i < route.hops.length; i++) {
|
|
396
|
+
const hop = route.hops[i];
|
|
397
|
+
console.log(chalk.gray(` Step ${i + 1}: `), chalk.dim(`${hop.dex}: ${hop.tokenIn.slice(0, 8)}... → ${hop.tokenOut.slice(0, 8)}...`));
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
console.log(' Output: ', chalk.green.bold(routeData.estimatedOutput));
|
|
402
|
+
console.log(' Impact: ', chalk.yellow(`${((routeData.priceImpact || 0) * 100).toFixed(2)}%`));
|
|
403
|
+
console.log(' Gas: ', chalk.gray(`~${routeData.gasEstimate}`));
|
|
404
|
+
console.log();
|
|
405
|
+
|
|
406
|
+
if (!options.yes) {
|
|
407
|
+
const confirmed = await confirm('Execute this swap?');
|
|
408
|
+
if (!confirmed) {
|
|
409
|
+
info('Cancelled');
|
|
410
|
+
process.exit(0);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
info('Swap execution requires wallet signing. Transaction data is available above.');
|
|
415
|
+
console.log();
|
|
416
|
+
} catch (err) {
|
|
417
|
+
error(`Smart swap failed: ${err}`);
|
|
418
|
+
process.exit(1);
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
|
|
303
422
|
// List tokens subcommand
|
|
304
423
|
cmd
|
|
305
424
|
.command('tokens')
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { success, error, info, withSpinner, outputJSON } from '../lib/display';
|
|
3
|
+
import type { OutputOptions } from '../lib/types';
|
|
4
|
+
|
|
5
|
+
const API_URL = process.env.PAYLOBSTER_API_URL || 'https://paylobster.com';
|
|
6
|
+
|
|
7
|
+
interface Webhook {
|
|
8
|
+
id: string;
|
|
9
|
+
url: string;
|
|
10
|
+
events: string[];
|
|
11
|
+
secret?: string;
|
|
12
|
+
createdAt: string;
|
|
13
|
+
status: 'active' | 'paused';
|
|
14
|
+
lastDeliveryAttempt?: string;
|
|
15
|
+
lastDeliveryStatus?: 'success' | 'failed';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Webhooks command
|
|
20
|
+
*/
|
|
21
|
+
export function createWebhookCommand(): Command {
|
|
22
|
+
const cmd = new Command('webhooks')
|
|
23
|
+
.description('Register and manage webhooks');
|
|
24
|
+
|
|
25
|
+
// Register subcommand
|
|
26
|
+
cmd
|
|
27
|
+
.command('register')
|
|
28
|
+
.description('Register a new webhook')
|
|
29
|
+
.requiredOption('--url <url>', 'Webhook URL')
|
|
30
|
+
.requiredOption('--events <events>', 'Comma-separated event types')
|
|
31
|
+
.option('--secret <secret>', 'Webhook secret for signature verification')
|
|
32
|
+
.option('--json', 'Output as JSON')
|
|
33
|
+
.action(async (options: {
|
|
34
|
+
url: string;
|
|
35
|
+
events: string;
|
|
36
|
+
secret?: string;
|
|
37
|
+
} & OutputOptions) => {
|
|
38
|
+
try {
|
|
39
|
+
// Validate URL
|
|
40
|
+
try {
|
|
41
|
+
new URL(options.url);
|
|
42
|
+
} catch {
|
|
43
|
+
error('Invalid URL format');
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Parse events
|
|
48
|
+
const events = options.events.split(',').map(e => e.trim()).filter(Boolean);
|
|
49
|
+
if (events.length === 0) {
|
|
50
|
+
error('At least one event type is required');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const webhook = await withSpinner(
|
|
55
|
+
'Registering webhook...',
|
|
56
|
+
async () => {
|
|
57
|
+
const response = await fetch(`${API_URL}/api/v3/webhooks`, {
|
|
58
|
+
method: 'POST',
|
|
59
|
+
headers: {
|
|
60
|
+
'Content-Type': 'application/json',
|
|
61
|
+
},
|
|
62
|
+
body: JSON.stringify({
|
|
63
|
+
url: options.url,
|
|
64
|
+
events,
|
|
65
|
+
secret: options.secret,
|
|
66
|
+
}),
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
const err = await response.json() as { error?: string };
|
|
71
|
+
throw new Error(err.error || 'Failed to register webhook');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return response.json() as Promise<Webhook>;
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
if (outputJSON(webhook, options)) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
success('Webhook registered!');
|
|
83
|
+
console.log();
|
|
84
|
+
console.log(' Webhook ID: ', webhook.id);
|
|
85
|
+
console.log(' URL: ', webhook.url);
|
|
86
|
+
console.log(' Events: ', webhook.events.join(', '));
|
|
87
|
+
console.log(' Status: ', webhook.status);
|
|
88
|
+
if (webhook.secret) {
|
|
89
|
+
console.log(' Secret: ', webhook.secret);
|
|
90
|
+
}
|
|
91
|
+
console.log();
|
|
92
|
+
info('Your webhook will receive notifications for the specified events');
|
|
93
|
+
} catch (err) {
|
|
94
|
+
error(`Failed to register webhook: ${(err as Error).message}`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// List subcommand
|
|
100
|
+
cmd
|
|
101
|
+
.command('list')
|
|
102
|
+
.description('List all registered webhooks')
|
|
103
|
+
.option('--address <address>', 'Filter by address')
|
|
104
|
+
.option('--json', 'Output as JSON')
|
|
105
|
+
.action(async (options: {
|
|
106
|
+
address?: string;
|
|
107
|
+
} & OutputOptions) => {
|
|
108
|
+
try {
|
|
109
|
+
const result = await withSpinner(
|
|
110
|
+
'Fetching webhooks...',
|
|
111
|
+
async () => {
|
|
112
|
+
const url = new URL(`${API_URL}/api/v3/webhooks`);
|
|
113
|
+
if (options.address) {
|
|
114
|
+
url.searchParams.set('address', options.address);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const response = await fetch(url.toString(), {
|
|
118
|
+
method: 'GET',
|
|
119
|
+
headers: {
|
|
120
|
+
'Content-Type': 'application/json',
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
if (!response.ok) {
|
|
125
|
+
const err = await response.json() as { error?: string };
|
|
126
|
+
throw new Error(err.error || 'Failed to fetch webhooks');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return response.json() as Promise<{ webhooks: Webhook[] }>;
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
if (outputJSON(result, options)) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (result.webhooks.length === 0) {
|
|
138
|
+
info('No webhooks registered');
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
console.log();
|
|
143
|
+
console.log(`Found ${result.webhooks.length} webhook(s):`);
|
|
144
|
+
console.log();
|
|
145
|
+
|
|
146
|
+
for (const webhook of result.webhooks) {
|
|
147
|
+
console.log(` ${webhook.id}`);
|
|
148
|
+
console.log(` URL: ${webhook.url}`);
|
|
149
|
+
console.log(` Events: ${webhook.events.join(', ')}`);
|
|
150
|
+
console.log(` Status: ${webhook.status === 'active' ? '✅ Active' : '⏸️ Paused'}`);
|
|
151
|
+
if (webhook.lastDeliveryAttempt) {
|
|
152
|
+
const status = webhook.lastDeliveryStatus === 'success' ? '✅' : '❌';
|
|
153
|
+
console.log(` Last: ${status} ${new Date(webhook.lastDeliveryAttempt).toLocaleString()}`);
|
|
154
|
+
}
|
|
155
|
+
console.log();
|
|
156
|
+
}
|
|
157
|
+
} catch (err) {
|
|
158
|
+
error(`Failed to fetch webhooks: ${(err as Error).message}`);
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// Get subcommand
|
|
164
|
+
cmd
|
|
165
|
+
.command('get <id>')
|
|
166
|
+
.description('Get webhook details')
|
|
167
|
+
.option('--include-secret', 'Include webhook secret in output')
|
|
168
|
+
.option('--json', 'Output as JSON')
|
|
169
|
+
.action(async (id: string, options: {
|
|
170
|
+
includeSecret?: boolean;
|
|
171
|
+
} & OutputOptions) => {
|
|
172
|
+
try {
|
|
173
|
+
const webhook = await withSpinner(
|
|
174
|
+
'Fetching webhook...',
|
|
175
|
+
async () => {
|
|
176
|
+
const url = new URL(`${API_URL}/api/v3/webhooks/${id}`);
|
|
177
|
+
if (options.includeSecret) {
|
|
178
|
+
url.searchParams.set('includeSecret', 'true');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const response = await fetch(url.toString(), {
|
|
182
|
+
method: 'GET',
|
|
183
|
+
headers: {
|
|
184
|
+
'Content-Type': 'application/json',
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
if (!response.ok) {
|
|
189
|
+
const err = await response.json() as { error?: string };
|
|
190
|
+
throw new Error(err.error || 'Failed to fetch webhook');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return response.json() as Promise<Webhook>;
|
|
194
|
+
}
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
if (outputJSON(webhook, options)) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
console.log();
|
|
202
|
+
console.log('Webhook Details:');
|
|
203
|
+
console.log(' ID: ', webhook.id);
|
|
204
|
+
console.log(' URL: ', webhook.url);
|
|
205
|
+
console.log(' Events: ', webhook.events.join(', '));
|
|
206
|
+
console.log(' Status: ', webhook.status === 'active' ? '✅ Active' : '⏸️ Paused');
|
|
207
|
+
if (webhook.secret) {
|
|
208
|
+
console.log(' Secret: ', webhook.secret);
|
|
209
|
+
}
|
|
210
|
+
console.log(' Created: ', new Date(webhook.createdAt).toLocaleString());
|
|
211
|
+
if (webhook.lastDeliveryAttempt) {
|
|
212
|
+
const status = webhook.lastDeliveryStatus === 'success' ? '✅ Success' : '❌ Failed';
|
|
213
|
+
console.log(' Last: ', `${status} at ${new Date(webhook.lastDeliveryAttempt).toLocaleString()}`);
|
|
214
|
+
}
|
|
215
|
+
console.log();
|
|
216
|
+
} catch (err) {
|
|
217
|
+
error(`Failed to fetch webhook: ${(err as Error).message}`);
|
|
218
|
+
process.exit(1);
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
// Delete subcommand
|
|
223
|
+
cmd
|
|
224
|
+
.command('delete <id>')
|
|
225
|
+
.description('Delete a webhook')
|
|
226
|
+
.option('--json', 'Output as JSON')
|
|
227
|
+
.action(async (id: string, options: OutputOptions) => {
|
|
228
|
+
try {
|
|
229
|
+
const result = await withSpinner(
|
|
230
|
+
'Deleting webhook...',
|
|
231
|
+
async () => {
|
|
232
|
+
const response = await fetch(`${API_URL}/api/v3/webhooks/${id}`, {
|
|
233
|
+
method: 'DELETE',
|
|
234
|
+
headers: {
|
|
235
|
+
'Content-Type': 'application/json',
|
|
236
|
+
},
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
if (!response.ok) {
|
|
240
|
+
const err = await response.json() as { error?: string };
|
|
241
|
+
throw new Error(err.error || 'Failed to delete webhook');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return response.json() as Promise<{ success: boolean; message: string }>;
|
|
245
|
+
}
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
if (outputJSON(result, options)) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
success('Webhook deleted successfully');
|
|
253
|
+
} catch (err) {
|
|
254
|
+
error(`Failed to delete webhook: ${(err as Error).message}`);
|
|
255
|
+
process.exit(1);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
return cmd;
|
|
260
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -29,6 +29,11 @@ import { createHealthCommand } from './commands/health';
|
|
|
29
29
|
import { createSearchCommand } from './commands/search';
|
|
30
30
|
import { createExportCommand } from './commands/export';
|
|
31
31
|
import { createTrustGraphCommand } from './commands/trust-graph';
|
|
32
|
+
import { createInitCommand } from './commands/init';
|
|
33
|
+
import { createLinkCommand } from './commands/link';
|
|
34
|
+
import { createWebhookCommand } from './commands/webhook';
|
|
35
|
+
import { createRefundCommand } from './commands/refund';
|
|
36
|
+
import { createAlertsCommand } from './commands/alerts';
|
|
32
37
|
import chalk from 'chalk';
|
|
33
38
|
|
|
34
39
|
// Load environment variables from .env file if present
|
|
@@ -83,6 +88,11 @@ program.addCommand(createHealthCommand());
|
|
|
83
88
|
program.addCommand(createSearchCommand());
|
|
84
89
|
program.addCommand(createExportCommand());
|
|
85
90
|
program.addCommand(createTrustGraphCommand());
|
|
91
|
+
program.addCommand(createInitCommand());
|
|
92
|
+
program.addCommand(createLinkCommand());
|
|
93
|
+
program.addCommand(createWebhookCommand());
|
|
94
|
+
program.addCommand(createRefundCommand());
|
|
95
|
+
program.addCommand(createAlertsCommand());
|
|
86
96
|
registerInvestCommand(program);
|
|
87
97
|
|
|
88
98
|
// Handle errors
|
package/src/lib/contracts.ts
CHANGED
|
@@ -8,21 +8,21 @@ import type { Network, AgentInfo, Reputation, CreditStatus, EscrowInfo, Balance
|
|
|
8
8
|
const CONTRACTS_MAINNET = {
|
|
9
9
|
IDENTITY: '0xA174ee274F870631B3c330a85EBCad74120BE662' as Address,
|
|
10
10
|
REPUTATION: '0x02bb4132a86134684976E2a52E43D59D89E64b29' as Address,
|
|
11
|
-
CREDIT: '
|
|
12
|
-
ESCROW: '
|
|
11
|
+
CREDIT: '0x4c22B52eacAB9eD2Ce018d032739a93eC68eD27a' as Address,
|
|
12
|
+
ESCROW: '0x703B528C1b07cd27992af9Ae11DD67bE685E489e' as Address,
|
|
13
13
|
USDC: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913' as Address,
|
|
14
|
-
TRUST_GRAPH: '
|
|
14
|
+
TRUST_GRAPH: '0xbccd1d0a37ce981a13b3392d7881f94e28fa693b' as Address,
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
const CONTRACTS_SEPOLIA = {
|
|
18
18
|
IDENTITY: '0x3dfA02Ed4F0e4F10E8031d7a4cB8Ea0bBbFbCB8c' as Address,
|
|
19
19
|
// NOTE: Reputation address provided has 41 hex chars (invalid). Using placeholder.
|
|
20
|
-
//
|
|
21
|
-
REPUTATION: '
|
|
20
|
+
// Was broken (41 hex chars) — fixed to correct address
|
|
21
|
+
REPUTATION: '0xb0033901e3b94f4F36dA0b3e59A1F4AD9f4f1697' as Address,
|
|
22
22
|
CREDIT: '0xBA64e2b2F2a80D03A4B13b3396942C1e78205C7d' as Address,
|
|
23
23
|
ESCROW: '0x78D1f50a1965dE34f6b5a3D3546C94FE1809Cd82' as Address,
|
|
24
24
|
USDC: '0x036CbD53842c5426634e7929541eC2318f3dCF7e' as Address,
|
|
25
|
-
TRUST_GRAPH: '
|
|
25
|
+
TRUST_GRAPH: '0xbccd1d0a37ce981a13b3392d7881f94e28fa693b' as Address,
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
// ABIs
|