moltspay 1.4.0 → 1.4.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/.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,14 @@ 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
30
 
31
31
  ## Installation
32
32
 
@@ -148,24 +148,24 @@ MoltsPay supports multiple payment protocols, each optimized for different chain
148
148
 
149
149
  ```
150
150
  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
- <─────────────────────────
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
+ | <-------------------------------------------------- | |
169
169
  ```
170
170
 
171
171
  **Key insight:** Client signs a payment authorization, server submits it. Neither party pays gas - the CDP facilitator handles settlement.
@@ -176,23 +176,23 @@ MPP (Machine Payments Protocol) is simpler - client executes the transfer direct
176
176
 
177
177
  ```
178
178
  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
- <─────────────────────────
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
+ | <-------------------------------------------------- |
196
196
  ```
197
197
 
198
198
  **Key insight:** On Tempo, the client executes the transfer directly (gas-free), then retries with the transaction hash. No CDP facilitator needed.
@@ -201,24 +201,24 @@ Client Server
201
201
 
202
202
  ```
203
203
  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
- <─────────────────────────
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
+ | <-------------------------------------------------- | |
222
222
  ```
223
223
 
224
224
  **Key insight:** Client only signs the SPL transfer (gasless). Server acts as fee payer and executes the transaction on-chain.
@@ -227,24 +227,24 @@ Client Server (Fee Payer) Solana Network
227
227
 
228
228
  ```
229
229
  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
- <─────────────────────────
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
+ | <-------------------------------------------------- | |
248
248
  ```
249
249
 
250
250
  **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 +255,9 @@ MoltsPay reads your skill's existing structure:
255
255
 
256
256
  ```
257
257
  my-skill/
258
- ├── package.json # MoltsPay reads "main" field
259
- ├── index.js # Your existing exports
260
- └── moltspay.services.json # Only file you add!
258
+ +------ package.json # MoltsPay reads "main" field
259
+ +------ index.js # Your existing exports
260
+ +------ moltspay.services.json # Only file you add!
261
261
  ```
262
262
 
263
263
  **Your functions stay untouched.** Just add the JSON config.
@@ -350,8 +350,8 @@ Server does NOT need a private key - the x402 facilitator handles settlement.
350
350
 
351
351
  The server automatically detects which chain to verify payments on based on the client's payment header:
352
352
 
353
- - Client pays with `--chain base` Server verifies on Base mainnet
354
- - Client pays with `--chain base_sepolia` Server verifies on Base Sepolia
353
+ - Client pays with `--chain base` -> Server verifies on Base mainnet
354
+ - Client pays with `--chain base_sepolia` -> Server verifies on Base Sepolia
355
355
 
356
356
  **No `USE_MAINNET` env var needed!** Just configure your accepted chains in the manifest.
357
357
 
@@ -425,16 +425,63 @@ npx moltspay validate <path> # Validate manifest
425
425
  ```typescript
426
426
  import { MoltsPayClient } from 'moltspay/client';
427
427
 
428
- const client = new MoltsPayClient({ chain: 'base' });
428
+ // Initialize client (uses wallet from ~/.moltspay/wallet.json)
429
+ const client = new MoltsPayClient();
429
430
 
430
- // Pay for a service
431
- const result = await client.execute('https://server.com', 'text-to-video', {
432
- prompt: 'a cat dancing'
433
- });
431
+ // Standard service call (params wrapped in { params: {...} })
432
+ const result = await client.pay(
433
+ 'https://server.com',
434
+ 'text-to-video',
435
+ { prompt: 'a cat dancing' },
436
+ { chain: 'base' }
437
+ );
434
438
 
435
439
  console.log(result.video_url);
436
440
  ```
437
441
 
442
+ #### Custom Input Formats (rawData)
443
+
444
+ Some services have custom input formats instead of the standard `{ params: { prompt } }`.
445
+ Use `rawData: true` to send your data at the top level:
446
+
447
+ ```typescript
448
+ // Service expects: { text: "...", target_lang: "..." }
449
+ // NOT: { params: { text: "...", target_lang: "..." } }
450
+
451
+ const result = await client.pay(
452
+ 'https://server.com',
453
+ 'translate',
454
+ { text: 'Hello world', target_lang: 'es' },
455
+ {
456
+ chain: 'base_sepolia',
457
+ rawData: true // Send data at top level
458
+ }
459
+ );
460
+
461
+ // Server receives: { service: "translate", text: "Hello world", target_lang: "es", chain: "base_sepolia" }
462
+ ```
463
+
464
+ #### PayOptions Reference
465
+
466
+ ```typescript
467
+ interface PayOptions {
468
+ token?: 'USDC' | 'USDT'; // Token to pay with (default: USDC)
469
+ autoSelect?: boolean; // Auto-select token based on balance
470
+ chain?: string; // Chain: base, polygon, solana, bnb, tempo_moderato, + testnets
471
+ rawData?: boolean; // Send data at top level (for custom input formats)
472
+ }
473
+ ```
474
+
475
+ #### CLI Equivalent
476
+
477
+ ```bash
478
+ # Standard format (uses { params: { prompt } })
479
+ npx moltspay pay https://server.com text-to-video --prompt "a cat dancing"
480
+
481
+ # Custom format (uses rawData, sends at top level)
482
+ npx moltspay pay https://server.com translate --data '{"text": "Hello", "target_lang": "es"}'
483
+ ```
484
+
438
485
  ### Server
439
486
 
440
487
  ```typescript
@@ -493,9 +540,9 @@ A **facilitator** is the entity that executes the on-chain payment and pays the
493
540
 
494
541
  | Aspect | CDP (External) | Self-hosted |
495
542
  |--------|----------------|-------------|
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 |
543
+ | Single point of failure | Coinbase down = everyone stuck | Each provider independent |
544
+ | Censorship risk | Coinbase can block accounts | Cannot be censored |
545
+ | Dependency | Relies on third-party | Fully autonomous |
499
546
 
500
547
  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
548
 
@@ -628,7 +675,7 @@ npx moltspay pay https://moltspay.com/a/zen7 text-to-video --chain solana_devnet
628
675
 
629
676
  Join our Discord for help, feedback, and updates:
630
677
 
631
- 👉 **[MoltsPay Discord](https://discord.gg/QwCJgVBxVK)**
678
+ --> **[MoltsPay Discord](https://discord.gg/QwCJgVBxVK)**
632
679
 
633
680
  Or visit the [#moltspay-support](https://discord.com/channels/1472602423267819734/1480968496346304522) channel directly.
634
681
 
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) {
@@ -3273,8 +3291,10 @@ var MoltsPayServer = class {
3273
3291
  isProxyAllowed(clientIP) {
3274
3292
  const allowedIPs = process.env.PROXY_ALLOWED_IPS?.split(",").map((ip) => ip.trim()) || [];
3275
3293
  if (allowedIPs.length === 0) {
3276
- console.log(`[MoltsPay] /proxy denied: no PROXY_ALLOWED_IPS configured`);
3277
- return false;
3294
+ return true;
3295
+ }
3296
+ if (allowedIPs.includes("*")) {
3297
+ return true;
3278
3298
  }
3279
3299
  const normalizedIP = clientIP === "::1" ? "127.0.0.1" : clientIP.replace("::ffff:", "");
3280
3300
  const allowed = allowedIPs.includes(normalizedIP) || allowedIPs.includes(clientIP);
@@ -4870,14 +4890,23 @@ program.command("stop").description("Stop the running MoltsPay server").action(a
4870
4890
  process.exit(1);
4871
4891
  }
4872
4892
  });
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) => {
4893
+ 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
4894
  const client = new MoltsPayClient({ configDir: options.configDir });
4875
4895
  if (!client.isInitialized) {
4876
4896
  console.error("\u274C Wallet not initialized. Run: npx moltspay init");
4877
4897
  process.exit(1);
4878
4898
  }
4879
4899
  let params = {};
4880
- if (paramsJson) {
4900
+ let useRawData = false;
4901
+ if (options.data) {
4902
+ try {
4903
+ params = JSON.parse(options.data);
4904
+ useRawData = true;
4905
+ } catch {
4906
+ console.error("\u274C Invalid JSON in --data flag");
4907
+ process.exit(1);
4908
+ }
4909
+ } else if (paramsJson) {
4881
4910
  try {
4882
4911
  params = JSON.parse(paramsJson);
4883
4912
  } catch {
@@ -4885,7 +4914,7 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
4885
4914
  process.exit(1);
4886
4915
  }
4887
4916
  }
4888
- if (options.prompt) params.prompt = options.prompt;
4917
+ if (!useRawData && options.prompt) params.prompt = options.prompt;
4889
4918
  if (options.image) {
4890
4919
  const imagePath = options.image;
4891
4920
  if (imagePath.startsWith("http://") || imagePath.startsWith("https://")) {
@@ -4926,7 +4955,11 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
4926
4955
  `);
4927
4956
  console.log(` Server: ${server}`);
4928
4957
  console.log(` Service: ${service}`);
4929
- console.log(` Prompt: ${params.prompt}`);
4958
+ if (useRawData) {
4959
+ console.log(` Data: ${JSON.stringify(params).slice(0, 50)}${JSON.stringify(params).length > 50 ? "..." : ""}`);
4960
+ } else {
4961
+ console.log(` Prompt: ${params.prompt}`);
4962
+ }
4930
4963
  if (imageDisplay) console.log(` Image: ${imageDisplay}`);
4931
4964
  console.log(` Chain: ${chain || "(auto)"}`);
4932
4965
  console.log(` Token: ${token}`);
@@ -4936,7 +4969,8 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
4936
4969
  try {
4937
4970
  const result = await client.pay(server, service, params, {
4938
4971
  token,
4939
- chain
4972
+ chain,
4973
+ rawData: useRawData
4940
4974
  });
4941
4975
  if (options.json) {
4942
4976
  console.log(JSON.stringify(result));