x402-proxy 0.8.2 → 0.8.4

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/CHANGELOG.md CHANGED
@@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.8.4] - 2026-03-24
11
+
12
+ ### Fixed
13
+ - Servers returning Solana payment options with EVM-format `payTo` addresses (e.g. `0x...`) no longer crash with a base58 decode error - malformed options are filtered out and valid options are used instead
14
+ - When all payment options from a server have mismatched address formats, a clear error is shown instead of a cryptic codec failure
15
+
16
+ ## [0.8.3] - 2026-03-24
17
+
18
+ ### Added
19
+ - JSON response pretty-printing: non-streaming `application/json` responses auto-formatted with 2-space indent on TTY
20
+ - `--json` flag now works: forces JSON pretty-printing even when piped (non-TTY)
21
+ - Color-coded HTTP status lines: green for 2xx, yellow for 3xx, red for 4xx/5xx
22
+
23
+ ### Fixed
24
+ - MPP streaming payment label unified from `MPP session:` to `Payment: ... MPP` to match non-streaming format
25
+ - MPP streaming status line now starts on a new line instead of appending to last JSON chunk
26
+
10
27
  ## [0.8.2] - 2026-03-24
11
28
 
12
29
  ### Fixed
@@ -219,7 +236,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
219
236
  - `appendHistory` / `readHistory` / `calcSpend` - JSONL transaction history
220
237
  - Re-exports from `@x402/fetch`, `@x402/svm`, `@x402/evm`
221
238
 
222
- [Unreleased]: https://github.com/cascade-protocol/x402-proxy/compare/v0.8.2...HEAD
239
+ [Unreleased]: https://github.com/cascade-protocol/x402-proxy/compare/v0.8.4...HEAD
240
+ [0.8.4]: https://github.com/cascade-protocol/x402-proxy/compare/v0.8.3...v0.8.4
241
+ [0.8.3]: https://github.com/cascade-protocol/x402-proxy/compare/v0.8.2...v0.8.3
223
242
  [0.8.2]: https://github.com/cascade-protocol/x402-proxy/compare/v0.8.1...v0.8.2
224
243
  [0.8.1]: https://github.com/cascade-protocol/x402-proxy/compare/v0.8.0...v0.8.1
225
244
  [0.8.0]: https://github.com/cascade-protocol/x402-proxy/compare/v0.7.1...v0.8.0
package/dist/bin/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { a as getHistoryPath, c as loadConfig, i as getConfigDirShort, l as loadWalletFile, s as isConfigured, u as saveConfig } from "../derive-BR6N1ZjI.js";
3
- import { _ as error, b as warn, c as resolveWallet, d as displayNetwork, f as formatAmount, g as dim, h as readHistory, l as appendHistory, m as formatUsdcValue, o as walletInfoCommand, p as formatTxLine, s as buildX402Client, u as calcSpend, v as info, y as isTTY } from "../wallet-CeY5DPj-.js";
3
+ import { _ as error, b as warn, c as resolveWallet, d as displayNetwork, f as formatAmount, g as dim, h as readHistory, l as appendHistory, m as formatUsdcValue, o as walletInfoCommand, p as formatTxLine, s as buildX402Client, u as calcSpend, v as info, y as isTTY } from "../wallet-BiX5_XFY.js";
4
4
  import { n as setupCommand } from "../setup-Di_b5Vp9.js";
5
- import { n as statusCommand } from "../status-S3t-XV_M.js";
5
+ import { n as statusCommand } from "../status-DvSu8reM.js";
6
6
  import { buildApplication, buildCommand, buildRouteMap, run } from "@stricli/core";
7
7
  import pc from "picocolors";
8
8
  import { decodePaymentResponseHeader, wrapFetchWithPayment } from "@x402/fetch";
@@ -301,6 +301,9 @@ async function createMppProxyHandler(opts) {
301
301
 
302
302
  //#endregion
303
303
  //#region src/commands/fetch.ts
304
+ function isStreamingResponse(res) {
305
+ return (res.headers.get("content-type") ?? "").includes("text/event-stream");
306
+ }
304
307
  const fetchCommand = buildCommand({
305
308
  docs: {
306
309
  brief: "Make a paid HTTP request (default command)",
@@ -395,7 +398,7 @@ Examples:
395
398
  };
396
399
  if (!url) {
397
400
  if (isConfigured()) {
398
- const { displayStatus } = await import("../status-CJPUbh6Z.js");
401
+ const { displayStatus } = await import("../status-DjudJ6fD.js");
399
402
  await displayStatus();
400
403
  console.log();
401
404
  console.log(pc.dim(" Commands:"));
@@ -460,7 +463,7 @@ Examples:
460
463
  verbose(`protocol: ${resolvedProtocol ?? "auto-detect"}, maxDeposit: ${maxDeposit}`);
461
464
  let preferredNetwork = config?.defaultNetwork;
462
465
  if (!preferredNetwork && wallet.evmAddress && wallet.solanaAddress) {
463
- const { fetchAllBalances } = await import("../wallet-CJBRFJw8.js");
466
+ const { fetchAllBalances } = await import("../wallet-BkZ0DOJL.js");
464
467
  const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
465
468
  const evmUsdc = balances.evm ? Number(balances.evm.usdc) : 0;
466
469
  const solUsdc = balances.sol ? Number(balances.sol.usdc) : 0;
@@ -527,7 +530,8 @@ Examples:
527
530
  usedProtocol = "mpp";
528
531
  const elapsedMs = Date.now() - startMs;
529
532
  const spentAmount = mppPayment.amount ? Number(mppPayment.amount) : void 0;
530
- if (mppPayment && isTTY()) info(` MPP session: ${spentAmount != null ? `${formatAmount(spentAmount, "USDC")} ` : ""}(${displayNetwork(mppPayment.network)})`);
533
+ if (isTTY()) process.stderr.write("\n");
534
+ if (mppPayment && isTTY()) info(` Payment:${spentAmount != null ? ` ${formatAmount(spentAmount, "USDC")}` : ""} MPP (${displayNetwork(mppPayment.network)})`);
531
535
  if (isTTY()) dim(` Streamed (${elapsedMs}ms)`);
532
536
  if (mppPayment) {
533
537
  const record = {
@@ -626,7 +630,7 @@ Examples:
626
630
  const hasSolana = accepts.some((a) => a.network.startsWith("solana:"));
627
631
  const hasMpp = detected.mpp;
628
632
  const hasOther = accepts.some((a) => !a.network.startsWith("eip155:") && !a.network.startsWith("solana:"));
629
- const { fetchAllBalances } = await import("../wallet-CJBRFJw8.js");
633
+ const { fetchAllBalances } = await import("../wallet-BkZ0DOJL.js");
630
634
  const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
631
635
  const evmUsdc = hasEvm && balances.evm ? Number(balances.evm.usdc) : 0;
632
636
  const solUsdc = hasSolana && balances.sol ? Number(balances.sol.usdc) : 0;
@@ -688,7 +692,11 @@ Examples:
688
692
  else if (x402Payment) info(` Payment: ${x402Payment.amount ? formatAmount(Number(x402Payment.amount) / 1e6, "USDC") : "? USDC"} (${displayNetwork(x402Payment.network ?? "unknown")})`);
689
693
  if (txSig) dim(` Tx: ${txSig}`);
690
694
  }
691
- if (isTTY()) dim(` ${response.status} ${response.statusText} (${elapsedMs}ms)`);
695
+ if (isTTY()) {
696
+ const statusText = ` ${response.status} ${response.statusText} (${elapsedMs}ms)`;
697
+ const colorFn = response.status < 300 ? pc.green : response.status < 400 ? pc.yellow : pc.red;
698
+ process.stderr.write(`${colorFn(statusText)}\n`);
699
+ }
692
700
  if (x402Payment) {
693
701
  const record = {
694
702
  t: Date.now(),
@@ -719,7 +727,16 @@ Examples:
719
727
  };
720
728
  appendHistory(getHistoryPath(), record);
721
729
  }
722
- if (response.body) {
730
+ const shouldPrettyPrint = (response.headers.get("content-type") ?? "").includes("application/json") && (flags.json || isTTY()) && !isStreamingResponse(response);
731
+ if (response.body) if (shouldPrettyPrint) {
732
+ const text = await response.text();
733
+ try {
734
+ process.stdout.write(`${JSON.stringify(JSON.parse(text), null, 2)}\n`);
735
+ } catch {
736
+ process.stdout.write(text);
737
+ if (isTTY()) process.stdout.write("\n");
738
+ }
739
+ } else {
723
740
  const reader = response.body.getReader();
724
741
  try {
725
742
  while (true) {
@@ -730,8 +747,8 @@ Examples:
730
747
  } finally {
731
748
  reader.releaseLock();
732
749
  }
750
+ if (isTTY()) process.stdout.write("\n");
733
751
  }
734
- if (isTTY() && response.body) process.stdout.write("\n");
735
752
  }
736
753
  });
737
754
 
@@ -823,7 +840,7 @@ Add to your MCP client config (Claude, Cursor, etc.):
823
840
  async function startX402Proxy() {
824
841
  let preferredNetwork = config?.defaultNetwork;
825
842
  if (!preferredNetwork && wallet.evmAddress && wallet.solanaAddress) {
826
- const { fetchAllBalances } = await import("../wallet-CJBRFJw8.js");
843
+ const { fetchAllBalances } = await import("../wallet-BkZ0DOJL.js");
827
844
  const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
828
845
  const evmUsdc = balances.evm ? Number(balances.evm.usdc) : 0;
829
846
  const solUsdc = balances.sol ? Number(balances.sol.usdc) : 0;
@@ -843,7 +860,7 @@ Add to your MCP client config (Claude, Cursor, etc.):
843
860
  }
844
861
  const remoteClient = new Client({
845
862
  name: "x402-proxy",
846
- version: "0.8.2"
863
+ version: "0.8.4"
847
864
  });
848
865
  const x402Mcp = new x402MCPClient(remoteClient, x402PaymentClient, {
849
866
  autoPayment: true,
@@ -881,7 +898,7 @@ Add to your MCP client config (Claude, Cursor, etc.):
881
898
  }
882
899
  const localServer = new Server({
883
900
  name: "x402-proxy",
884
- version: "0.8.2"
901
+ version: "0.8.4"
885
902
  }, { capabilities: {
886
903
  tools: tools.length > 0 ? {} : void 0,
887
904
  resources: remoteResources.length > 0 ? {} : void 0
@@ -976,7 +993,7 @@ Add to your MCP client config (Claude, Cursor, etc.):
976
993
  }));
977
994
  const remoteClient = new Client({
978
995
  name: "x402-proxy",
979
- version: "0.8.2"
996
+ version: "0.8.4"
980
997
  });
981
998
  await connectTransport(remoteClient);
982
999
  const mppClient = McpClient.wrap(remoteClient, { methods: wrappedMethods });
@@ -991,7 +1008,7 @@ Add to your MCP client config (Claude, Cursor, etc.):
991
1008
  }
992
1009
  const localServer = new Server({
993
1010
  name: "x402-proxy",
994
- version: "0.8.2"
1011
+ version: "0.8.4"
995
1012
  }, { capabilities: {
996
1013
  tools: tools.length > 0 ? {} : void 0,
997
1014
  resources: remoteResources.length > 0 ? {} : void 0
@@ -1203,7 +1220,7 @@ const routes = buildRouteMap({
1203
1220
  });
1204
1221
  const app = buildApplication(routes, {
1205
1222
  name: "x402-proxy",
1206
- versionInfo: { currentVersion: "0.8.2" },
1223
+ versionInfo: { currentVersion: "0.8.4" },
1207
1224
  scanner: { caseStyle: "allow-kebab-for-camel" }
1208
1225
  });
1209
1226
 
@@ -726,7 +726,7 @@ function createWalletCommand(ctx) {
726
726
  try {
727
727
  const snap = await getWalletSnapshot(ctx.rpcUrl, walletAddress, ctx.historyPath);
728
728
  const solscanUrl = `https://solscan.io/account/${walletAddress}`;
729
- const lines = [`x402-proxy v0.8.2`];
729
+ const lines = [`x402-proxy v0.8.4`];
730
730
  const defaultModel = ctx.allModels[0];
731
731
  if (defaultModel) lines.push("", `**Model** - ${defaultModel.name} (${defaultModel.provider})`);
732
732
  lines.push("", `**[Wallet](${solscanUrl})**`, `\`${walletAddress}\``);
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { n as statusCommand, t as displayStatus } from "./status-S3t-XV_M.js";
2
+ import { n as statusCommand, t as displayStatus } from "./status-DvSu8reM.js";
3
3
 
4
4
  export { displayStatus };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { a as getHistoryPath, c as loadConfig, i as getConfigDirShort } from "./derive-BR6N1ZjI.js";
3
- import { c as resolveWallet, f as formatAmount, g as dim, h as readHistory, m as formatUsdcValue, n as fetchAllBalances, p as formatTxLine, t as balanceLine, u as calcSpend } from "./wallet-CeY5DPj-.js";
3
+ import { c as resolveWallet, f as formatAmount, g as dim, h as readHistory, m as formatUsdcValue, n as fetchAllBalances, p as formatTxLine, t as balanceLine, u as calcSpend } from "./wallet-BiX5_XFY.js";
4
4
  import { buildCommand } from "@stricli/core";
5
5
  import pc from "picocolors";
6
6
 
@@ -236,6 +236,28 @@ function networkToCaipPrefix(name) {
236
236
  default: return name;
237
237
  }
238
238
  }
239
+ /**
240
+ * Validate that payTo addresses match the network format.
241
+ * Filters out malformed entries (e.g. EVM hex address on a Solana network).
242
+ */
243
+ function createAddressValidationPolicy() {
244
+ return (_version, reqs) => {
245
+ const malformed = [];
246
+ const valid = reqs.filter((r) => {
247
+ if (r.network.startsWith("solana:") && r.payTo.startsWith("0x")) {
248
+ malformed.push(`Solana option has EVM-format payTo (${r.payTo})`);
249
+ return false;
250
+ }
251
+ if (r.network.startsWith("eip155:") && !r.payTo.startsWith("0x")) {
252
+ malformed.push(`EVM option has non-EVM payTo (${r.payTo})`);
253
+ return false;
254
+ }
255
+ return true;
256
+ });
257
+ if (valid.length === 0 && malformed.length > 0) throw new Error(`Server returned only malformed payment options:\n ${malformed.join("\n ")}\nThe server's payTo addresses don't match the advertised networks.`);
258
+ return valid;
259
+ };
260
+ }
239
261
  function createNetworkFilter(network) {
240
262
  const prefix = networkToCaipPrefix(network);
241
263
  return (_version, reqs) => {
@@ -269,6 +291,7 @@ async function buildX402Client(wallet, opts) {
269
291
  const { createKeyPairSignerFromBytes } = await import("@solana/kit");
270
292
  registerExactSvmScheme(client, { signer: await createKeyPairSignerFromBytes(wallet.solanaKey) });
271
293
  }
294
+ client.registerPolicy(createAddressValidationPolicy());
272
295
  if (opts?.network) client.registerPolicy(createNetworkFilter(opts.network));
273
296
  const daily = opts?.spendLimitDaily;
274
297
  const perTx = opts?.spendLimitPerTx;
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { a as fetchTempoBalances, i as fetchSolanaBalances, n as fetchAllBalances, o as walletInfoCommand, r as fetchEvmBalances, t as balanceLine } from "./wallet-CeY5DPj-.js";
2
+ import { a as fetchTempoBalances, i as fetchSolanaBalances, n as fetchAllBalances, o as walletInfoCommand, r as fetchEvmBalances, t as balanceLine } from "./wallet-BiX5_XFY.js";
3
3
 
4
4
  export { fetchAllBalances };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x402-proxy",
3
- "version": "0.8.2",
3
+ "version": "0.8.4",
4
4
  "description": "curl for x402 paid APIs. Auto-pays any endpoint on Base, Solana, and Tempo. Also works as an OpenClaw plugin.",
5
5
  "type": "module",
6
6
  "sideEffects": false,