moltspay 1.4.0 → 1.5.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.
package/.env.example CHANGED
@@ -60,3 +60,17 @@ CDP_API_KEY_SECRET=
60
60
  # https://facilitator.questflow.ai/
61
61
  # QUESTFLOW_ENDPOINT=https://facilitator.questflow.ai
62
62
  # QUESTFLOW_API_KEY=
63
+
64
+ # ===========================================
65
+ # Solana Relay Fee Payer
66
+ # ===========================================
67
+ # Private key for paying Solana transaction fees (base58 encoded)
68
+ # Required for server-side settlement on Solana chains
69
+ # SOLANA_FEE_PAYER_KEY=
70
+
71
+ # ===========================================
72
+ # BNB Chain Relay Fee Payer
73
+ # ===========================================
74
+ # Private key for paying BNB Chain transaction fees
75
+ # Required for server-side settlement on BNB chains (bsc, opbnb)
76
+ # BNB_SERVER_PRIVATE_KEY=
package/README.md CHANGED
@@ -19,14 +19,15 @@ MoltsPay enables agent-to-agent commerce using the [x402 protocol](https://www.x
19
19
 
20
20
  ## Features
21
21
 
22
- - 🔌 **Skill Integration** - Add `moltspay.services.json` to any existing skill
23
- - 🎫 **x402 Protocol** - HTTP-native payments (402 Payment Required)
24
- - 💨 **Gasless** - Both client and server pay no gas (facilitators handle it)
25
- - **Payment Verification** - Automatic on-chain verification
26
- - 🔒 **Secure Wallet** - Spending limits, whitelist, and audit logging
27
- - ⛓️ **Multi-chain** - Base, Polygon, Solana, BNB, Tempo (mainnet & testnet)
28
- - 🤖 **Agent-to-Agent** - Complete A2A payment flow support
29
- - 🌐 **Multi-VM** - EVM chains + Solana (SVM) with unified API
22
+ - **Skill Integration** - Add `moltspay.services.json` to any existing skill
23
+ - **x402 Protocol** - HTTP-native payments (402 Payment Required)
24
+ - **Gasless** - Both client and server pay no gas (facilitators handle it)
25
+ - **Payment Verification** - Automatic on-chain verification
26
+ - **Secure Wallet** - Spending limits, whitelist, and audit logging
27
+ - **Multi-chain** - Base, Polygon, Solana, BNB, Tempo (mainnet & testnet)
28
+ - **Agent-to-Agent** - Complete A2A payment flow support
29
+ - **Multi-VM** - EVM chains + Solana (SVM) with unified API
30
+ - **MCP Server** - Expose wallet + payments to Claude Desktop, Cursor, and other MCP hosts
30
31
 
31
32
  ## Installation
32
33
 
@@ -133,6 +134,50 @@ npx moltspay pay https://server.com service-id \
133
134
  --chain tempo_moderato --prompt "test"
134
135
  ```
135
136
 
137
+ ## MCP Server (For AI Assistants)
138
+
139
+ MoltsPay ships an [MCP (Model Context Protocol)](https://modelcontextprotocol.io) stdio server that lets MCP-compatible hosts (Cursor, Windsurf, Claude Code, Zed, etc.) browse services, check wallet status, and pay for x402 services on your behalf.
140
+
141
+ It is a thin wrapper around `MoltsPayClient` — wallet custody, spending limits, and all payment protocols (x402, MPP, Solana, BNB) are reused from the SDK.
142
+
143
+ ### Setup
144
+
145
+ **1. Create a wallet and set spending limits** (the MCP server refuses to start without a wallet):
146
+
147
+ ```bash
148
+ npx moltspay init
149
+ npx moltspay config --max-per-tx 2 --max-per-day 10
150
+ npx moltspay fund 5 # or: npx moltspay faucet for testnet
151
+ ```
152
+
153
+ **2. Point your MCP host at the `moltspay-mcp` binary over stdio:**
154
+
155
+ ```bash
156
+ npx -y moltspay-mcp # normal mode
157
+ npx -y moltspay-mcp --dry-run # preview payments without signing
158
+ ```
159
+
160
+ Each host has its own config file for registering stdio MCP servers — check your host's docs for the exact location. For a safer first run, use `--dry-run` so `moltspay_pay` returns a preview instead of spending real funds.
161
+
162
+ ### Tools
163
+
164
+ | Tool | What it does | Destructive? |
165
+ |---|---|---|
166
+ | `moltspay_status` | Wallet address, balances across all supported chains, spending limits | No |
167
+ | `moltspay_services` | Fetch services manifest from a provider URL; optional `query`/`maxPrice` filter | No |
168
+ | `moltspay_pay` | Execute an x402/MPP/SOL/BNB payment and return the service result | **Yes** |
169
+ | `moltspay_config` | Read or update `maxPerTx` / `maxPerDay` limits | Updates config file |
170
+
171
+ ### Safety Layers
172
+
173
+ `moltspay_pay` is the only tool that moves money. Three guards stack on top of the MCP host's own tool-approval prompt:
174
+
175
+ 1. **SDK spending limits** — `maxPerTx` / `maxPerDay` enforced before signing.
176
+ 2. **Dry-run mode** — launch with `--dry-run` and payments return a preview instead of signing.
177
+ 3. **Confirmation gate** — set `MOLTSPAY_MCP_REQUIRE_CONFIRM=1` to require a second tool call (`confirmed: true`) for any payment exceeding `maxPerTx / 10`.
178
+
179
+ Private keys and mnemonics are never exposed over MCP — wallet creation stays on the CLI (`npx moltspay init`) by design. See [`docs/MCP-USAGE.md`](docs/MCP-USAGE.md) for full tool arguments and troubleshooting.
180
+
136
181
  ## Payment Protocols
137
182
 
138
183
  MoltsPay supports multiple payment protocols, each optimized for different chains:
@@ -148,24 +193,24 @@ MoltsPay supports multiple payment protocols, each optimized for different chain
148
193
 
149
194
  ```
150
195
  Client Server CDP Facilitator
151
-
152
- POST /execute
153
- ─────────────────────────>
154
-
155
- 402 + payment requirements
156
- <─────────────────────────
157
-
158
- [Sign EIP-3009 - NO GAS]
159
-
160
- POST + X-Payment header
161
- ─────────────────────────> Verify signature
162
- ─────────────────────────>
163
-
164
- Execute transfer (pays gas)
165
- <─────────────────────────
166
-
167
- 200 OK + result
168
- <─────────────────────────
196
+ | | |
197
+ | POST /execute | |
198
+ | --------------------------------------------------> | |
199
+ | | |
200
+ | 402 + payment requirements | |
201
+ | <-------------------------------------------------- | |
202
+ | | |
203
+ | [Sign EIP-3009 - NO GAS] | |
204
+ | | |
205
+ | POST + X-Payment header | |
206
+ | --------------------------------------------------> | Verify signature |
207
+ | | --------------------------------------------------> |
208
+ | | |
209
+ | | Execute transfer (pays gas) |
210
+ | | <-------------------------------------------------- |
211
+ | | |
212
+ | 200 OK + result | |
213
+ | <-------------------------------------------------- | |
169
214
  ```
170
215
 
171
216
  **Key insight:** Client signs a payment authorization, server submits it. Neither party pays gas - the CDP facilitator handles settlement.
@@ -176,23 +221,23 @@ MPP (Machine Payments Protocol) is simpler - client executes the transfer direct
176
221
 
177
222
  ```
178
223
  Client Server
179
-
180
- POST /service
181
- ─────────────────────────>
182
-
183
- 402 + WWW-Authenticate
184
- <─────────────────────────
185
-
186
- [Execute TIP-20 transfer]
187
- [No gas needed on Tempo]
188
-
189
- POST + Authorization: Payment
190
- ─────────────────────────>
191
-
192
- [Server verifies on-chain]
193
-
194
- 200 OK + Payment-Receipt
195
- <─────────────────────────
224
+ | |
225
+ | POST /service |
226
+ | --------------------------------------------------> |
227
+ | |
228
+ | 402 + WWW-Authenticate |
229
+ | <-------------------------------------------------- |
230
+ | |
231
+ | [Execute TIP-20 transfer] |
232
+ | [No gas needed on Tempo] |
233
+ | |
234
+ | POST + Authorization: Payment|
235
+ | --------------------------------------------------> |
236
+ | |
237
+ | [Server verifies on-chain] |
238
+ | |
239
+ | 200 OK + Payment-Receipt |
240
+ | <-------------------------------------------------- |
196
241
  ```
197
242
 
198
243
  **Key insight:** On Tempo, the client executes the transfer directly (gas-free), then retries with the transaction hash. No CDP facilitator needed.
@@ -201,24 +246,24 @@ Client Server
201
246
 
202
247
  ```
203
248
  Client Server (Fee Payer) Solana Network
204
-
205
- POST /execute
206
- ─────────────────────────>
207
-
208
- 402 + payment requirements
209
- (includes solana_wallet)
210
- <─────────────────────────
211
-
212
- [Sign SPL Transfer]
213
- [NO GAS - just signing]
214
-
215
- POST + X-Payment (signature)
216
- ─────────────────────────> Execute transfer
217
- (server pays ~$0.001 SOL)
218
- ─────────────────────────>
219
-
220
- 200 OK + result
221
- <─────────────────────────
249
+ | | |
250
+ | POST /execute | |
251
+ | --------------------------------------------------> | |
252
+ | | |
253
+ | 402 + payment requirements | |
254
+ | (includes solana_wallet) | |
255
+ | <-------------------------------------------------- | |
256
+ | | |
257
+ | [Sign SPL Transfer] | |
258
+ | [NO GAS - just signing] | |
259
+ | | |
260
+ | POST + X-Payment (signature) | |
261
+ | --------------------------------------------------> | Execute transfer |
262
+ | | (server pays ~$0.001 SOL) |
263
+ | | --------------------------------------------------> |
264
+ | | |
265
+ | 200 OK + result | |
266
+ | <-------------------------------------------------- | |
222
267
  ```
223
268
 
224
269
  **Key insight:** Client only signs the SPL transfer (gasless). Server acts as fee payer and executes the transaction on-chain.
@@ -227,24 +272,24 @@ Client Server (Fee Payer) Solana Network
227
272
 
228
273
  ```
229
274
  Client Server BNB Network
230
-
231
- POST /execute
232
- ─────────────────────────>
233
-
234
- 402 + payment requirements
235
- (includes bnbSpender)
236
- <─────────────────────────
237
-
238
- [Sign EIP-712 intent]
239
- [NO GAS - just signing]
240
-
241
- POST + X-Payment (signature)
242
- ─────────────────────────> Execute transferFrom
243
- (server pays ~$0.0001 gas)
244
- ─────────────────────────>
245
-
246
- 200 OK + result
247
- <─────────────────────────
275
+ | | |
276
+ | POST /execute | |
277
+ | --------------------------------------------------> | |
278
+ | | |
279
+ | 402 + payment requirements | |
280
+ | (includes bnbSpender) | |
281
+ | <-------------------------------------------------- | |
282
+ | | |
283
+ | [Sign EIP-712 intent] | |
284
+ | [NO GAS - just signing] | |
285
+ | | |
286
+ | POST + X-Payment (signature) | |
287
+ | --------------------------------------------------> | Execute transferFrom |
288
+ | | (server pays ~$0.0001 gas) |
289
+ | | --------------------------------------------------> |
290
+ | | |
291
+ | 200 OK + result | |
292
+ | <-------------------------------------------------- | |
248
293
  ```
249
294
 
250
295
  **Key insight:** Client only signs an intent (gasless). Server executes the actual transfer and pays the minimal gas (~$0.0001). This is the "pay-for-success" model - payment only happens if service succeeds.
@@ -255,9 +300,9 @@ MoltsPay reads your skill's existing structure:
255
300
 
256
301
  ```
257
302
  my-skill/
258
- ├── package.json # MoltsPay reads "main" field
259
- ├── index.js # Your existing exports
260
- └── moltspay.services.json # Only file you add!
303
+ +------ package.json # MoltsPay reads "main" field
304
+ +------ index.js # Your existing exports
305
+ +------ moltspay.services.json # Only file you add!
261
306
  ```
262
307
 
263
308
  **Your functions stay untouched.** Just add the JSON config.
@@ -350,8 +395,8 @@ Server does NOT need a private key - the x402 facilitator handles settlement.
350
395
 
351
396
  The server automatically detects which chain to verify payments on based on the client's payment header:
352
397
 
353
- - Client pays with `--chain base` Server verifies on Base mainnet
354
- - Client pays with `--chain base_sepolia` Server verifies on Base Sepolia
398
+ - Client pays with `--chain base` -> Server verifies on Base mainnet
399
+ - Client pays with `--chain base_sepolia` -> Server verifies on Base Sepolia
355
400
 
356
401
  **No `USE_MAINNET` env var needed!** Just configure your accepted chains in the manifest.
357
402
 
@@ -425,16 +470,63 @@ npx moltspay validate <path> # Validate manifest
425
470
  ```typescript
426
471
  import { MoltsPayClient } from 'moltspay/client';
427
472
 
428
- const client = new MoltsPayClient({ chain: 'base' });
473
+ // Initialize client (uses wallet from ~/.moltspay/wallet.json)
474
+ const client = new MoltsPayClient();
429
475
 
430
- // Pay for a service
431
- const result = await client.execute('https://server.com', 'text-to-video', {
432
- prompt: 'a cat dancing'
433
- });
476
+ // Standard service call (params wrapped in { params: {...} })
477
+ const result = await client.pay(
478
+ 'https://server.com',
479
+ 'text-to-video',
480
+ { prompt: 'a cat dancing' },
481
+ { chain: 'base' }
482
+ );
434
483
 
435
484
  console.log(result.video_url);
436
485
  ```
437
486
 
487
+ #### Custom Input Formats (rawData)
488
+
489
+ Some services have custom input formats instead of the standard `{ params: { prompt } }`.
490
+ Use `rawData: true` to send your data at the top level:
491
+
492
+ ```typescript
493
+ // Service expects: { text: "...", target_lang: "..." }
494
+ // NOT: { params: { text: "...", target_lang: "..." } }
495
+
496
+ const result = await client.pay(
497
+ 'https://server.com',
498
+ 'translate',
499
+ { text: 'Hello world', target_lang: 'es' },
500
+ {
501
+ chain: 'base_sepolia',
502
+ rawData: true // Send data at top level
503
+ }
504
+ );
505
+
506
+ // Server receives: { service: "translate", text: "Hello world", target_lang: "es", chain: "base_sepolia" }
507
+ ```
508
+
509
+ #### PayOptions Reference
510
+
511
+ ```typescript
512
+ interface PayOptions {
513
+ token?: 'USDC' | 'USDT'; // Token to pay with (default: USDC)
514
+ autoSelect?: boolean; // Auto-select token based on balance
515
+ chain?: string; // Chain: base, polygon, solana, bnb, tempo_moderato, + testnets
516
+ rawData?: boolean; // Send data at top level (for custom input formats)
517
+ }
518
+ ```
519
+
520
+ #### CLI Equivalent
521
+
522
+ ```bash
523
+ # Standard format (uses { params: { prompt } })
524
+ npx moltspay pay https://server.com text-to-video --prompt "a cat dancing"
525
+
526
+ # Custom format (uses rawData, sends at top level)
527
+ npx moltspay pay https://server.com translate --data '{"text": "Hello", "target_lang": "es"}'
528
+ ```
529
+
438
530
  ### Server
439
531
 
440
532
  ```typescript
@@ -493,9 +585,9 @@ A **facilitator** is the entity that executes the on-chain payment and pays the
493
585
 
494
586
  | Aspect | CDP (External) | Self-hosted |
495
587
  |--------|----------------|-------------|
496
- | Single point of failure | Coinbase down = everyone stuck | Each provider independent |
497
- | Censorship risk | Coinbase can block accounts | Cannot be censored |
498
- | Dependency | Relies on third-party | Fully autonomous |
588
+ | Single point of failure | Coinbase down = everyone stuck | Each provider independent |
589
+ | Censorship risk | Coinbase can block accounts | Cannot be censored |
590
+ | Dependency | Relies on third-party | Fully autonomous |
499
591
 
500
592
  This self-hosted approach is a key innovation: **any service provider can become their own facilitator** without relying on third-party infrastructure. Unlike CDP where all users depend on Coinbase, self-hosted facilitators create a truly decentralized network with no single point of failure.
501
593
 
@@ -628,7 +720,7 @@ npx moltspay pay https://moltspay.com/a/zen7 text-to-video --chain solana_devnet
628
720
 
629
721
  Join our Discord for help, feedback, and updates:
630
722
 
631
- 👉 **[MoltsPay Discord](https://discord.gg/QwCJgVBxVK)**
723
+ --> **[MoltsPay Discord](https://discord.gg/QwCJgVBxVK)**
632
724
 
633
725
  Or visit the [#moltspay-support](https://discord.com/channels/1472602423267819734/1480968496346304522) channel directly.
634
726
 
package/dist/cli/index.js CHANGED
@@ -682,11 +682,26 @@ var MoltsPayClient = class {
682
682
  throw new Error("Client not initialized. Run: npx moltspay init");
683
683
  }
684
684
  console.log(`[MoltsPay] Requesting service: ${service}`);
685
- const requestBody = { service, params };
685
+ let executeUrl = `${serverUrl}/execute`;
686
+ try {
687
+ const services = await this.getServices(serverUrl);
688
+ const svc = services.services?.find((s) => s.id === service);
689
+ if (svc?.endpoint) {
690
+ executeUrl = `${serverUrl}${svc.endpoint}`;
691
+ console.log(`[MoltsPay] Using service endpoint: ${svc.endpoint}`);
692
+ }
693
+ } catch {
694
+ }
695
+ let requestBody;
696
+ if (options.rawData) {
697
+ requestBody = { service, ...params };
698
+ } else {
699
+ requestBody = { service, params };
700
+ }
686
701
  if (options.chain) {
687
702
  requestBody.chain = options.chain;
688
703
  }
689
- const initialRes = await fetch(`${serverUrl}/execute`, {
704
+ const initialRes = await fetch(executeUrl, {
690
705
  method: "POST",
691
706
  headers: { "Content-Type": "application/json" },
692
707
  body: JSON.stringify(requestBody)
@@ -702,7 +717,7 @@ var MoltsPayClient = class {
702
717
  const paymentRequiredHeader = initialRes.headers.get(PAYMENT_REQUIRED_HEADER);
703
718
  if (wwwAuthHeader && wwwAuthHeader.toLowerCase().includes("payment")) {
704
719
  console.log("[MoltsPay] Detected MPP protocol, using Tempo flow...");
705
- return await this.handleMPPPayment(serverUrl, service, params, wwwAuthHeader);
720
+ return await this.handleMPPPayment(executeUrl, service, params, wwwAuthHeader, options);
706
721
  }
707
722
  if (!paymentRequiredHeader) {
708
723
  throw new Error("Missing payment header (x-payment-required or www-authenticate)");
@@ -763,7 +778,7 @@ Please specify: --chain <chain_name>`
763
778
  if (!req2) {
764
779
  throw new Error(`Failed to find payment requirement for ${selectedChain}`);
765
780
  }
766
- return await this.handleSolanaPayment(serverUrl, service, params, req2, solanaChain);
781
+ return await this.handleSolanaPayment(executeUrl, service, params, req2, solanaChain, options);
767
782
  }
768
783
  const chainName = selectedChain;
769
784
  const chain = getChain(chainName);
@@ -810,14 +825,14 @@ Please specify: --chain <chain_name>`
810
825
  if (!bnbSpender) {
811
826
  throw new Error("Server did not provide bnbSpender address. Server may not support BNB payments.");
812
827
  }
813
- return await this.handleBNBPayment(serverUrl, service, params, {
828
+ return await this.handleBNBPayment(executeUrl, service, params, {
814
829
  to: payTo2,
815
830
  amount,
816
831
  token,
817
832
  chainName,
818
833
  chain,
819
834
  spender: bnbSpender
820
- });
835
+ }, options);
821
836
  }
822
837
  const payTo = req.payTo || req.resource;
823
838
  if (!payTo) {
@@ -848,11 +863,11 @@ Please specify: --chain <chain_name>`
848
863
  };
849
864
  const paymentHeader = Buffer.from(JSON.stringify(payload)).toString("base64");
850
865
  console.log(`[MoltsPay] Sending request with payment...`);
851
- const paidRequestBody = { service, params };
866
+ const paidRequestBody = options.rawData ? { service, ...params } : { service, params };
852
867
  if (options.chain) {
853
868
  paidRequestBody.chain = options.chain;
854
869
  }
855
- const paidRes = await fetch(`${serverUrl}/execute`, {
870
+ const paidRes = await fetch(executeUrl, {
856
871
  method: "POST",
857
872
  headers: {
858
873
  "Content-Type": "application/json",
@@ -866,13 +881,13 @@ Please specify: --chain <chain_name>`
866
881
  }
867
882
  this.recordSpending(amount);
868
883
  console.log(`[MoltsPay] Success! Payment: ${result.payment?.status || "claimed"}`);
869
- return result.result;
884
+ return result.result || result;
870
885
  }
871
886
  /**
872
887
  * Handle MPP (Machine Payments Protocol) payment flow
873
888
  * Called when pay() detects WWW-Authenticate header in 402 response
874
889
  */
875
- async handleMPPPayment(serverUrl, service, params, wwwAuthHeader) {
890
+ async handleMPPPayment(executeUrl, service, params, wwwAuthHeader, options = {}) {
876
891
  const { privateKeyToAccount: privateKeyToAccount2 } = await import("viem/accounts");
877
892
  const { createWalletClient, createPublicClient, http } = await import("viem");
878
893
  const { tempoModerato } = await import("viem/chains");
@@ -933,13 +948,14 @@ Please specify: --chain <chain_name>`
933
948
  source: `did:pkh:eip155:${chainId}:${account.address}`
934
949
  };
935
950
  const credentialB64 = Buffer.from(JSON.stringify(credential)).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
936
- const paidRes = await fetch(`${serverUrl}/execute`, {
951
+ const retryBody = options.rawData ? { service, ...params, chain: "tempo_moderato" } : { service, params, chain: "tempo_moderato" };
952
+ const paidRes = await fetch(executeUrl, {
937
953
  method: "POST",
938
954
  headers: {
939
955
  "Content-Type": "application/json",
940
956
  "Authorization": `Payment ${credentialB64}`
941
957
  },
942
- body: JSON.stringify({ service, params, chain: "tempo_moderato" })
958
+ body: JSON.stringify(retryBody)
943
959
  });
944
960
  const result = await paidRes.json();
945
961
  if (!paidRes.ok) {
@@ -959,7 +975,7 @@ Please specify: --chain <chain_name>`
959
975
  * 4. Server executes service
960
976
  * 5. Server calls transferFrom if successful (pay-for-success)
961
977
  */
962
- async handleBNBPayment(serverUrl, service, params, paymentDetails) {
978
+ async handleBNBPayment(executeUrl, service, params, paymentDetails, options = {}) {
963
979
  const { to, amount, token, chainName, chain, spender } = paymentDetails;
964
980
  const tokenConfig = chain.tokens[token];
965
981
  const provider = new import_ethers.ethers.JsonRpcProvider(chain.rpc);
@@ -1055,13 +1071,14 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1055
1071
  };
1056
1072
  const paymentHeader = Buffer.from(JSON.stringify(payload)).toString("base64");
1057
1073
  console.log(`[MoltsPay] Sending BNB payment request...`);
1058
- const paidRes = await fetch(`${serverUrl}/execute`, {
1074
+ const bnbRequestBody = options.rawData ? { service, ...params, chain: chainName } : { service, params, chain: chainName };
1075
+ const paidRes = await fetch(executeUrl, {
1059
1076
  method: "POST",
1060
1077
  headers: {
1061
1078
  "Content-Type": "application/json",
1062
1079
  "X-Payment": paymentHeader
1063
1080
  },
1064
- body: JSON.stringify({ service, params, chain: chainName })
1081
+ body: JSON.stringify(bnbRequestBody)
1065
1082
  });
1066
1083
  const result = await paidRes.json();
1067
1084
  if (!paidRes.ok) {
@@ -1078,7 +1095,7 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1078
1095
  * 1. Client creates and signs a transfer transaction
1079
1096
  * 2. Server submits the transaction after service completes
1080
1097
  */
1081
- async handleSolanaPayment(serverUrl, service, params, requirements, chain) {
1098
+ async handleSolanaPayment(executeUrl, service, params, requirements, chain, options = {}) {
1082
1099
  const solanaWallet = loadSolanaWallet(this.configDir);
1083
1100
  if (!solanaWallet) {
1084
1101
  throw new Error("No Solana wallet found. Run: npx moltspay init --chain solana_devnet");
@@ -1131,13 +1148,14 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1131
1148
  }
1132
1149
  };
1133
1150
  const paymentHeader = Buffer.from(JSON.stringify(payload)).toString("base64");
1134
- const paidRes = await fetch(`${serverUrl}/execute`, {
1151
+ const solanaRequestBody = options.rawData ? { service, ...params, chain } : { service, params, chain };
1152
+ const paidRes = await fetch(executeUrl, {
1135
1153
  method: "POST",
1136
1154
  headers: {
1137
1155
  "Content-Type": "application/json",
1138
1156
  "X-Payment": paymentHeader
1139
1157
  },
1140
- body: JSON.stringify({ service, params, chain })
1158
+ body: JSON.stringify(solanaRequestBody)
1141
1159
  });
1142
1160
  const result = await paidRes.json();
1143
1161
  if (!paidRes.ok) {
@@ -1282,15 +1300,17 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1282
1300
  loadWallet() {
1283
1301
  const walletPath = (0, import_path2.join)(this.configDir, "wallet.json");
1284
1302
  if ((0, import_fs2.existsSync)(walletPath)) {
1285
- try {
1286
- const stats = (0, import_fs2.statSync)(walletPath);
1287
- const mode = stats.mode & 511;
1288
- if (mode !== 384) {
1289
- console.warn(`[MoltsPay] WARNING: wallet.json has insecure permissions (${mode.toString(8)})`);
1290
- console.warn(`[MoltsPay] Fixing permissions to 0600...`);
1291
- (0, import_fs2.chmodSync)(walletPath, 384);
1303
+ if (process.platform !== "win32") {
1304
+ try {
1305
+ const stats = (0, import_fs2.statSync)(walletPath);
1306
+ const mode = stats.mode & 511;
1307
+ if (mode !== 384) {
1308
+ console.warn(`[MoltsPay] WARNING: wallet.json has insecure permissions (${mode.toString(8)})`);
1309
+ console.warn(`[MoltsPay] Fixing permissions to 0600...`);
1310
+ (0, import_fs2.chmodSync)(walletPath, 384);
1311
+ }
1312
+ } catch {
1292
1313
  }
1293
- } catch (err) {
1294
1314
  }
1295
1315
  const content = (0, import_fs2.readFileSync)(walletPath, "utf-8");
1296
1316
  return JSON.parse(content);
@@ -3273,8 +3293,10 @@ var MoltsPayServer = class {
3273
3293
  isProxyAllowed(clientIP) {
3274
3294
  const allowedIPs = process.env.PROXY_ALLOWED_IPS?.split(",").map((ip) => ip.trim()) || [];
3275
3295
  if (allowedIPs.length === 0) {
3276
- console.log(`[MoltsPay] /proxy denied: no PROXY_ALLOWED_IPS configured`);
3277
- return false;
3296
+ return true;
3297
+ }
3298
+ if (allowedIPs.includes("*")) {
3299
+ return true;
3278
3300
  }
3279
3301
  const normalizedIP = clientIP === "::1" ? "127.0.0.1" : clientIP.replace("::ffff:", "");
3280
3302
  const allowed = allowedIPs.includes(normalizedIP) || allowedIPs.includes(clientIP);
@@ -4870,14 +4892,23 @@ program.command("stop").description("Stop the running MoltsPay server").action(a
4870
4892
  process.exit(1);
4871
4893
  }
4872
4894
  });
4873
- program.command("pay <server> <service> [params]").description("Pay for a service and get the result").option("--prompt <text>", "Prompt for the service").option("--image <path>", "Image URL or local file path").option("--token <token>", "Token to pay with (USDC or USDT)", "USDC").option("--chain <chain>", "Chain to pay on (base, polygon, base_sepolia, tempo_moderato, solana, or solana_devnet).").option("--config-dir <dir>", "Config directory with wallet.json", DEFAULT_CONFIG_DIR2).option("--json", "Output raw JSON only").action(async (server, service, paramsJson, options) => {
4895
+ program.command("pay <server> <service> [params]").description("Pay for a service and get the result").option("--prompt <text>", "Prompt for the service").option("--image <path>", "Image URL or local file path").option("--data <json>", "Raw JSON data to send (for custom input formats)").option("--token <token>", "Token to pay with (USDC or USDT)", "USDC").option("--chain <chain>", "Chain to pay on (base, polygon, base_sepolia, tempo_moderato, solana, or solana_devnet).").option("--config-dir <dir>", "Config directory with wallet.json", DEFAULT_CONFIG_DIR2).option("--json", "Output raw JSON only").action(async (server, service, paramsJson, options) => {
4874
4896
  const client = new MoltsPayClient({ configDir: options.configDir });
4875
4897
  if (!client.isInitialized) {
4876
4898
  console.error("\u274C Wallet not initialized. Run: npx moltspay init");
4877
4899
  process.exit(1);
4878
4900
  }
4879
4901
  let params = {};
4880
- if (paramsJson) {
4902
+ let useRawData = false;
4903
+ if (options.data) {
4904
+ try {
4905
+ params = JSON.parse(options.data);
4906
+ useRawData = true;
4907
+ } catch {
4908
+ console.error("\u274C Invalid JSON in --data flag");
4909
+ process.exit(1);
4910
+ }
4911
+ } else if (paramsJson) {
4881
4912
  try {
4882
4913
  params = JSON.parse(paramsJson);
4883
4914
  } catch {
@@ -4885,7 +4916,7 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
4885
4916
  process.exit(1);
4886
4917
  }
4887
4918
  }
4888
- if (options.prompt) params.prompt = options.prompt;
4919
+ if (!useRawData && options.prompt) params.prompt = options.prompt;
4889
4920
  if (options.image) {
4890
4921
  const imagePath = options.image;
4891
4922
  if (imagePath.startsWith("http://") || imagePath.startsWith("https://")) {
@@ -4926,7 +4957,11 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
4926
4957
  `);
4927
4958
  console.log(` Server: ${server}`);
4928
4959
  console.log(` Service: ${service}`);
4929
- console.log(` Prompt: ${params.prompt}`);
4960
+ if (useRawData) {
4961
+ console.log(` Data: ${JSON.stringify(params).slice(0, 50)}${JSON.stringify(params).length > 50 ? "..." : ""}`);
4962
+ } else {
4963
+ console.log(` Prompt: ${params.prompt}`);
4964
+ }
4930
4965
  if (imageDisplay) console.log(` Image: ${imageDisplay}`);
4931
4966
  console.log(` Chain: ${chain || "(auto)"}`);
4932
4967
  console.log(` Token: ${token}`);
@@ -4936,7 +4971,8 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
4936
4971
  try {
4937
4972
  const result = await client.pay(server, service, params, {
4938
4973
  token,
4939
- chain
4974
+ chain,
4975
+ rawData: useRawData
4940
4976
  });
4941
4977
  if (options.json) {
4942
4978
  console.log(JSON.stringify(result));