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.
@@ -676,11 +676,26 @@ var MoltsPayClient = class {
676
676
  throw new Error("Client not initialized. Run: npx moltspay init");
677
677
  }
678
678
  console.log(`[MoltsPay] Requesting service: ${service}`);
679
- const requestBody = { service, params };
679
+ let executeUrl = `${serverUrl}/execute`;
680
+ try {
681
+ const services = await this.getServices(serverUrl);
682
+ const svc = services.services?.find((s) => s.id === service);
683
+ if (svc?.endpoint) {
684
+ executeUrl = `${serverUrl}${svc.endpoint}`;
685
+ console.log(`[MoltsPay] Using service endpoint: ${svc.endpoint}`);
686
+ }
687
+ } catch {
688
+ }
689
+ let requestBody;
690
+ if (options.rawData) {
691
+ requestBody = { service, ...params };
692
+ } else {
693
+ requestBody = { service, params };
694
+ }
680
695
  if (options.chain) {
681
696
  requestBody.chain = options.chain;
682
697
  }
683
- const initialRes = await fetch(`${serverUrl}/execute`, {
698
+ const initialRes = await fetch(executeUrl, {
684
699
  method: "POST",
685
700
  headers: { "Content-Type": "application/json" },
686
701
  body: JSON.stringify(requestBody)
@@ -696,7 +711,7 @@ var MoltsPayClient = class {
696
711
  const paymentRequiredHeader = initialRes.headers.get(PAYMENT_REQUIRED_HEADER);
697
712
  if (wwwAuthHeader && wwwAuthHeader.toLowerCase().includes("payment")) {
698
713
  console.log("[MoltsPay] Detected MPP protocol, using Tempo flow...");
699
- return await this.handleMPPPayment(serverUrl, service, params, wwwAuthHeader);
714
+ return await this.handleMPPPayment(executeUrl, service, params, wwwAuthHeader, options);
700
715
  }
701
716
  if (!paymentRequiredHeader) {
702
717
  throw new Error("Missing payment header (x-payment-required or www-authenticate)");
@@ -757,7 +772,7 @@ Please specify: --chain <chain_name>`
757
772
  if (!req2) {
758
773
  throw new Error(`Failed to find payment requirement for ${selectedChain}`);
759
774
  }
760
- return await this.handleSolanaPayment(serverUrl, service, params, req2, solanaChain);
775
+ return await this.handleSolanaPayment(executeUrl, service, params, req2, solanaChain, options);
761
776
  }
762
777
  const chainName = selectedChain;
763
778
  const chain = getChain(chainName);
@@ -804,14 +819,14 @@ Please specify: --chain <chain_name>`
804
819
  if (!bnbSpender) {
805
820
  throw new Error("Server did not provide bnbSpender address. Server may not support BNB payments.");
806
821
  }
807
- return await this.handleBNBPayment(serverUrl, service, params, {
822
+ return await this.handleBNBPayment(executeUrl, service, params, {
808
823
  to: payTo2,
809
824
  amount,
810
825
  token,
811
826
  chainName,
812
827
  chain,
813
828
  spender: bnbSpender
814
- });
829
+ }, options);
815
830
  }
816
831
  const payTo = req.payTo || req.resource;
817
832
  if (!payTo) {
@@ -842,11 +857,11 @@ Please specify: --chain <chain_name>`
842
857
  };
843
858
  const paymentHeader = Buffer.from(JSON.stringify(payload)).toString("base64");
844
859
  console.log(`[MoltsPay] Sending request with payment...`);
845
- const paidRequestBody = { service, params };
860
+ const paidRequestBody = options.rawData ? { service, ...params } : { service, params };
846
861
  if (options.chain) {
847
862
  paidRequestBody.chain = options.chain;
848
863
  }
849
- const paidRes = await fetch(`${serverUrl}/execute`, {
864
+ const paidRes = await fetch(executeUrl, {
850
865
  method: "POST",
851
866
  headers: {
852
867
  "Content-Type": "application/json",
@@ -860,13 +875,13 @@ Please specify: --chain <chain_name>`
860
875
  }
861
876
  this.recordSpending(amount);
862
877
  console.log(`[MoltsPay] Success! Payment: ${result.payment?.status || "claimed"}`);
863
- return result.result;
878
+ return result.result || result;
864
879
  }
865
880
  /**
866
881
  * Handle MPP (Machine Payments Protocol) payment flow
867
882
  * Called when pay() detects WWW-Authenticate header in 402 response
868
883
  */
869
- async handleMPPPayment(serverUrl, service, params, wwwAuthHeader) {
884
+ async handleMPPPayment(executeUrl, service, params, wwwAuthHeader, options = {}) {
870
885
  const { privateKeyToAccount: privateKeyToAccount2 } = await import("viem/accounts");
871
886
  const { createWalletClient, createPublicClient, http } = await import("viem");
872
887
  const { tempoModerato } = await import("viem/chains");
@@ -927,13 +942,14 @@ Please specify: --chain <chain_name>`
927
942
  source: `did:pkh:eip155:${chainId}:${account.address}`
928
943
  };
929
944
  const credentialB64 = Buffer.from(JSON.stringify(credential)).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
930
- const paidRes = await fetch(`${serverUrl}/execute`, {
945
+ const retryBody = options.rawData ? { service, ...params, chain: "tempo_moderato" } : { service, params, chain: "tempo_moderato" };
946
+ const paidRes = await fetch(executeUrl, {
931
947
  method: "POST",
932
948
  headers: {
933
949
  "Content-Type": "application/json",
934
950
  "Authorization": `Payment ${credentialB64}`
935
951
  },
936
- body: JSON.stringify({ service, params, chain: "tempo_moderato" })
952
+ body: JSON.stringify(retryBody)
937
953
  });
938
954
  const result = await paidRes.json();
939
955
  if (!paidRes.ok) {
@@ -953,7 +969,7 @@ Please specify: --chain <chain_name>`
953
969
  * 4. Server executes service
954
970
  * 5. Server calls transferFrom if successful (pay-for-success)
955
971
  */
956
- async handleBNBPayment(serverUrl, service, params, paymentDetails) {
972
+ async handleBNBPayment(executeUrl, service, params, paymentDetails, options = {}) {
957
973
  const { to, amount, token, chainName, chain, spender } = paymentDetails;
958
974
  const tokenConfig = chain.tokens[token];
959
975
  const provider = new ethers.JsonRpcProvider(chain.rpc);
@@ -1049,13 +1065,14 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1049
1065
  };
1050
1066
  const paymentHeader = Buffer.from(JSON.stringify(payload)).toString("base64");
1051
1067
  console.log(`[MoltsPay] Sending BNB payment request...`);
1052
- const paidRes = await fetch(`${serverUrl}/execute`, {
1068
+ const bnbRequestBody = options.rawData ? { service, ...params, chain: chainName } : { service, params, chain: chainName };
1069
+ const paidRes = await fetch(executeUrl, {
1053
1070
  method: "POST",
1054
1071
  headers: {
1055
1072
  "Content-Type": "application/json",
1056
1073
  "X-Payment": paymentHeader
1057
1074
  },
1058
- body: JSON.stringify({ service, params, chain: chainName })
1075
+ body: JSON.stringify(bnbRequestBody)
1059
1076
  });
1060
1077
  const result = await paidRes.json();
1061
1078
  if (!paidRes.ok) {
@@ -1072,7 +1089,7 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1072
1089
  * 1. Client creates and signs a transfer transaction
1073
1090
  * 2. Server submits the transaction after service completes
1074
1091
  */
1075
- async handleSolanaPayment(serverUrl, service, params, requirements, chain) {
1092
+ async handleSolanaPayment(executeUrl, service, params, requirements, chain, options = {}) {
1076
1093
  const solanaWallet = loadSolanaWallet(this.configDir);
1077
1094
  if (!solanaWallet) {
1078
1095
  throw new Error("No Solana wallet found. Run: npx moltspay init --chain solana_devnet");
@@ -1125,13 +1142,14 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1125
1142
  }
1126
1143
  };
1127
1144
  const paymentHeader = Buffer.from(JSON.stringify(payload)).toString("base64");
1128
- const paidRes = await fetch(`${serverUrl}/execute`, {
1145
+ const solanaRequestBody = options.rawData ? { service, ...params, chain } : { service, params, chain };
1146
+ const paidRes = await fetch(executeUrl, {
1129
1147
  method: "POST",
1130
1148
  headers: {
1131
1149
  "Content-Type": "application/json",
1132
1150
  "X-Payment": paymentHeader
1133
1151
  },
1134
- body: JSON.stringify({ service, params, chain })
1152
+ body: JSON.stringify(solanaRequestBody)
1135
1153
  });
1136
1154
  const result = await paidRes.json();
1137
1155
  if (!paidRes.ok) {
@@ -1276,15 +1294,17 @@ Run: npx moltspay approve --chain ${chainName} --spender ${spender}`
1276
1294
  loadWallet() {
1277
1295
  const walletPath = join2(this.configDir, "wallet.json");
1278
1296
  if (existsSync2(walletPath)) {
1279
- try {
1280
- const stats = statSync(walletPath);
1281
- const mode = stats.mode & 511;
1282
- if (mode !== 384) {
1283
- console.warn(`[MoltsPay] WARNING: wallet.json has insecure permissions (${mode.toString(8)})`);
1284
- console.warn(`[MoltsPay] Fixing permissions to 0600...`);
1285
- chmodSync(walletPath, 384);
1297
+ if (process.platform !== "win32") {
1298
+ try {
1299
+ const stats = statSync(walletPath);
1300
+ const mode = stats.mode & 511;
1301
+ if (mode !== 384) {
1302
+ console.warn(`[MoltsPay] WARNING: wallet.json has insecure permissions (${mode.toString(8)})`);
1303
+ console.warn(`[MoltsPay] Fixing permissions to 0600...`);
1304
+ chmodSync(walletPath, 384);
1305
+ }
1306
+ } catch {
1286
1307
  }
1287
- } catch (err) {
1288
1308
  }
1289
1309
  const content = readFileSync2(walletPath, "utf-8");
1290
1310
  return JSON.parse(content);
@@ -3267,8 +3287,10 @@ var MoltsPayServer = class {
3267
3287
  isProxyAllowed(clientIP) {
3268
3288
  const allowedIPs = process.env.PROXY_ALLOWED_IPS?.split(",").map((ip) => ip.trim()) || [];
3269
3289
  if (allowedIPs.length === 0) {
3270
- console.log(`[MoltsPay] /proxy denied: no PROXY_ALLOWED_IPS configured`);
3271
- return false;
3290
+ return true;
3291
+ }
3292
+ if (allowedIPs.includes("*")) {
3293
+ return true;
3272
3294
  }
3273
3295
  const normalizedIP = clientIP === "::1" ? "127.0.0.1" : clientIP.replace("::ffff:", "");
3274
3296
  const allowed = allowedIPs.includes(normalizedIP) || allowedIPs.includes(clientIP);
@@ -4864,14 +4886,23 @@ program.command("stop").description("Stop the running MoltsPay server").action(a
4864
4886
  process.exit(1);
4865
4887
  }
4866
4888
  });
4867
- 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) => {
4889
+ 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) => {
4868
4890
  const client = new MoltsPayClient({ configDir: options.configDir });
4869
4891
  if (!client.isInitialized) {
4870
4892
  console.error("\u274C Wallet not initialized. Run: npx moltspay init");
4871
4893
  process.exit(1);
4872
4894
  }
4873
4895
  let params = {};
4874
- if (paramsJson) {
4896
+ let useRawData = false;
4897
+ if (options.data) {
4898
+ try {
4899
+ params = JSON.parse(options.data);
4900
+ useRawData = true;
4901
+ } catch {
4902
+ console.error("\u274C Invalid JSON in --data flag");
4903
+ process.exit(1);
4904
+ }
4905
+ } else if (paramsJson) {
4875
4906
  try {
4876
4907
  params = JSON.parse(paramsJson);
4877
4908
  } catch {
@@ -4879,7 +4910,7 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
4879
4910
  process.exit(1);
4880
4911
  }
4881
4912
  }
4882
- if (options.prompt) params.prompt = options.prompt;
4913
+ if (!useRawData && options.prompt) params.prompt = options.prompt;
4883
4914
  if (options.image) {
4884
4915
  const imagePath = options.image;
4885
4916
  if (imagePath.startsWith("http://") || imagePath.startsWith("https://")) {
@@ -4920,7 +4951,11 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
4920
4951
  `);
4921
4952
  console.log(` Server: ${server}`);
4922
4953
  console.log(` Service: ${service}`);
4923
- console.log(` Prompt: ${params.prompt}`);
4954
+ if (useRawData) {
4955
+ console.log(` Data: ${JSON.stringify(params).slice(0, 50)}${JSON.stringify(params).length > 50 ? "..." : ""}`);
4956
+ } else {
4957
+ console.log(` Prompt: ${params.prompt}`);
4958
+ }
4924
4959
  if (imageDisplay) console.log(` Image: ${imageDisplay}`);
4925
4960
  console.log(` Chain: ${chain || "(auto)"}`);
4926
4961
  console.log(` Token: ${token}`);
@@ -4930,7 +4965,8 @@ program.command("pay <server> <service> [params]").description("Pay for a servic
4930
4965
  try {
4931
4966
  const result = await client.pay(server, service, params, {
4932
4967
  token,
4933
- chain
4968
+ chain,
4969
+ rawData: useRawData
4934
4970
  });
4935
4971
  if (options.json) {
4936
4972
  console.log(JSON.stringify(result));