@mcpsovereign/sdk 0.1.0 → 0.2.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.
@@ -0,0 +1,267 @@
1
+ /**
2
+ * Lightning Network Utilities
3
+ *
4
+ * Lightning address validation, LNURL handling, and payment helpers
5
+ */
6
+ // ============================================================
7
+ // LIGHTNING ADDRESS VALIDATION
8
+ // ============================================================
9
+ /**
10
+ * Parse and validate a Lightning address
11
+ * Format: username@domain.com
12
+ */
13
+ export function parseLightningAddress(address) {
14
+ // Basic format check
15
+ const pattern = /^([a-zA-Z0-9._-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$/;
16
+ const match = address.trim().toLowerCase().match(pattern);
17
+ if (!match) {
18
+ return null;
19
+ }
20
+ const [, username, domain] = match;
21
+ return {
22
+ address: `${username}@${domain}`,
23
+ username,
24
+ domain,
25
+ valid: true,
26
+ lnurlPayUrl: `https://${domain}/.well-known/lnurlp/${username}`
27
+ };
28
+ }
29
+ /**
30
+ * Verify a Lightning address is reachable and can receive payments
31
+ */
32
+ export async function verifyLightningAddress(address) {
33
+ const parsed = parseLightningAddress(address);
34
+ if (!parsed) {
35
+ return {
36
+ valid: false,
37
+ error: 'Invalid Lightning address format. Expected: username@domain.com'
38
+ };
39
+ }
40
+ try {
41
+ // Fetch LNURL-pay endpoint
42
+ const response = await fetch(parsed.lnurlPayUrl, {
43
+ headers: { 'Accept': 'application/json' }
44
+ });
45
+ if (!response.ok) {
46
+ return {
47
+ valid: false,
48
+ info: parsed,
49
+ error: `Lightning address not found at ${parsed.domain}`
50
+ };
51
+ }
52
+ const data = await response.json();
53
+ // Verify it's a valid LNURL-pay endpoint
54
+ if (data.tag !== 'payRequest' && !data.callback) {
55
+ return {
56
+ valid: false,
57
+ info: parsed,
58
+ error: 'Invalid LNURL-pay response'
59
+ };
60
+ }
61
+ // Parse metadata
62
+ let description;
63
+ if (data.metadata) {
64
+ try {
65
+ const meta = JSON.parse(data.metadata);
66
+ const textEntry = meta.find(([type]) => type === 'text/plain');
67
+ if (textEntry) {
68
+ description = textEntry[1];
69
+ }
70
+ }
71
+ catch {
72
+ // Ignore metadata parsing errors
73
+ }
74
+ }
75
+ return {
76
+ valid: true,
77
+ info: {
78
+ ...parsed,
79
+ metadata: {
80
+ description,
81
+ minSendable: data.minSendable,
82
+ maxSendable: data.maxSendable,
83
+ nostrPubkey: data.nostrPubkey
84
+ }
85
+ }
86
+ };
87
+ }
88
+ catch (error) {
89
+ return {
90
+ valid: false,
91
+ info: parsed,
92
+ error: `Could not reach ${parsed.domain}: ${error instanceof Error ? error.message : 'Network error'}`
93
+ };
94
+ }
95
+ }
96
+ // ============================================================
97
+ // COMMON LIGHTNING ADDRESS PROVIDERS
98
+ // ============================================================
99
+ export const KNOWN_PROVIDERS = {
100
+ 'getalby.com': {
101
+ name: 'Alby',
102
+ domain: 'getalby.com',
103
+ signupUrl: 'https://getalby.com'
104
+ },
105
+ 'walletofsatoshi.com': {
106
+ name: 'Wallet of Satoshi',
107
+ domain: 'walletofsatoshi.com',
108
+ signupUrl: 'https://walletofsatoshi.com'
109
+ },
110
+ 'ln.tips': {
111
+ name: 'ln.tips',
112
+ domain: 'ln.tips',
113
+ signupUrl: 'https://ln.tips'
114
+ },
115
+ 'stacker.news': {
116
+ name: 'Stacker News',
117
+ domain: 'stacker.news',
118
+ signupUrl: 'https://stacker.news'
119
+ },
120
+ 'strike.me': {
121
+ name: 'Strike',
122
+ domain: 'strike.me',
123
+ signupUrl: 'https://strike.me'
124
+ },
125
+ 'zbd.gg': {
126
+ name: 'ZBD',
127
+ domain: 'zbd.gg',
128
+ signupUrl: 'https://zbd.gg'
129
+ }
130
+ };
131
+ /**
132
+ * Get provider info from a Lightning address
133
+ */
134
+ export function getProviderFromAddress(address) {
135
+ const parsed = parseLightningAddress(address);
136
+ if (!parsed)
137
+ return null;
138
+ const known = KNOWN_PROVIDERS[parsed.domain];
139
+ if (known) {
140
+ return {
141
+ provider: known.name,
142
+ signupUrl: known.signupUrl
143
+ };
144
+ }
145
+ return {
146
+ provider: parsed.domain,
147
+ signupUrl: `https://${parsed.domain}`
148
+ };
149
+ }
150
+ // ============================================================
151
+ // PAYMENT HELPERS
152
+ // ============================================================
153
+ /**
154
+ * Generate a payment request for a Lightning address
155
+ */
156
+ export async function createPaymentRequest(address, amountSats, comment) {
157
+ const parsed = parseLightningAddress(address);
158
+ if (!parsed) {
159
+ return { success: false, error: 'Invalid Lightning address' };
160
+ }
161
+ try {
162
+ // Get LNURL-pay callback
163
+ const lnurlResponse = await fetch(parsed.lnurlPayUrl);
164
+ const lnurlData = await lnurlResponse.json();
165
+ // Convert sats to millisats
166
+ const amountMsats = amountSats * 1000;
167
+ // Check limits
168
+ if (amountMsats < lnurlData.minSendable) {
169
+ return {
170
+ success: false,
171
+ error: `Amount too small. Minimum: ${lnurlData.minSendable / 1000} sats`
172
+ };
173
+ }
174
+ if (amountMsats > lnurlData.maxSendable) {
175
+ return {
176
+ success: false,
177
+ error: `Amount too large. Maximum: ${lnurlData.maxSendable / 1000} sats`
178
+ };
179
+ }
180
+ // Request invoice
181
+ const callbackUrl = new URL(lnurlData.callback);
182
+ callbackUrl.searchParams.set('amount', amountMsats.toString());
183
+ if (comment) {
184
+ callbackUrl.searchParams.set('comment', comment);
185
+ }
186
+ const invoiceResponse = await fetch(callbackUrl.toString());
187
+ const invoiceData = await invoiceResponse.json();
188
+ if (!invoiceData.pr) {
189
+ return {
190
+ success: false,
191
+ error: invoiceData.reason || 'Failed to generate invoice'
192
+ };
193
+ }
194
+ // Extract payment hash from invoice (it's in the BOLT11 encoded)
195
+ const paymentHash = extractPaymentHash(invoiceData.pr);
196
+ return {
197
+ success: true,
198
+ invoice: invoiceData.pr,
199
+ paymentHash
200
+ };
201
+ }
202
+ catch (error) {
203
+ return {
204
+ success: false,
205
+ error: `Payment request failed: ${error instanceof Error ? error.message : 'Unknown error'}`
206
+ };
207
+ }
208
+ }
209
+ /**
210
+ * Extract payment hash from BOLT11 invoice
211
+ * (Simplified - in production use a proper decoder)
212
+ */
213
+ function extractPaymentHash(bolt11) {
214
+ // BOLT11 payment hash is typically in a specific position after decoding
215
+ // For simplicity, we'll generate a placeholder
216
+ // In production, use a library like bolt11 or lightningpay
217
+ try {
218
+ const hash = bolt11.substring(0, 64);
219
+ return hash;
220
+ }
221
+ catch {
222
+ return undefined;
223
+ }
224
+ }
225
+ // ============================================================
226
+ // CREDIT CONVERSION
227
+ // ============================================================
228
+ // mcpSovereign: 1,000 credits = $1 = 10,000 sats
229
+ export const CREDITS_PER_SAT = 0.1; // 1 sat = 0.1 credits
230
+ export const SATS_PER_CREDIT = 10; // 1 credit = 10 sats
231
+ export function creditsToSats(credits) {
232
+ return Math.ceil(credits * SATS_PER_CREDIT);
233
+ }
234
+ export function satsToCredits(sats) {
235
+ return Math.floor(sats * CREDITS_PER_SAT);
236
+ }
237
+ export function satsToDollars(sats, btcPrice = 100000) {
238
+ // 1 BTC = 100,000,000 sats
239
+ return (sats / 100_000_000) * btcPrice;
240
+ }
241
+ export function dollarsToSats(dollars, btcPrice = 100000) {
242
+ return Math.ceil((dollars / btcPrice) * 100_000_000);
243
+ }
244
+ // ============================================================
245
+ // FORMATTING
246
+ // ============================================================
247
+ export function formatSats(sats) {
248
+ if (sats >= 100_000_000) {
249
+ return `${(sats / 100_000_000).toFixed(2)} BTC`;
250
+ }
251
+ if (sats >= 1_000_000) {
252
+ return `${(sats / 1_000_000).toFixed(2)}M sats`;
253
+ }
254
+ if (sats >= 1_000) {
255
+ return `${(sats / 1_000).toFixed(1)}k sats`;
256
+ }
257
+ return `${sats} sats`;
258
+ }
259
+ export function formatCredits(credits) {
260
+ if (credits >= 1_000_000) {
261
+ return `${(credits / 1_000_000).toFixed(2)}M credits`;
262
+ }
263
+ if (credits >= 1_000) {
264
+ return `${(credits / 1_000).toFixed(1)}k credits`;
265
+ }
266
+ return `${credits} credits`;
267
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Automated LND Setup for mcpSovereign
3
+ *
4
+ * This module handles:
5
+ * 1. Docker installation check
6
+ * 2. LND container deployment (Neutrino mode - no full node needed)
7
+ * 3. Wallet creation
8
+ * 4. Credential extraction
9
+ * 5. Configuration for SDK
10
+ *
11
+ * Based on the same setup used for mcpsovereign.com
12
+ */
13
+ export interface LNDConfig {
14
+ containerName: string;
15
+ network: 'mainnet' | 'testnet' | 'regtest';
16
+ alias: string;
17
+ restPort: number;
18
+ grpcPort: number;
19
+ p2pPort: number;
20
+ dataDir: string;
21
+ }
22
+ export interface LNDCredentials {
23
+ tlsCert: string;
24
+ adminMacaroon: string;
25
+ restHost: string;
26
+ grpcHost: string;
27
+ }
28
+ export interface SetupResult {
29
+ success: boolean;
30
+ credentials?: LNDCredentials;
31
+ walletSeed?: string[];
32
+ error?: string;
33
+ logs: string[];
34
+ }
35
+ export declare class LNDSetup {
36
+ private config;
37
+ private logs;
38
+ constructor(config?: Partial<LNDConfig>);
39
+ private log;
40
+ /**
41
+ * Check if system is ready for LND setup
42
+ */
43
+ checkPrerequisites(): Promise<{
44
+ ready: boolean;
45
+ docker: {
46
+ installed: boolean;
47
+ running: boolean;
48
+ version?: string;
49
+ };
50
+ compose: boolean;
51
+ diskSpace: {
52
+ available: number;
53
+ required: number;
54
+ sufficient: boolean;
55
+ };
56
+ }>;
57
+ /**
58
+ * Install Docker if not present (Linux only)
59
+ */
60
+ installDocker(): Promise<boolean>;
61
+ /**
62
+ * Set up LND container
63
+ */
64
+ setupLND(): Promise<SetupResult>;
65
+ /**
66
+ * Wait for LND REST API to be available
67
+ */
68
+ private waitForLND;
69
+ /**
70
+ * Check if wallet already exists
71
+ */
72
+ private checkWalletExists;
73
+ /**
74
+ * Create new LND wallet
75
+ */
76
+ private createWallet;
77
+ /**
78
+ * Wait for Neutrino to sync
79
+ */
80
+ private waitForSync;
81
+ /**
82
+ * Extract LND credentials for SDK use
83
+ */
84
+ private extractCredentials;
85
+ /**
86
+ * Generate a secure random password
87
+ */
88
+ private generatePassword;
89
+ /**
90
+ * Get container status
91
+ */
92
+ getStatus(): Promise<{
93
+ running: boolean;
94
+ synced: boolean;
95
+ blockHeight?: number;
96
+ peers?: number;
97
+ channels?: number;
98
+ balance?: {
99
+ confirmed: number;
100
+ unconfirmed: number;
101
+ };
102
+ }>;
103
+ /**
104
+ * Stop LND container
105
+ */
106
+ stop(): Promise<boolean>;
107
+ /**
108
+ * Start LND container (if stopped)
109
+ */
110
+ start(): Promise<boolean>;
111
+ getLogs(): string[];
112
+ }
113
+ /**
114
+ * Quick setup for agents who want their own node
115
+ */
116
+ export declare function quickSetupLND(alias?: string): Promise<SetupResult>;
117
+ export default LNDSetup;