x402-proxy 0.7.1 → 0.8.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/CHANGELOG.md CHANGED
@@ -7,6 +7,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.8.1] - 2026-03-24
11
+
12
+ ### Added
13
+ - Curl-style short flags: `-X` (method), `-H` (header), `-d` (body) for the `fetch` command
14
+ - `-H` preprocessing in CLI entry point to work around Stricli reserving `-H` for `--help-all`
15
+
16
+ ### Fixed
17
+ - SSE streaming resilience: swallow Node.js "terminated" errors when server closes connection after final event, so payment logging still completes
18
+ - Bumped `mppx` to ^0.4.9 (fixes 204-safe SSE receipt wrapping and idempotent voucher replay)
19
+
20
+ ## [0.8.0] - 2026-03-21
21
+
22
+ ### Added
23
+ - `config` command with `show`, `set`, and `unset` subcommands for managing configuration from the CLI (no more manual YAML editing)
24
+ - Setup wizard now asks about preferred payment protocol (x402/MPP) and network
25
+ - MCP proxy handles `McpError(-32042)` from dual-protocol servers that throw instead of using `isError`, automatically retrying with x402 payment
26
+ - MPP payment amounts captured from challenges and displayed in payment logs, MCP proxy output, and transaction history
27
+ - `formatAmount()` and `formatUsdcValue()` exported from library for adaptive USDC precision formatting
28
+
29
+ ### Changed
30
+ - USDC amounts displayed with adaptive precision (2-6 decimals based on magnitude) instead of fixed 4 decimals everywhere
31
+ - Zero-balance detection uses numeric comparison instead of string matching
32
+
10
33
  ## [0.7.1] - 2026-03-20
11
34
 
12
35
  ### Fixed
@@ -191,7 +214,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
191
214
  - `appendHistory` / `readHistory` / `calcSpend` - JSONL transaction history
192
215
  - Re-exports from `@x402/fetch`, `@x402/svm`, `@x402/evm`
193
216
 
194
- [Unreleased]: https://github.com/cascade-protocol/x402-proxy/compare/v0.7.1...HEAD
217
+ [Unreleased]: https://github.com/cascade-protocol/x402-proxy/compare/v0.8.1...HEAD
218
+ [0.8.1]: https://github.com/cascade-protocol/x402-proxy/compare/v0.8.0...v0.8.1
219
+ [0.8.0]: https://github.com/cascade-protocol/x402-proxy/compare/v0.7.1...v0.8.0
195
220
  [0.7.1]: https://github.com/cascade-protocol/x402-proxy/compare/v0.7.0...v0.7.1
196
221
  [0.7.0]: https://github.com/cascade-protocol/x402-proxy/compare/v0.6.0...v0.7.0
197
222
  [0.6.0]: https://github.com/cascade-protocol/x402-proxy/compare/v0.5.2...v0.6.0
package/README.md CHANGED
@@ -40,10 +40,10 @@ Works like curl. Response body streams to stdout, payment info goes to stderr.
40
40
  # GET request
41
41
  $ npx x402-proxy https://twitter.surf.cascade.fyi/users/cascade_fyi
42
42
 
43
- # POST with body and headers
44
- $ npx x402-proxy --method POST \
45
- --header "Content-Type: application/json" \
46
- --body '{"url":"https://x402.org"}' \
43
+ # POST with body and headers (curl-style short flags: -X, -H, -d)
44
+ $ npx x402-proxy -X POST \
45
+ -H "Content-Type: application/json" \
46
+ -d '{"url":"https://x402.org"}' \
47
47
  https://web.surf.cascade.fyi/v1/crawl
48
48
 
49
49
  # Force a specific network
@@ -54,9 +54,8 @@ $ npx x402-proxy --verbose https://api.example.com/data
54
54
 
55
55
  # Use MPP protocol for streaming payments
56
56
  $ npx x402-proxy --protocol mpp \
57
- --method POST \
58
- --header "Content-Type: application/json" \
59
- --body '{"model":"minimax/minimax-m2.5","stream":true,"messages":[{"role":"user","content":"Hello"}]}' \
57
+ -X POST -H "Content-Type: application/json" \
58
+ -d '{"model":"minimax/minimax-m2.5","stream":true,"messages":[{"role":"user","content":"Hello"}]}' \
60
59
  https://inference.surf.cascade.fyi/v1/chat/completions
61
60
 
62
61
  # Pipe-safe
@@ -70,6 +69,9 @@ $ npx x402-proxy <url> # paid HTTP request (default command)
70
69
  $ npx x402-proxy mcp <url> # MCP stdio proxy for agents
71
70
  $ npx x402-proxy setup # onboarding wizard
72
71
  $ npx x402-proxy status # config + wallet + spend summary
72
+ $ npx x402-proxy config # show current configuration
73
+ $ npx x402-proxy config set <key> <value> # set a config value
74
+ $ npx x402-proxy config unset <key> # remove a config value
73
75
  $ npx x402-proxy wallet # show addresses and balances
74
76
  $ npx x402-proxy wallet history # payment history
75
77
  $ npx x402-proxy wallet export-key <target> # bare key/mnemonic to stdout (evm|solana|mnemonic)
package/dist/bin/cli.js CHANGED
@@ -1,14 +1,165 @@
1
1
  #!/usr/bin/env node
2
- import { _ as formatTxLine, c as resolveWallet, d as info, f as isTTY, g as displayNetwork, h as calcSpend, l as dim, m as appendHistory, o as walletInfoCommand, p as warn, s as buildX402Client, u as error, v as readHistory } from "../wallet-DxKCHa7U.js";
3
- import { a as getHistoryPath, c as loadConfig, l as loadWalletFile, s as isConfigured } from "../derive-ibF2UinV.js";
4
- import { n as setupCommand } from "../setup-hJGkO2Lo.js";
5
- import { n as statusCommand } from "../status-JNGv2Ghp.js";
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";
4
+ import { n as setupCommand } from "../setup-Di_b5Vp9.js";
5
+ import { n as statusCommand } from "../status-S3t-XV_M.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";
9
9
  import { base58 } from "@scure/base";
10
10
  import * as prompts from "@clack/prompts";
11
11
 
12
+ //#region src/commands/config.ts
13
+ const VALID_KEYS = {
14
+ defaultNetwork: {
15
+ description: "Preferred network (base, solana, tempo)",
16
+ parse: (v) => {
17
+ if (![
18
+ "base",
19
+ "solana",
20
+ "tempo"
21
+ ].includes(v)) throw new Error("Must be one of: base, solana, tempo");
22
+ return v;
23
+ }
24
+ },
25
+ preferredProtocol: {
26
+ description: "Payment protocol (x402, mpp)",
27
+ parse: (v) => {
28
+ if (!["x402", "mpp"].includes(v)) throw new Error("Must be one of: x402, mpp");
29
+ return v;
30
+ }
31
+ },
32
+ mppSessionBudget: {
33
+ description: "Max USDC per MPP session (default: 1)",
34
+ parse: (v) => {
35
+ const n = Number(v);
36
+ if (Number.isNaN(n) || n <= 0) throw new Error("Must be a positive number");
37
+ return v;
38
+ }
39
+ },
40
+ spendLimitDaily: {
41
+ description: "Daily spending limit in USDC",
42
+ parse: (v) => {
43
+ const n = Number(v);
44
+ if (Number.isNaN(n) || n <= 0) throw new Error("Must be a positive number");
45
+ return n;
46
+ }
47
+ },
48
+ spendLimitPerTx: {
49
+ description: "Per-transaction spending limit in USDC",
50
+ parse: (v) => {
51
+ const n = Number(v);
52
+ if (Number.isNaN(n) || n <= 0) throw new Error("Must be a positive number");
53
+ return n;
54
+ }
55
+ }
56
+ };
57
+ function isConfigKey(k) {
58
+ return k in VALID_KEYS;
59
+ }
60
+ const configShowCommand = buildCommand({
61
+ docs: { brief: "Show current configuration" },
62
+ parameters: {
63
+ flags: {},
64
+ positional: {
65
+ kind: "tuple",
66
+ parameters: []
67
+ }
68
+ },
69
+ async func() {
70
+ const config = loadConfig();
71
+ console.log();
72
+ console.log(pc.bold("Configuration"));
73
+ dim(` ${getConfigDirShort()}/config.yaml`);
74
+ console.log();
75
+ if (!config || Object.keys(config).length === 0) {
76
+ dim(" No configuration set. Using defaults.");
77
+ console.log();
78
+ dim(" Available keys:");
79
+ for (const [key, meta] of Object.entries(VALID_KEYS)) dim(` ${pc.cyan(key)} - ${meta.description}`);
80
+ console.log();
81
+ dim(` Set with: ${pc.cyan("npx x402-proxy config set <key> <value>")}`);
82
+ console.log();
83
+ return;
84
+ }
85
+ for (const key of Object.keys(VALID_KEYS)) {
86
+ const value = config[key];
87
+ if (value !== void 0) console.log(` ${pc.cyan(key)}: ${pc.green(String(value))}`);
88
+ else dim(` ${key}: ${pc.dim("(not set)")}`);
89
+ }
90
+ console.log();
91
+ }
92
+ });
93
+ const configSetCommand = buildCommand({
94
+ docs: {
95
+ brief: "Set a configuration value",
96
+ fullDescription: `Set a configuration value.
97
+
98
+ Available keys:
99
+ ${Object.entries(VALID_KEYS).map(([k, v]) => ` ${k} - ${v.description}`).join("\n")}
100
+
101
+ To unset a value, use: npx x402-proxy config unset <key>`
102
+ },
103
+ parameters: {
104
+ flags: {},
105
+ positional: {
106
+ kind: "tuple",
107
+ parameters: [{
108
+ brief: "Configuration key",
109
+ parse: String
110
+ }, {
111
+ brief: "Value to set",
112
+ parse: String
113
+ }]
114
+ }
115
+ },
116
+ async func(_flags, key, value) {
117
+ if (!isConfigKey(key)) {
118
+ error(`Unknown config key: ${key}`);
119
+ console.error();
120
+ dim(" Available keys:");
121
+ for (const [k, m] of Object.entries(VALID_KEYS)) dim(` ${pc.cyan(k)} - ${m.description}`);
122
+ process.exit(1);
123
+ }
124
+ const meta = VALID_KEYS[key];
125
+ let parsed;
126
+ try {
127
+ parsed = meta.parse(value);
128
+ } catch (err) {
129
+ error(`Invalid value for ${key}: ${err instanceof Error ? err.message : String(err)}`);
130
+ process.exit(1);
131
+ }
132
+ const config = loadConfig() ?? {};
133
+ config[key] = parsed;
134
+ saveConfig(config);
135
+ console.log(` ${pc.cyan(key)} = ${pc.green(String(parsed))}`);
136
+ }
137
+ });
138
+ const configUnsetCommand = buildCommand({
139
+ docs: { brief: "Unset a configuration value" },
140
+ parameters: {
141
+ flags: {},
142
+ positional: {
143
+ kind: "tuple",
144
+ parameters: [{
145
+ brief: "Configuration key to remove",
146
+ parse: String
147
+ }]
148
+ }
149
+ },
150
+ async func(_flags, key) {
151
+ if (!isConfigKey(key)) {
152
+ error(`Unknown config key: ${key}`);
153
+ process.exit(1);
154
+ }
155
+ const config = loadConfig() ?? {};
156
+ delete config[key];
157
+ saveConfig(config);
158
+ dim(` ${key} unset`);
159
+ }
160
+ });
161
+
162
+ //#endregion
12
163
  //#region src/handler.ts
13
164
  /**
14
165
  * Detect which payment protocols a 402 response advertises.
@@ -70,30 +221,40 @@ async function createMppProxyHandler(opts) {
70
221
  const account = privateKeyToAccount(opts.evmKey);
71
222
  const maxDeposit = opts.maxDeposit ?? "1";
72
223
  const paymentQueue = [];
224
+ let lastChallengeAmount;
73
225
  const mppx = Mppx.create({
74
226
  methods: [tempo({
75
227
  account,
76
228
  maxDeposit
77
229
  })],
78
- polyfill: false
230
+ polyfill: false,
231
+ onChallenge: async (challenge) => {
232
+ const req = challenge.request;
233
+ if (req.amount) lastChallengeAmount = (Number(req.amount) / 10 ** (req.decimals ?? 6)).toString();
234
+ }
79
235
  });
80
236
  let session;
81
237
  return {
82
238
  async fetch(input, init) {
83
239
  const response = await mppx.fetch(typeof input === "string" ? input : input.toString(), init);
84
240
  const receiptHeader = response.headers.get("Payment-Receipt");
85
- if (receiptHeader) try {
86
- const receipt = JSON.parse(Buffer.from(receiptHeader, "base64url").toString());
87
- paymentQueue.push({
88
- protocol: "mpp",
89
- network: TEMPO_NETWORK,
90
- receipt
91
- });
92
- } catch {
93
- paymentQueue.push({
94
- protocol: "mpp",
95
- network: TEMPO_NETWORK
96
- });
241
+ if (receiptHeader) {
242
+ try {
243
+ const receipt = JSON.parse(Buffer.from(receiptHeader, "base64url").toString());
244
+ paymentQueue.push({
245
+ protocol: "mpp",
246
+ network: TEMPO_NETWORK,
247
+ amount: lastChallengeAmount,
248
+ receipt
249
+ });
250
+ } catch {
251
+ paymentQueue.push({
252
+ protocol: "mpp",
253
+ network: TEMPO_NETWORK,
254
+ amount: lastChallengeAmount
255
+ });
256
+ }
257
+ lastChallengeAmount = void 0;
97
258
  }
98
259
  return response;
99
260
  },
@@ -206,6 +367,10 @@ Examples:
206
367
  default: false
207
368
  }
208
369
  },
370
+ aliases: {
371
+ X: "method",
372
+ d: "body"
373
+ },
209
374
  positional: {
210
375
  kind: "tuple",
211
376
  parameters: [{
@@ -230,7 +395,7 @@ Examples:
230
395
  };
231
396
  if (!url) {
232
397
  if (isConfigured()) {
233
- const { displayStatus } = await import("../status-w5y-fhhe.js");
398
+ const { displayStatus } = await import("../status-CJPUbh6Z.js");
234
399
  await displayStatus();
235
400
  console.log();
236
401
  console.log(pc.dim(" Commands:"));
@@ -282,7 +447,7 @@ Examples:
282
447
  process.exit(1);
283
448
  }
284
449
  dim(" No wallet found. Let's set one up first.\n");
285
- const { runSetup } = await import("../setup-j_xQ14-4.js");
450
+ const { runSetup } = await import("../setup-CJwYRd78.js");
286
451
  await runSetup();
287
452
  console.log();
288
453
  wallet = resolveWallet();
@@ -295,7 +460,7 @@ Examples:
295
460
  verbose(`protocol: ${resolvedProtocol ?? "auto-detect"}, maxDeposit: ${maxDeposit}`);
296
461
  let preferredNetwork = config?.defaultNetwork;
297
462
  if (!preferredNetwork && wallet.evmAddress && wallet.solanaAddress) {
298
- const { fetchAllBalances } = await import("../wallet-DjixXCHy.js");
463
+ const { fetchAllBalances } = await import("../wallet-CJBRFJw8.js");
299
464
  const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
300
465
  const evmUsdc = balances.evm ? Number(balances.evm.usdc) : 0;
301
466
  const solUsdc = balances.sol ? Number(balances.sol.usdc) : 0;
@@ -336,7 +501,13 @@ Examples:
336
501
  verbose("opening SSE session...");
337
502
  const tokens = await mppHandler.sse(parsedUrl.toString(), init);
338
503
  verbose("SSE stream opened, reading tokens...");
339
- for await (const token of tokens) process.stdout.write(token);
504
+ try {
505
+ for await (const token of tokens) process.stdout.write(token);
506
+ } catch (streamErr) {
507
+ const msg = streamErr instanceof Error ? streamErr.message : String(streamErr);
508
+ verbose(`SSE stream error: ${msg}`);
509
+ if (!msg.includes("terminated")) throw streamErr;
510
+ }
340
511
  verbose("SSE stream complete");
341
512
  } finally {
342
513
  await closeMppSession(mppHandler);
@@ -352,7 +523,7 @@ Examples:
352
523
  usedProtocol = "mpp";
353
524
  const elapsedMs = Date.now() - startMs;
354
525
  const spentAmount = mppPayment.amount ? Number(mppPayment.amount) : void 0;
355
- if (mppPayment && isTTY()) info(` MPP session: ${spentAmount != null ? `${spentAmount.toFixed(4)} USDC ` : ""}(${displayNetwork(mppPayment.network)})`);
526
+ if (mppPayment && isTTY()) info(` MPP session: ${spentAmount != null ? `${formatAmount(spentAmount, "USDC")} ` : ""}(${displayNetwork(mppPayment.network)})`);
356
527
  if (isTTY()) dim(` Streamed (${elapsedMs}ms)`);
357
528
  if (mppPayment) {
358
529
  const record = {
@@ -445,13 +616,13 @@ Examples:
445
616
  if (accepts.length > 0) {
446
617
  const cheapest = accepts.reduce((min, a) => Number(a.amount) < Number(min.amount) ? a : min);
447
618
  costNum = Number(cheapest.amount) / 1e6;
448
- costStr = costNum.toFixed(4);
619
+ costStr = formatUsdcValue(costNum);
449
620
  }
450
621
  const hasEvm = accepts.some((a) => a.network.startsWith("eip155:"));
451
622
  const hasSolana = accepts.some((a) => a.network.startsWith("solana:"));
452
623
  const hasMpp = detected.mpp;
453
624
  const hasOther = accepts.some((a) => !a.network.startsWith("eip155:") && !a.network.startsWith("solana:"));
454
- const { fetchAllBalances } = await import("../wallet-DjixXCHy.js");
625
+ const { fetchAllBalances } = await import("../wallet-CJBRFJw8.js");
455
626
  const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
456
627
  const evmUsdc = hasEvm && balances.evm ? Number(balances.evm.usdc) : 0;
457
628
  const solUsdc = hasSolana && balances.sol ? Number(balances.sol.usdc) : 0;
@@ -470,9 +641,9 @@ Examples:
470
641
  if (payment) dim(" Payment was signed and sent but rejected by the server.");
471
642
  else dim(" Payment was not attempted despite sufficient balance.");
472
643
  if (serverReason) dim(` Reason: ${serverReason}`);
473
- if (hasEvm && wallet.evmAddress && evmUsdc > 0) console.error(` Base: ${pc.cyan(wallet.evmAddress)} ${pc.dim(`(${evmUsdc.toFixed(4)} USDC)`)}`);
474
- if (hasMpp && wallet.evmAddress && tempoUsdc > 0) console.error(` Tempo: ${pc.cyan(wallet.evmAddress)} ${pc.dim(`(${tempoUsdc.toFixed(4)} USDC)`)}`);
475
- if (hasSolana && wallet.solanaAddress && solUsdc > 0) console.error(` Solana: ${pc.cyan(wallet.solanaAddress)} ${pc.dim(`(${solUsdc.toFixed(4)} USDC)`)}`);
644
+ if (hasEvm && wallet.evmAddress && evmUsdc > 0) console.error(` Base: ${pc.cyan(wallet.evmAddress)} ${pc.dim(`(${formatAmount(evmUsdc, "USDC")})`)}`);
645
+ if (hasMpp && wallet.evmAddress && tempoUsdc > 0) console.error(` Tempo: ${pc.cyan(wallet.evmAddress)} ${pc.dim(`(${formatAmount(tempoUsdc, "USDC")})`)}`);
646
+ if (hasSolana && wallet.solanaAddress && solUsdc > 0) console.error(` Solana: ${pc.cyan(wallet.solanaAddress)} ${pc.dim(`(${formatAmount(solUsdc, "USDC")})`)}`);
476
647
  console.error();
477
648
  dim(" This may be a temporary server-side issue. Try again in a moment.");
478
649
  console.error();
@@ -482,15 +653,15 @@ Examples:
482
653
  console.error();
483
654
  dim(" Fund your wallet with USDC:");
484
655
  if (hasEvm && wallet.evmAddress) {
485
- const balHint = evmUsdc > 0 ? pc.dim(` (${evmUsdc.toFixed(4)} USDC)`) : "";
656
+ const balHint = evmUsdc > 0 ? pc.dim(` (${formatAmount(evmUsdc, "USDC")})`) : "";
486
657
  console.error(` Base: ${pc.cyan(wallet.evmAddress)}${balHint}`);
487
658
  }
488
659
  if (hasMpp && wallet.evmAddress) {
489
- const balHint = tempoUsdc > 0 ? pc.dim(` (${tempoUsdc.toFixed(4)} USDC)`) : "";
660
+ const balHint = tempoUsdc > 0 ? pc.dim(` (${formatAmount(tempoUsdc, "USDC")})`) : "";
490
661
  console.error(` Tempo: ${pc.cyan(wallet.evmAddress)}${balHint}`);
491
662
  }
492
663
  if (hasSolana && wallet.solanaAddress) {
493
- const balHint = solUsdc > 0 ? pc.dim(` (${solUsdc.toFixed(4)} USDC)`) : "";
664
+ const balHint = solUsdc > 0 ? pc.dim(` (${formatAmount(solUsdc, "USDC")})`) : "";
494
665
  console.error(` Solana: ${pc.cyan(wallet.solanaAddress)}${balHint}`);
495
666
  }
496
667
  if (hasEvm && !wallet.evmAddress) dim(" Base: endpoint accepts EVM but no EVM wallet configured");
@@ -509,8 +680,8 @@ Examples:
509
680
  return;
510
681
  }
511
682
  if (payment && isTTY()) {
512
- if (usedProtocol === "mpp" && mppPayment) info(` Payment: MPP (${displayNetwork(mppPayment.network)})`);
513
- else if (x402Payment) info(` Payment: ${x402Payment.amount ? (Number(x402Payment.amount) / 1e6).toFixed(4) : "?"} USDC (${displayNetwork(x402Payment.network ?? "unknown")})`);
683
+ if (usedProtocol === "mpp" && mppPayment) info(` Payment:${mppPayment.amount ? ` ${formatAmount(Number(mppPayment.amount), "USDC")}` : ""} MPP (${displayNetwork(mppPayment.network)})`);
684
+ else if (x402Payment) info(` Payment: ${x402Payment.amount ? formatAmount(Number(x402Payment.amount) / 1e6, "USDC") : "? USDC"} (${displayNetwork(x402Payment.network ?? "unknown")})`);
514
685
  if (txSig) dim(` Tx: ${txSig}`);
515
686
  }
516
687
  if (isTTY()) dim(` ${response.status} ${response.statusText} (${elapsedMs}ms)`);
@@ -626,7 +797,7 @@ Add to your MCP client config (Claude, Cursor, etc.):
626
797
  const { StreamableHTTPClientTransport } = await import("@modelcontextprotocol/sdk/client/streamableHttp.js");
627
798
  const { Server } = await import("@modelcontextprotocol/sdk/server/index.js");
628
799
  const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
629
- const { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ToolListChangedNotificationSchema, ResourceListChangedNotificationSchema } = await import("@modelcontextprotocol/sdk/types.js");
800
+ const { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ToolListChangedNotificationSchema, ResourceListChangedNotificationSchema, McpError } = await import("@modelcontextprotocol/sdk/types.js");
630
801
  async function connectTransport(target) {
631
802
  try {
632
803
  const transport = new StreamableHTTPClientTransport(new URL(remoteUrl));
@@ -648,7 +819,7 @@ Add to your MCP client config (Claude, Cursor, etc.):
648
819
  async function startX402Proxy() {
649
820
  let preferredNetwork = config?.defaultNetwork;
650
821
  if (!preferredNetwork && wallet.evmAddress && wallet.solanaAddress) {
651
- const { fetchAllBalances } = await import("../wallet-DjixXCHy.js");
822
+ const { fetchAllBalances } = await import("../wallet-CJBRFJw8.js");
652
823
  const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
653
824
  const evmUsdc = balances.evm ? Number(balances.evm.usdc) : 0;
654
825
  const solUsdc = balances.sol ? Number(balances.sol.usdc) : 0;
@@ -662,15 +833,18 @@ Add to your MCP client config (Claude, Cursor, etc.):
662
833
  spendLimitPerTx: config?.spendLimitPerTx
663
834
  });
664
835
  const { x402MCPClient } = await import("@x402/mcp");
836
+ function warnPayment(accepts, toolName) {
837
+ const accept = accepts?.[0];
838
+ if (accept) warn(` Payment: ${accept.amount ? formatAmount(Number(accept.amount) / 1e6, "USDC") : "? USDC"} on ${displayNetwork(accept.network)} for tool "${toolName}"`);
839
+ }
665
840
  const remoteClient = new Client({
666
841
  name: "x402-proxy",
667
- version: "0.7.1"
842
+ version: "0.8.1"
668
843
  });
669
844
  const x402Mcp = new x402MCPClient(remoteClient, x402PaymentClient, {
670
845
  autoPayment: true,
671
846
  onPaymentRequested: (ctx) => {
672
- const accept = ctx.paymentRequired.accepts?.[0];
673
- if (accept) warn(` Payment: ${accept.amount ? (Number(accept.amount) / 1e6).toFixed(4) : "?"} USDC on ${displayNetwork(accept.network)} for tool "${ctx.toolName}"`);
847
+ warnPayment(ctx.paymentRequired.accepts, ctx.toolName);
674
848
  return true;
675
849
  }
676
850
  });
@@ -703,7 +877,7 @@ Add to your MCP client config (Claude, Cursor, etc.):
703
877
  }
704
878
  const localServer = new Server({
705
879
  name: "x402-proxy",
706
- version: "0.7.1"
880
+ version: "0.8.1"
707
881
  }, { capabilities: {
708
882
  tools: tools.length > 0 ? {} : void 0,
709
883
  resources: remoteResources.length > 0 ? {} : void 0
@@ -716,11 +890,28 @@ Add to your MCP client config (Claude, Cursor, etc.):
716
890
  })) }));
717
891
  localServer.setRequestHandler(CallToolRequestSchema, async (request) => {
718
892
  const { name, arguments: args } = request.params;
719
- const result = await x402Mcp.callTool(name, args ?? {});
720
- return {
721
- content: result.content,
722
- isError: result.isError
723
- };
893
+ try {
894
+ const result = await x402Mcp.callTool(name, args ?? {});
895
+ return {
896
+ content: result.content,
897
+ isError: result.isError
898
+ };
899
+ } catch (err) {
900
+ if (err instanceof McpError && err.code === -32042) {
901
+ const x402PaymentRequired = err.data?.x402;
902
+ if (x402PaymentRequired) {
903
+ const accepts = x402PaymentRequired.accepts;
904
+ warnPayment(accepts, name);
905
+ const paymentPayload = await x402PaymentClient.createPaymentPayload(x402PaymentRequired);
906
+ const result = await x402Mcp.callToolWithPayment(name, args ?? {}, paymentPayload);
907
+ return {
908
+ content: result.content,
909
+ isError: result.isError
910
+ };
911
+ }
912
+ }
913
+ throw err;
914
+ }
724
915
  });
725
916
  if (remoteResources.length > 0) {
726
917
  localServer.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: remoteResources.map((r) => ({
@@ -767,15 +958,24 @@ Add to your MCP client config (Claude, Cursor, etc.):
767
958
  const { privateKeyToAccount } = await import("viem/accounts");
768
959
  const account = privateKeyToAccount(wallet.evmKey);
769
960
  const maxDeposit = config?.mppSessionBudget ?? "1";
961
+ let lastChallengeAmount;
962
+ const wrappedMethods = tempo({
963
+ account,
964
+ maxDeposit
965
+ }).map((m) => ({
966
+ ...m,
967
+ createCredential: async (params) => {
968
+ const req = params.challenge.request;
969
+ if (req.amount) lastChallengeAmount = Number(req.amount) / 10 ** (req.decimals ?? 6);
970
+ return m.createCredential(params);
971
+ }
972
+ }));
770
973
  const remoteClient = new Client({
771
974
  name: "x402-proxy",
772
- version: "0.7.1"
975
+ version: "0.8.1"
773
976
  });
774
977
  await connectTransport(remoteClient);
775
- const mppClient = McpClient.wrap(remoteClient, { methods: [tempo({
776
- account,
777
- maxDeposit
778
- })] });
978
+ const mppClient = McpClient.wrap(remoteClient, { methods: wrappedMethods });
779
979
  let { tools } = await remoteClient.listTools();
780
980
  dim(` ${tools.length} tools available`);
781
981
  let remoteResources = [];
@@ -787,7 +987,7 @@ Add to your MCP client config (Claude, Cursor, etc.):
787
987
  }
788
988
  const localServer = new Server({
789
989
  name: "x402-proxy",
790
- version: "0.7.1"
990
+ version: "0.8.1"
791
991
  }, { capabilities: {
792
992
  tools: tools.length > 0 ? {} : void 0,
793
993
  resources: remoteResources.length > 0 ? {} : void 0
@@ -812,11 +1012,14 @@ Add to your MCP client config (Claude, Cursor, etc.):
812
1012
  net: TEMPO_NETWORK,
813
1013
  from: wallet.evmAddress ?? "unknown",
814
1014
  tx: result.receipt.reference,
1015
+ amount: lastChallengeAmount,
815
1016
  token: "USDC",
816
1017
  label: `mcp:${name}`
817
1018
  };
818
1019
  appendHistory(getHistoryPath(), record);
819
- warn(` MPP payment for tool "${name}" (Tempo)`);
1020
+ const amountStr = lastChallengeAmount !== void 0 ? formatAmount(lastChallengeAmount, "USDC") : "";
1021
+ warn(` MPP payment for tool "${name}" (Tempo)${amountStr ? ` \u00b7 ${amountStr}` : ""}`);
1022
+ lastChallengeAmount = void 0;
820
1023
  }
821
1024
  return {
822
1025
  content: result.content,
@@ -959,7 +1162,7 @@ const walletHistoryCommand = buildCommand({
959
1162
  console.log(line);
960
1163
  }
961
1164
  console.log();
962
- console.log(pc.dim(` Today: ${spend.today.toFixed(4)} USDC | Total: ${spend.total.toFixed(4)} USDC | ${spend.count} transactions`));
1165
+ console.log(pc.dim(` Today: ${formatAmount(spend.today, "USDC")} | Total: ${formatAmount(spend.total, "USDC")} | ${spend.count} transactions`));
963
1166
  console.log();
964
1167
  }
965
1168
  });
@@ -979,6 +1182,15 @@ const routes = buildRouteMap({
979
1182
  defaultCommand: "info",
980
1183
  docs: { brief: "Wallet management" }
981
1184
  }),
1185
+ config: buildRouteMap({
1186
+ routes: {
1187
+ show: configShowCommand,
1188
+ set: configSetCommand,
1189
+ unset: configUnsetCommand
1190
+ },
1191
+ defaultCommand: "show",
1192
+ docs: { brief: "Manage configuration" }
1193
+ }),
982
1194
  setup: setupCommand,
983
1195
  status: statusCommand
984
1196
  },
@@ -987,7 +1199,7 @@ const routes = buildRouteMap({
987
1199
  });
988
1200
  const app = buildApplication(routes, {
989
1201
  name: "x402-proxy",
990
- versionInfo: { currentVersion: "0.7.1" },
1202
+ versionInfo: { currentVersion: "0.8.1" },
991
1203
  scanner: { caseStyle: "allow-kebab-for-camel" }
992
1204
  });
993
1205
 
@@ -999,7 +1211,7 @@ function buildContext(process) {
999
1211
 
1000
1212
  //#endregion
1001
1213
  //#region src/bin/cli.ts
1002
- await run(app, process.argv.slice(2), buildContext(process));
1214
+ await run(app, process.argv.slice(2).map((a) => a === "-H" ? "--header" : a), buildContext(process));
1003
1215
 
1004
1216
  //#endregion
1005
1217
  export { };
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import fs from "node:fs";
3
- import path from "node:path";
4
3
  import os from "node:os";
4
+ import path from "node:path";
5
5
  import { parse, stringify } from "yaml";
6
6
  import { ed25519 } from "@noble/curves/ed25519.js";
7
7
  import { base58 } from "@scure/base";
package/dist/index.js CHANGED
@@ -67,30 +67,40 @@ async function createMppProxyHandler(opts) {
67
67
  const account = privateKeyToAccount(opts.evmKey);
68
68
  const maxDeposit = opts.maxDeposit ?? "1";
69
69
  const paymentQueue = [];
70
+ let lastChallengeAmount;
70
71
  const mppx = Mppx.create({
71
72
  methods: [tempo({
72
73
  account,
73
74
  maxDeposit
74
75
  })],
75
- polyfill: false
76
+ polyfill: false,
77
+ onChallenge: async (challenge) => {
78
+ const req = challenge.request;
79
+ if (req.amount) lastChallengeAmount = (Number(req.amount) / 10 ** (req.decimals ?? 6)).toString();
80
+ }
76
81
  });
77
82
  let session;
78
83
  return {
79
84
  async fetch(input, init) {
80
85
  const response = await mppx.fetch(typeof input === "string" ? input : input.toString(), init);
81
86
  const receiptHeader = response.headers.get("Payment-Receipt");
82
- if (receiptHeader) try {
83
- const receipt = JSON.parse(Buffer.from(receiptHeader, "base64url").toString());
84
- paymentQueue.push({
85
- protocol: "mpp",
86
- network: TEMPO_NETWORK,
87
- receipt
88
- });
89
- } catch {
90
- paymentQueue.push({
91
- protocol: "mpp",
92
- network: TEMPO_NETWORK
93
- });
87
+ if (receiptHeader) {
88
+ try {
89
+ const receipt = JSON.parse(Buffer.from(receiptHeader, "base64url").toString());
90
+ paymentQueue.push({
91
+ protocol: "mpp",
92
+ network: TEMPO_NETWORK,
93
+ amount: lastChallengeAmount,
94
+ receipt
95
+ });
96
+ } catch {
97
+ paymentQueue.push({
98
+ protocol: "mpp",
99
+ network: TEMPO_NETWORK,
100
+ amount: lastChallengeAmount
101
+ });
102
+ }
103
+ lastChallengeAmount = void 0;
94
104
  }
95
105
  return response;
96
106
  },
@@ -189,13 +199,15 @@ function calcSpend(records) {
189
199
  count
190
200
  };
191
201
  }
202
+ /** Format a USDC value with adaptive precision (no token suffix). */
203
+ function formatUsdcValue(amount) {
204
+ if (amount >= .01) return amount.toFixed(2);
205
+ if (amount >= .001) return amount.toFixed(3);
206
+ if (amount >= 1e-4) return amount.toFixed(4);
207
+ return amount.toFixed(6);
208
+ }
192
209
  function formatAmount(amount, token) {
193
- if (token === "USDC") {
194
- if (amount >= .01) return `${amount.toFixed(2)} USDC`;
195
- if (amount >= .001) return `${amount.toFixed(3)} USDC`;
196
- if (amount >= 1e-4) return `${amount.toFixed(4)} USDC`;
197
- return `${amount.toFixed(6)} USDC`;
198
- }
210
+ if (token === "USDC") return `${formatUsdcValue(amount)} USDC`;
199
211
  if (token === "SOL") return `${amount} SOL`;
200
212
  return `${amount} ${token}`;
201
213
  }
@@ -138,13 +138,15 @@ function calcSpend(records) {
138
138
  count
139
139
  };
140
140
  }
141
+ /** Format a USDC value with adaptive precision (no token suffix). */
142
+ function formatUsdcValue(amount) {
143
+ if (amount >= .01) return amount.toFixed(2);
144
+ if (amount >= .001) return amount.toFixed(3);
145
+ if (amount >= 1e-4) return amount.toFixed(4);
146
+ return amount.toFixed(6);
147
+ }
141
148
  function formatAmount(amount, token) {
142
- if (token === "USDC") {
143
- if (amount >= .01) return `${amount.toFixed(2)} USDC`;
144
- if (amount >= .001) return `${amount.toFixed(3)} USDC`;
145
- if (amount >= 1e-4) return `${amount.toFixed(4)} USDC`;
146
- return `${amount.toFixed(6)} USDC`;
147
- }
149
+ if (token === "USDC") return `${formatUsdcValue(amount)} USDC`;
148
150
  if (token === "SOL") return `${amount} SOL`;
149
151
  return `${amount} ${token}`;
150
152
  }
@@ -502,8 +504,8 @@ function createBalanceTool(ctx) {
502
504
  `USDC: ${snap.ui} USDC`,
503
505
  `Available for tools: ${available.toFixed(2)} USDC`,
504
506
  `Reserved for inference: ${INFERENCE_RESERVE.toFixed(2)} USDC`,
505
- `Spent today: ${snap.spend.today.toFixed(4)} USDC`,
506
- `Total spent: ${snap.spend.total.toFixed(4)} USDC (${snap.spend.count} txs)`,
507
+ `Spent today: ${formatAmount(snap.spend.today, "USDC")}`,
508
+ `Total spent: ${formatAmount(snap.spend.total, "USDC")} (${snap.spend.count} txs)`,
507
509
  ...tokenLines.length > 0 ? [`Tokens held: ${tokenLines.join(", ")}`] : []
508
510
  ].join("\n"));
509
511
  } catch (err) {
@@ -724,7 +726,7 @@ function createWalletCommand(ctx) {
724
726
  try {
725
727
  const snap = await getWalletSnapshot(ctx.rpcUrl, walletAddress, ctx.historyPath);
726
728
  const solscanUrl = `https://solscan.io/account/${walletAddress}`;
727
- const lines = [`x402-proxy v0.7.1`];
729
+ const lines = [`x402-proxy v0.8.1`];
728
730
  const defaultModel = ctx.allModels[0];
729
731
  if (defaultModel) lines.push("", `**Model** - ${defaultModel.name} (${defaultModel.provider})`);
730
732
  lines.push("", `**[Wallet](${solscanUrl})**`, `\`${walletAddress}\``);
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { n as setupCommand, t as runSetup } from "./setup-Di_b5Vp9.js";
3
+
4
+ export { runSetup };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { d as saveWalletFile, i as getConfigDirShort, n as deriveSolanaKeypair, o as getWalletPath, r as generateMnemonic, s as isConfigured, t as deriveEvmKeypair, u as saveConfig } from "./derive-ibF2UinV.js";
2
+ import { d as saveWalletFile, i as getConfigDirShort, n as deriveSolanaKeypair, o as getWalletPath, r as generateMnemonic, s as isConfigured, t as deriveEvmKeypair, u as saveConfig } from "./derive-BR6N1ZjI.js";
3
3
  import { buildCommand } from "@stricli/core";
4
4
  import pc from "picocolors";
5
5
  import * as prompts from "@clack/prompts";
@@ -57,7 +57,48 @@ async function runSetup(opts) {
57
57
  solana: sol.address
58
58
  }
59
59
  });
60
- saveConfig({});
60
+ const protocol = await prompts.select({
61
+ message: "Preferred payment protocol?",
62
+ options: [{
63
+ value: "x402",
64
+ label: "x402 - on-chain payments (Base, Solana)"
65
+ }, {
66
+ value: "mpp",
67
+ label: "MPP - streaming micropayments (Tempo)"
68
+ }]
69
+ });
70
+ if (prompts.isCancel(protocol)) {
71
+ prompts.cancel("Setup cancelled.");
72
+ process.exit(0);
73
+ }
74
+ const networkOptions = protocol === "mpp" ? [{
75
+ value: "tempo",
76
+ label: "Tempo"
77
+ }] : [
78
+ {
79
+ value: "auto",
80
+ label: "Auto-detect (pick chain with highest balance)"
81
+ },
82
+ {
83
+ value: "base",
84
+ label: "Base (EVM)"
85
+ },
86
+ {
87
+ value: "solana",
88
+ label: "Solana"
89
+ }
90
+ ];
91
+ const network = await prompts.select({
92
+ message: "Preferred network?",
93
+ options: networkOptions
94
+ });
95
+ if (prompts.isCancel(network)) {
96
+ prompts.cancel("Setup cancelled.");
97
+ process.exit(0);
98
+ }
99
+ const config = { preferredProtocol: protocol };
100
+ if (network !== "auto") config.defaultNetwork = network;
101
+ saveConfig(config);
61
102
  prompts.log.info(`Config directory: ${pc.dim(getConfigDirShort())}`);
62
103
  prompts.log.step("Fund your wallets to start using x402 resources:");
63
104
  prompts.log.message(` Solana (USDC): Send USDC to ${pc.cyan(sol.address)}`);
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { n as statusCommand, t as displayStatus } from "./status-JNGv2Ghp.js";
2
+ import { n as statusCommand, t as displayStatus } from "./status-S3t-XV_M.js";
3
3
 
4
4
  export { displayStatus };
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { _ as formatTxLine, c as resolveWallet, h as calcSpend, l as dim, n as fetchAllBalances, t as balanceLine, v as readHistory } from "./wallet-DxKCHa7U.js";
3
- import { a as getHistoryPath, c as loadConfig, i as getConfigDirShort } from "./derive-ibF2UinV.js";
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";
4
4
  import { buildCommand } from "@stricli/core";
5
5
  import pc from "picocolors";
6
6
 
@@ -35,7 +35,7 @@ async function displayStatus() {
35
35
  console.log();
36
36
  if (config.spendLimitDaily) {
37
37
  const pct = config.spendLimitDaily > 0 ? Math.round(spend.today / config.spendLimitDaily * 100) : 0;
38
- dim(` Daily limit: ${spend.today.toFixed(4)} / ${config.spendLimitDaily} USDC (${pct}%)`);
38
+ dim(` Daily limit: ${formatUsdcValue(spend.today)} / ${config.spendLimitDaily} USDC (${pct}%)`);
39
39
  }
40
40
  if (config.spendLimitPerTx) dim(` Per-tx limit: ${config.spendLimitPerTx} USDC`);
41
41
  }
@@ -49,7 +49,7 @@ async function displayStatus() {
49
49
  console.log(line);
50
50
  }
51
51
  console.log();
52
- dim(` Today: ${spend.today.toFixed(4)} USDC | Total: ${spend.total.toFixed(4)} USDC | ${spend.count} tx`);
52
+ dim(` Today: ${formatAmount(spend.today, "USDC")} | Total: ${formatAmount(spend.total, "USDC")} | ${spend.count} tx`);
53
53
  } else dim(" No payment history yet.");
54
54
  console.log();
55
55
  if (config?.defaultNetwork) dim(` Network: ${config.defaultNetwork}`);
@@ -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-DxKCHa7U.js";
2
+ import { a as fetchTempoBalances, i as fetchSolanaBalances, n as fetchAllBalances, o as walletInfoCommand, r as fetchEvmBalances, t as balanceLine } from "./wallet-CeY5DPj-.js";
3
3
 
4
4
  export { fetchAllBalances };
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { a as getHistoryPath, l as loadWalletFile, n as deriveSolanaKeypair, t as deriveEvmKeypair } from "./derive-ibF2UinV.js";
2
+ import { a as getHistoryPath, l as loadWalletFile, n as deriveSolanaKeypair, t as deriveEvmKeypair } from "./derive-BR6N1ZjI.js";
3
3
  import { buildCommand } from "@stricli/core";
4
4
  import pc from "picocolors";
5
- import { x402Client } from "@x402/fetch";
6
5
  import { appendFileSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
7
6
  import { dirname } from "node:path";
7
+ import { x402Client } from "@x402/fetch";
8
8
  import { ed25519 } from "@noble/curves/ed25519.js";
9
9
  import { base58 } from "@scure/base";
10
10
  import { toClientEvmSigner } from "@x402/evm";
@@ -15,6 +15,24 @@ import { privateKeyToAccount } from "viem/accounts";
15
15
  import { base } from "viem/chains";
16
16
  import { address, getAddressEncoder, getProgramDerivedAddress } from "@solana/kit";
17
17
 
18
+ //#region src/lib/output.ts
19
+ function isTTY() {
20
+ return !!process.stderr.isTTY;
21
+ }
22
+ function info(msg) {
23
+ process.stderr.write(`${isTTY() ? pc.cyan(msg) : msg}\n`);
24
+ }
25
+ function warn(msg) {
26
+ process.stderr.write(`${isTTY() ? pc.yellow(msg) : msg}\n`);
27
+ }
28
+ function error(msg) {
29
+ process.stderr.write(`${isTTY() ? pc.red(`✗ ${msg}`) : `✗ ${msg}`}\n`);
30
+ }
31
+ function dim(msg) {
32
+ process.stderr.write(`${isTTY() ? pc.dim(msg) : msg}\n`);
33
+ }
34
+
35
+ //#endregion
18
36
  //#region src/history.ts
19
37
  const HISTORY_MAX_LINES = 1e3;
20
38
  const HISTORY_KEEP_LINES = 500;
@@ -68,13 +86,15 @@ function calcSpend(records) {
68
86
  count
69
87
  };
70
88
  }
89
+ /** Format a USDC value with adaptive precision (no token suffix). */
90
+ function formatUsdcValue(amount) {
91
+ if (amount >= .01) return amount.toFixed(2);
92
+ if (amount >= .001) return amount.toFixed(3);
93
+ if (amount >= 1e-4) return amount.toFixed(4);
94
+ return amount.toFixed(6);
95
+ }
71
96
  function formatAmount(amount, token) {
72
- if (token === "USDC") {
73
- if (amount >= .01) return `${amount.toFixed(2)} USDC`;
74
- if (amount >= .001) return `${amount.toFixed(3)} USDC`;
75
- if (amount >= 1e-4) return `${amount.toFixed(4)} USDC`;
76
- return `${amount.toFixed(6)} USDC`;
77
- }
97
+ if (token === "USDC") return `${formatUsdcValue(amount)} USDC`;
78
98
  if (token === "SOL") return `${amount} SOL`;
79
99
  return `${amount} ${token}`;
80
100
  }
@@ -138,24 +158,6 @@ function formatTxLine(r, opts) {
138
158
  return ` ${timeStr} ${r.ok ? "" : "✗ "}${parts.join(" · ")}`;
139
159
  }
140
160
 
141
- //#endregion
142
- //#region src/lib/output.ts
143
- function isTTY() {
144
- return !!process.stderr.isTTY;
145
- }
146
- function info(msg) {
147
- process.stderr.write(`${isTTY() ? pc.cyan(msg) : msg}\n`);
148
- }
149
- function warn(msg) {
150
- process.stderr.write(`${isTTY() ? pc.yellow(msg) : msg}\n`);
151
- }
152
- function error(msg) {
153
- process.stderr.write(`${isTTY() ? pc.red(`✗ ${msg}`) : `✗ ${msg}`}\n`);
154
- }
155
- function dim(msg) {
156
- process.stderr.write(`${isTTY() ? pc.dim(msg) : msg}\n`);
157
- }
158
-
159
161
  //#endregion
160
162
  //#region src/lib/resolve-wallet.ts
161
163
  /**
@@ -273,10 +275,10 @@ async function buildX402Client(wallet, opts) {
273
275
  if (daily || perTx) client.registerPolicy((_version, reqs) => {
274
276
  if (daily) {
275
277
  const spend = calcSpend(readHistory(getHistoryPath()));
276
- if (spend.today >= daily) throw new Error(`Daily spend limit reached (${spend.today.toFixed(4)}/${daily} USDC)`);
278
+ if (spend.today >= daily) throw new Error(`Daily spend limit reached (${formatUsdcValue(spend.today)}/${daily} USDC)`);
277
279
  const remaining = daily - spend.today;
278
280
  reqs = reqs.filter((r) => Number(r.amount) / 1e6 <= remaining);
279
- if (reqs.length === 0) throw new Error(`Daily spend limit of ${daily} USDC would be exceeded (${spend.today.toFixed(4)} spent today)`);
281
+ if (reqs.length === 0) throw new Error(`Daily spend limit of ${daily} USDC would be exceeded (${formatUsdcValue(spend.today)} spent today)`);
280
282
  }
281
283
  if (perTx) {
282
284
  const before = reqs.length;
@@ -320,7 +322,7 @@ async function fetchEvmBalances(address) {
320
322
  }, "latest"])]);
321
323
  return {
322
324
  eth: ethRes.result ? (Number(BigInt(ethRes.result)) / 0xde0b6b3a7640000).toFixed(6) : "?",
323
- usdc: usdcRes.result ? (Number(BigInt(usdcRes.result)) / 1e6).toFixed(4) : "?"
325
+ usdc: usdcRes.result ? formatUsdcValue(Number(BigInt(usdcRes.result)) / 1e6) : "?"
324
326
  };
325
327
  }
326
328
  async function fetchTempoBalances(address) {
@@ -328,7 +330,7 @@ async function fetchTempoBalances(address) {
328
330
  to: USDC_TEMPO,
329
331
  data: `0x70a08231${address.slice(2).padStart(64, "0")}`
330
332
  }, "latest"]);
331
- return { usdc: res.result ? (Number(BigInt(res.result)) / 1e6).toFixed(4) : "?" };
333
+ return { usdc: res.result ? formatUsdcValue(Number(BigInt(res.result)) / 1e6) : "?" };
332
334
  }
333
335
  async function getUsdcAta(owner) {
334
336
  const encoder = getAddressEncoder();
@@ -349,7 +351,7 @@ async function fetchSolanaBalances(ownerAddress) {
349
351
  const usdcVal = usdcRes.result?.value;
350
352
  return {
351
353
  sol,
352
- usdc: usdcVal ? Number(usdcVal.uiAmountString).toFixed(4) : usdcVal === void 0 ? "?" : "0.0000"
354
+ usdc: usdcVal ? formatUsdcValue(Number(usdcVal.uiAmountString)) : usdcVal === void 0 ? "?" : "0"
353
355
  };
354
356
  }
355
357
  function balanceLine(usdc, native, nativeSymbol) {
@@ -404,9 +406,9 @@ const walletInfoCommand = buildCommand({
404
406
  const bal = sol ? balanceLine(sol.usdc, sol.sol, "SOL") : pc.dim(" (network error)");
405
407
  console.log(` Solana: ${pc.green(wallet.solanaAddress)}${bal}`);
406
408
  }
407
- const evmEmpty = !evm || evm.usdc === "0.0000";
408
- const solEmpty = !sol || sol.usdc === "0.0000";
409
- const tempoEmpty = !tempo || tempo.usdc === "0.0000";
409
+ const evmEmpty = !evm || Number(evm.usdc) === 0;
410
+ const solEmpty = !sol || Number(sol.usdc) === 0;
411
+ const tempoEmpty = !tempo || Number(tempo.usdc) === 0;
410
412
  if (evmEmpty && solEmpty && tempoEmpty) {
411
413
  console.log();
412
414
  dim(" Send USDC to any address above to start using paid APIs.");
@@ -422,7 +424,7 @@ const walletInfoCommand = buildCommand({
422
424
  console.log(line);
423
425
  }
424
426
  console.log();
425
- console.log(pc.dim(` Today: ${spend.today.toFixed(4)} USDC | Total: ${spend.total.toFixed(4)} USDC | ${spend.count} tx`));
427
+ console.log(pc.dim(` Today: ${formatAmount(spend.today, "USDC")} | Total: ${formatAmount(spend.total, "USDC")} | ${spend.count} tx`));
426
428
  } else dim(" No transactions yet.");
427
429
  console.log();
428
430
  console.log(pc.dim(" See also: wallet history, wallet export-key"));
@@ -431,4 +433,4 @@ const walletInfoCommand = buildCommand({
431
433
  });
432
434
 
433
435
  //#endregion
434
- export { formatTxLine as _, fetchTempoBalances as a, resolveWallet as c, info as d, isTTY as f, displayNetwork as g, calcSpend as h, fetchSolanaBalances as i, dim as l, appendHistory as m, fetchAllBalances as n, walletInfoCommand as o, warn as p, fetchEvmBalances as r, buildX402Client as s, balanceLine as t, error as u, readHistory as v };
436
+ export { error as _, fetchTempoBalances as a, warn as b, resolveWallet as c, displayNetwork as d, formatAmount as f, dim as g, readHistory as h, fetchSolanaBalances as i, appendHistory as l, formatUsdcValue as m, fetchAllBalances as n, walletInfoCommand as o, formatTxLine as p, fetchEvmBalances as r, buildX402Client as s, balanceLine as t, calcSpend as u, info as v, isTTY as y };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "x402-proxy",
3
- "version": "0.7.1",
3
+ "version": "0.8.1",
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,
@@ -39,7 +39,7 @@
39
39
  "@x402/evm": "^2.6.0",
40
40
  "@x402/fetch": "^2.6.0",
41
41
  "@x402/mcp": "^2.6.0",
42
- "mppx": "^0.4.7",
42
+ "mppx": "^0.4.9",
43
43
  "@x402/svm": "^2.6.0",
44
44
  "ethers": "^6.0.0",
45
45
  "picocolors": "^1.1.1",
package/skills/SKILL.md CHANGED
@@ -45,6 +45,9 @@ x402-proxy mcp <url> # MCP stdio proxy for AI agents
45
45
  x402-proxy setup # wallet onboarding wizard
46
46
  x402-proxy setup --force # re-run setup (overwrite existing wallet)
47
47
  x402-proxy status # config + wallet + daily spend summary
48
+ x402-proxy config # show current configuration
49
+ x402-proxy config set <key> <value> # set a config value
50
+ x402-proxy config unset <key> # remove a config value
48
51
  x402-proxy wallet # show addresses and USDC balances
49
52
  x402-proxy wallet history # payment log
50
53
  x402-proxy wallet history --limit 5 # last 5 payments
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env node
2
- import { n as setupCommand, t as runSetup } from "./setup-hJGkO2Lo.js";
3
-
4
- export { runSetup };