pay-lobster 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.
Files changed (120) hide show
  1. package/README.md +401 -0
  2. package/README.md.bak +401 -0
  3. package/dist/agent.d.ts +132 -0
  4. package/dist/agent.d.ts.map +1 -0
  5. package/dist/agent.js +224 -0
  6. package/dist/agent.js.map +1 -0
  7. package/dist/analytics.d.ts +120 -0
  8. package/dist/analytics.d.ts.map +1 -0
  9. package/dist/analytics.js +345 -0
  10. package/dist/analytics.js.map +1 -0
  11. package/dist/approvals.d.ts +168 -0
  12. package/dist/approvals.d.ts.map +1 -0
  13. package/dist/approvals.js +406 -0
  14. package/dist/approvals.js.map +1 -0
  15. package/dist/circle-client.d.ts +152 -0
  16. package/dist/circle-client.d.ts.map +1 -0
  17. package/dist/circle-client.js +266 -0
  18. package/dist/circle-client.js.map +1 -0
  19. package/dist/commission.d.ts +191 -0
  20. package/dist/commission.d.ts.map +1 -0
  21. package/dist/commission.js +475 -0
  22. package/dist/commission.js.map +1 -0
  23. package/dist/condition-builder.d.ts +98 -0
  24. package/dist/condition-builder.d.ts.map +1 -0
  25. package/dist/condition-builder.js +193 -0
  26. package/dist/condition-builder.js.map +1 -0
  27. package/dist/contacts.d.ts +179 -0
  28. package/dist/contacts.d.ts.map +1 -0
  29. package/dist/contacts.js +445 -0
  30. package/dist/contacts.js.map +1 -0
  31. package/dist/easy.d.ts +22 -0
  32. package/dist/easy.d.ts.map +1 -0
  33. package/dist/easy.js +40 -0
  34. package/dist/easy.js.map +1 -0
  35. package/dist/erc8004/constants.d.ts +152 -0
  36. package/dist/erc8004/constants.d.ts.map +1 -0
  37. package/dist/erc8004/constants.js +114 -0
  38. package/dist/erc8004/constants.js.map +1 -0
  39. package/dist/erc8004/discovery.d.ts +84 -0
  40. package/dist/erc8004/discovery.d.ts.map +1 -0
  41. package/dist/erc8004/discovery.js +217 -0
  42. package/dist/erc8004/discovery.js.map +1 -0
  43. package/dist/erc8004/identity.d.ts +91 -0
  44. package/dist/erc8004/identity.d.ts.map +1 -0
  45. package/dist/erc8004/identity.js +250 -0
  46. package/dist/erc8004/identity.js.map +1 -0
  47. package/dist/erc8004/index.d.ts +147 -0
  48. package/dist/erc8004/index.d.ts.map +1 -0
  49. package/dist/erc8004/index.js +225 -0
  50. package/dist/erc8004/index.js.map +1 -0
  51. package/dist/erc8004/reputation.d.ts +133 -0
  52. package/dist/erc8004/reputation.d.ts.map +1 -0
  53. package/dist/erc8004/reputation.js +277 -0
  54. package/dist/erc8004/reputation.js.map +1 -0
  55. package/dist/escrow-templates.d.ts +38 -0
  56. package/dist/escrow-templates.d.ts.map +1 -0
  57. package/dist/escrow-templates.js +419 -0
  58. package/dist/escrow-templates.js.map +1 -0
  59. package/dist/escrow.d.ts +320 -0
  60. package/dist/escrow.d.ts.map +1 -0
  61. package/dist/escrow.js +854 -0
  62. package/dist/escrow.js.map +1 -0
  63. package/dist/index.d.ts +11 -0
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +33 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/invoices.d.ts +212 -0
  68. package/dist/invoices.d.ts.map +1 -0
  69. package/dist/invoices.js +393 -0
  70. package/dist/invoices.js.map +1 -0
  71. package/dist/notifications.d.ts +141 -0
  72. package/dist/notifications.d.ts.map +1 -0
  73. package/dist/notifications.js +350 -0
  74. package/dist/notifications.js.map +1 -0
  75. package/dist/tips.d.ts +171 -0
  76. package/dist/tips.d.ts.map +1 -0
  77. package/dist/tips.js +390 -0
  78. package/dist/tips.js.map +1 -0
  79. package/dist/types.d.ts +100 -0
  80. package/dist/types.d.ts.map +1 -0
  81. package/dist/types.js +6 -0
  82. package/dist/types.js.map +1 -0
  83. package/dist/x402-client.d.ts +127 -0
  84. package/dist/x402-client.d.ts.map +1 -0
  85. package/dist/x402-client.js +350 -0
  86. package/dist/x402-client.js.map +1 -0
  87. package/dist/x402-server.d.ts +133 -0
  88. package/dist/x402-server.d.ts.map +1 -0
  89. package/dist/x402-server.js +330 -0
  90. package/dist/x402-server.js.map +1 -0
  91. package/lib/agent.ts +273 -0
  92. package/lib/analytics.ts +474 -0
  93. package/lib/analytics.ts.bak +474 -0
  94. package/lib/approvals.ts +585 -0
  95. package/lib/approvals.ts.bak +585 -0
  96. package/lib/circle-client.ts +376 -0
  97. package/lib/circle-client.ts.bak +376 -0
  98. package/lib/commission.ts +680 -0
  99. package/lib/commission.ts.bak +680 -0
  100. package/lib/condition-builder.ts +223 -0
  101. package/lib/condition-builder.ts.bak +223 -0
  102. package/lib/contacts.ts +615 -0
  103. package/lib/contacts.ts.bak +615 -0
  104. package/lib/easy.ts +46 -0
  105. package/lib/easy.ts.bak +352 -0
  106. package/lib/erc8004/constants.ts +175 -0
  107. package/lib/erc8004/discovery.ts +299 -0
  108. package/lib/erc8004/identity.ts +327 -0
  109. package/lib/erc8004/index.ts +285 -0
  110. package/lib/erc8004/reputation.ts +368 -0
  111. package/lib/escrow-templates.ts +462 -0
  112. package/lib/escrow.ts +1216 -0
  113. package/lib/index.ts +13 -0
  114. package/lib/invoices.ts +588 -0
  115. package/lib/notifications.ts +484 -0
  116. package/lib/tips.ts +570 -0
  117. package/lib/types.ts +108 -0
  118. package/lib/x402-client.ts +471 -0
  119. package/lib/x402-server.ts +462 -0
  120. package/package.json +58 -0
@@ -0,0 +1,484 @@
1
+ /**
2
+ * Payment Notifications & Webhooks
3
+ *
4
+ * Real-time notifications for USDC transactions and events.
5
+ */
6
+
7
+ import crypto from 'crypto';
8
+ import fs from 'fs/promises';
9
+ import path from 'path';
10
+
11
+ export interface NotificationConfig {
12
+ id: string;
13
+ name: string;
14
+ enabled: boolean;
15
+
16
+ // Trigger conditions
17
+ triggers: {
18
+ type: 'incoming' | 'outgoing' | 'recurring' | 'invoice' | 'threshold';
19
+ minAmount?: string; // Only trigger above this amount
20
+ addresses?: string[]; // Only for specific addresses
21
+ chains?: string[]; // Only for specific chains
22
+ }[];
23
+
24
+ // Delivery channels
25
+ channels: {
26
+ type: 'webhook' | 'telegram' | 'email' | 'clawdbot';
27
+ config: Record<string, string>;
28
+ }[];
29
+
30
+ // Rate limiting
31
+ cooldownMs?: number; // Minimum time between notifications
32
+ lastTriggeredAt?: string;
33
+
34
+ createdAt: string;
35
+ updatedAt: string;
36
+ }
37
+
38
+ export interface Notification {
39
+ id: string;
40
+ configId?: string;
41
+ type: 'payment_received' | 'payment_sent' | 'recurring_due' | 'invoice_paid' | 'threshold_alert' | 'bridge_complete';
42
+ title: string;
43
+ message: string;
44
+ data: Record<string, any>;
45
+ deliveries: {
46
+ channel: string;
47
+ status: 'pending' | 'sent' | 'failed';
48
+ sentAt?: string;
49
+ error?: string;
50
+ }[];
51
+ createdAt: string;
52
+ readAt?: string;
53
+ }
54
+
55
+ const DATA_DIR = process.env.USDC_DATA_DIR || './data';
56
+
57
+ /**
58
+ * Notification Manager
59
+ */
60
+ export class NotificationManager {
61
+ private configPath: string;
62
+ private historyPath: string;
63
+
64
+ constructor(dataDir = DATA_DIR) {
65
+ this.configPath = path.join(dataDir, 'notification-configs.json');
66
+ this.historyPath = path.join(dataDir, 'notification-history.json');
67
+ }
68
+
69
+ private async loadConfigs(): Promise<NotificationConfig[]> {
70
+ try {
71
+ const data = await fs.readFile(this.configPath, 'utf-8');
72
+ return JSON.parse(data);
73
+ } catch {
74
+ return [];
75
+ }
76
+ }
77
+
78
+ private async saveConfigs(configs: NotificationConfig[]): Promise<void> {
79
+ await fs.mkdir(path.dirname(this.configPath), { recursive: true });
80
+ await fs.writeFile(this.configPath, JSON.stringify(configs, null, 2));
81
+ }
82
+
83
+ private async loadHistory(): Promise<Notification[]> {
84
+ try {
85
+ const data = await fs.readFile(this.historyPath, 'utf-8');
86
+ return JSON.parse(data);
87
+ } catch {
88
+ return [];
89
+ }
90
+ }
91
+
92
+ private async saveHistory(history: Notification[]): Promise<void> {
93
+ await fs.mkdir(path.dirname(this.historyPath), { recursive: true });
94
+ // Keep last 1000 notifications
95
+ const trimmed = history.slice(-1000);
96
+ await fs.writeFile(this.historyPath, JSON.stringify(trimmed, null, 2));
97
+ }
98
+
99
+ // ============ Configuration Management ============
100
+
101
+ /**
102
+ * Create notification configuration
103
+ */
104
+ async createConfig(params: {
105
+ name: string;
106
+ triggers: NotificationConfig['triggers'];
107
+ channels: NotificationConfig['channels'];
108
+ cooldownMs?: number;
109
+ }): Promise<NotificationConfig> {
110
+ const configs = await this.loadConfigs();
111
+
112
+ const config: NotificationConfig = {
113
+ id: crypto.randomUUID(),
114
+ name: params.name,
115
+ enabled: true,
116
+ triggers: params.triggers,
117
+ channels: params.channels,
118
+ cooldownMs: params.cooldownMs,
119
+ createdAt: new Date().toISOString(),
120
+ updatedAt: new Date().toISOString(),
121
+ };
122
+
123
+ configs.push(config);
124
+ await this.saveConfigs(configs);
125
+
126
+ return config;
127
+ }
128
+
129
+ /**
130
+ * Enable/disable notification config
131
+ */
132
+ async toggleConfig(id: string, enabled: boolean): Promise<NotificationConfig | null> {
133
+ const configs = await this.loadConfigs();
134
+ const config = configs.find(c => c.id === id);
135
+
136
+ if (config) {
137
+ config.enabled = enabled;
138
+ config.updatedAt = new Date().toISOString();
139
+ await this.saveConfigs(configs);
140
+ }
141
+
142
+ return config || null;
143
+ }
144
+
145
+ /**
146
+ * List notification configs
147
+ */
148
+ async listConfigs(): Promise<NotificationConfig[]> {
149
+ return this.loadConfigs();
150
+ }
151
+
152
+ /**
153
+ * Delete notification config
154
+ */
155
+ async deleteConfig(id: string): Promise<boolean> {
156
+ const configs = await this.loadConfigs();
157
+ const index = configs.findIndex(c => c.id === id);
158
+
159
+ if (index >= 0) {
160
+ configs.splice(index, 1);
161
+ await this.saveConfigs(configs);
162
+ return true;
163
+ }
164
+
165
+ return false;
166
+ }
167
+
168
+ // ============ Notification Triggering ============
169
+
170
+ /**
171
+ * Process a transaction and trigger matching notifications
172
+ */
173
+ async processTransaction(tx: {
174
+ type: 'incoming' | 'outgoing';
175
+ amount: string;
176
+ fromAddress: string;
177
+ toAddress: string;
178
+ chain: string;
179
+ txHash: string;
180
+ }): Promise<Notification[]> {
181
+ const configs = await this.loadConfigs();
182
+ const triggeredNotifications: Notification[] = [];
183
+ const now = new Date();
184
+
185
+ for (const config of configs) {
186
+ if (!config.enabled) continue;
187
+
188
+ // Check cooldown
189
+ if (config.cooldownMs && config.lastTriggeredAt) {
190
+ const timeSince = now.getTime() - new Date(config.lastTriggeredAt).getTime();
191
+ if (timeSince < config.cooldownMs) continue;
192
+ }
193
+
194
+ // Check triggers
195
+ const matchingTrigger = config.triggers.find(trigger => {
196
+ if (trigger.type !== tx.type) return false;
197
+
198
+ if (trigger.minAmount && parseFloat(tx.amount) < parseFloat(trigger.minAmount)) {
199
+ return false;
200
+ }
201
+
202
+ if (trigger.addresses && trigger.addresses.length > 0) {
203
+ const relevantAddr = tx.type === 'incoming' ? tx.fromAddress : tx.toAddress;
204
+ if (!trigger.addresses.some(a => a.toLowerCase() === relevantAddr.toLowerCase())) {
205
+ return false;
206
+ }
207
+ }
208
+
209
+ if (trigger.chains && trigger.chains.length > 0) {
210
+ if (!trigger.chains.includes(tx.chain)) return false;
211
+ }
212
+
213
+ return true;
214
+ });
215
+
216
+ if (!matchingTrigger) continue;
217
+
218
+ // Create notification
219
+ const notification = await this.createNotification({
220
+ configId: config.id,
221
+ type: tx.type === 'incoming' ? 'payment_received' : 'payment_sent',
222
+ title: tx.type === 'incoming'
223
+ ? `Received ${tx.amount} USDC`
224
+ : `Sent ${tx.amount} USDC`,
225
+ message: tx.type === 'incoming'
226
+ ? `You received ${tx.amount} USDC from ${this.shortAddress(tx.fromAddress)} on ${tx.chain}`
227
+ : `You sent ${tx.amount} USDC to ${this.shortAddress(tx.toAddress)} on ${tx.chain}`,
228
+ data: tx,
229
+ channels: config.channels,
230
+ });
231
+
232
+ triggeredNotifications.push(notification);
233
+
234
+ // Update last triggered
235
+ config.lastTriggeredAt = now.toISOString();
236
+ }
237
+
238
+ await this.saveConfigs(configs);
239
+ return triggeredNotifications;
240
+ }
241
+
242
+ /**
243
+ * Create and deliver a notification
244
+ */
245
+ async createNotification(params: {
246
+ configId?: string;
247
+ type: Notification['type'];
248
+ title: string;
249
+ message: string;
250
+ data: Record<string, any>;
251
+ channels: NotificationConfig['channels'];
252
+ }): Promise<Notification> {
253
+ const notification: Notification = {
254
+ id: crypto.randomUUID(),
255
+ configId: params.configId,
256
+ type: params.type,
257
+ title: params.title,
258
+ message: params.message,
259
+ data: params.data,
260
+ deliveries: params.channels.map(ch => ({
261
+ channel: ch.type,
262
+ status: 'pending' as const,
263
+ })),
264
+ createdAt: new Date().toISOString(),
265
+ };
266
+
267
+ // Attempt delivery to each channel
268
+ for (let i = 0; i < params.channels.length; i++) {
269
+ const channel = params.channels[i];
270
+ try {
271
+ await this.deliver(channel, notification);
272
+ notification.deliveries[i].status = 'sent';
273
+ notification.deliveries[i].sentAt = new Date().toISOString();
274
+ } catch (err: any) {
275
+ notification.deliveries[i].status = 'failed';
276
+ notification.deliveries[i].error = err.message;
277
+ }
278
+ }
279
+
280
+ // Save to history
281
+ const history = await this.loadHistory();
282
+ history.push(notification);
283
+ await this.saveHistory(history);
284
+
285
+ return notification;
286
+ }
287
+
288
+ /**
289
+ * Deliver notification to a channel
290
+ */
291
+ private async deliver(
292
+ channel: NotificationConfig['channels'][0],
293
+ notification: Notification
294
+ ): Promise<void> {
295
+ switch (channel.type) {
296
+ case 'webhook':
297
+ await this.deliverWebhook(channel.config, notification);
298
+ break;
299
+ case 'telegram':
300
+ await this.deliverTelegram(channel.config, notification);
301
+ break;
302
+ case 'clawdbot':
303
+ await this.deliverClawdbot(channel.config, notification);
304
+ break;
305
+ case 'email':
306
+ // Email delivery would require SMTP config
307
+ console.log('Email delivery not implemented');
308
+ break;
309
+ default:
310
+ throw new Error(`Unknown channel type: ${channel.type}`);
311
+ }
312
+ }
313
+
314
+ /**
315
+ * Deliver via webhook
316
+ */
317
+ private async deliverWebhook(
318
+ config: Record<string, string>,
319
+ notification: Notification
320
+ ): Promise<void> {
321
+ if (!config.url) {
322
+ throw new Error('Webhook URL not configured');
323
+ }
324
+
325
+ const payload = {
326
+ id: notification.id,
327
+ type: notification.type,
328
+ title: notification.title,
329
+ message: notification.message,
330
+ data: notification.data,
331
+ timestamp: notification.createdAt,
332
+ };
333
+
334
+ const headers: Record<string, string> = {
335
+ 'Content-Type': 'application/json',
336
+ };
337
+
338
+ // Add optional secret for verification
339
+ if (config.secret) {
340
+ const signature = crypto
341
+ .createHmac('sha256', config.secret)
342
+ .update(JSON.stringify(payload))
343
+ .digest('hex');
344
+ headers['X-Signature'] = signature;
345
+ }
346
+
347
+ const response = await fetch(config.url, {
348
+ method: 'POST',
349
+ headers,
350
+ body: JSON.stringify(payload),
351
+ });
352
+
353
+ if (!response.ok) {
354
+ throw new Error(`Webhook failed: ${response.status}`);
355
+ }
356
+ }
357
+
358
+ /**
359
+ * Deliver via Telegram (would integrate with Clawdbot's messaging)
360
+ */
361
+ private async deliverTelegram(
362
+ config: Record<string, string>,
363
+ notification: Notification
364
+ ): Promise<void> {
365
+ // This would use Clawdbot's message tool
366
+ console.log(`[Telegram] ${notification.title}: ${notification.message}`);
367
+ // In practice, this would call the message API
368
+ }
369
+
370
+ /**
371
+ * Deliver via Clawdbot session message
372
+ */
373
+ private async deliverClawdbot(
374
+ config: Record<string, string>,
375
+ notification: Notification
376
+ ): Promise<void> {
377
+ // This would use sessions_send
378
+ console.log(`[Clawdbot] ${notification.title}: ${notification.message}`);
379
+ // In practice, this would call sessions_send
380
+ }
381
+
382
+ // ============ History & Management ============
383
+
384
+ /**
385
+ * Get notification history
386
+ */
387
+ async getHistory(options?: {
388
+ type?: Notification['type'];
389
+ limit?: number;
390
+ unreadOnly?: boolean;
391
+ }): Promise<Notification[]> {
392
+ let history = await this.loadHistory();
393
+
394
+ if (options?.type) {
395
+ history = history.filter(n => n.type === options.type);
396
+ }
397
+
398
+ if (options?.unreadOnly) {
399
+ history = history.filter(n => !n.readAt);
400
+ }
401
+
402
+ history.sort((a, b) =>
403
+ new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
404
+ );
405
+
406
+ if (options?.limit) {
407
+ history = history.slice(0, options.limit);
408
+ }
409
+
410
+ return history;
411
+ }
412
+
413
+ /**
414
+ * Mark notification as read
415
+ */
416
+ async markRead(id: string): Promise<void> {
417
+ const history = await this.loadHistory();
418
+ const notification = history.find(n => n.id === id);
419
+
420
+ if (notification) {
421
+ notification.readAt = new Date().toISOString();
422
+ await this.saveHistory(history);
423
+ }
424
+ }
425
+
426
+ /**
427
+ * Mark all as read
428
+ */
429
+ async markAllRead(): Promise<void> {
430
+ const history = await this.loadHistory();
431
+ const now = new Date().toISOString();
432
+
433
+ for (const notification of history) {
434
+ if (!notification.readAt) {
435
+ notification.readAt = now;
436
+ }
437
+ }
438
+
439
+ await this.saveHistory(history);
440
+ }
441
+
442
+ /**
443
+ * Get unread count
444
+ */
445
+ async getUnreadCount(): Promise<number> {
446
+ const history = await this.loadHistory();
447
+ return history.filter(n => !n.readAt).length;
448
+ }
449
+
450
+ // ============ Helpers ============
451
+
452
+ /**
453
+ * Shorten address for display
454
+ */
455
+ private shortAddress(address: string): string {
456
+ return `${address.slice(0, 6)}...${address.slice(-4)}`;
457
+ }
458
+
459
+ /**
460
+ * Create default notification configs for a new user
461
+ */
462
+ async createDefaultConfigs(): Promise<NotificationConfig[]> {
463
+ const configs: NotificationConfig[] = [];
464
+
465
+ // Incoming payments
466
+ configs.push(await this.createConfig({
467
+ name: 'All Incoming Payments',
468
+ triggers: [{ type: 'incoming' }],
469
+ channels: [{ type: 'clawdbot', config: {} }],
470
+ }));
471
+
472
+ // Large outgoing (security alert)
473
+ configs.push(await this.createConfig({
474
+ name: 'Large Outgoing (>1000 USDC)',
475
+ triggers: [{ type: 'outgoing', minAmount: '1000' }],
476
+ channels: [{ type: 'clawdbot', config: {} }],
477
+ cooldownMs: 60000, // 1 min cooldown
478
+ }));
479
+
480
+ return configs;
481
+ }
482
+ }
483
+
484
+ export default NotificationManager;