x402-proxy 0.2.0 → 0.2.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/README.md CHANGED
@@ -1,33 +1,47 @@
1
1
  # x402-proxy
2
2
 
3
- CLI and library for paying [x402](https://www.x402.org/) resources. Auto-pays HTTP 402 responses with USDC on Base or Solana.
3
+ `curl` for [x402](https://www.x402.org/) paid APIs. Auto-pays HTTP 402 responses with USDC on Base and Solana - zero crypto code on the buyer side.
4
4
 
5
5
  ## Quick Start
6
6
 
7
7
  ```bash
8
- npx x402-proxy setup # generate wallet from BIP-39 mnemonic
9
- npx x402-proxy wallet fund # see where to send USDC
10
- npx x402-proxy https://example.com # make a paid request
8
+ npx x402-proxy https://twitter.surf.cascade.fyi/search?q=cascade_fyi
11
9
  ```
12
10
 
13
- **Done.** Your wallet derives both EVM (Base) and Solana keypairs from a single mnemonic. Fund either chain and start paying for x402 resources.
11
+ That's it. The endpoint returns 402, x402-proxy pays and streams the response.
14
12
 
15
- ## Commands
13
+ First time? Set up a wallet:
16
14
 
17
15
  ```bash
18
- x402-proxy <url> # paid HTTP request (default command)
19
- x402-proxy mcp <url> # MCP stdio proxy for agents (alpha)
20
- x402-proxy setup # onboarding wizard
21
- x402-proxy status # config + wallet + spend summary
22
- x402-proxy wallet # show addresses
23
- x402-proxy wallet history # payment history
24
- x402-proxy wallet fund # funding instructions
25
- x402-proxy wallet export-key <chain> # bare key to stdout (evm|solana)
16
+ npx x402-proxy setup # generate wallet from BIP-39 mnemonic
17
+ npx x402-proxy wallet fund # see where to send USDC
26
18
  ```
27
19
 
28
- All commands support `--help` for details.
20
+ One mnemonic derives both EVM (Base) and Solana keypairs. Fund either chain and go.
21
+
22
+ ## MCP Proxy
23
+
24
+ Let your AI agent consume any paid MCP server. Configure in Claude, Cursor, or any MCP client:
25
+
26
+ ```json
27
+ {
28
+ "mcpServers": {
29
+ "paid-service": {
30
+ "command": "npx",
31
+ "args": ["x402-proxy", "mcp", "https://mcp.example.com/sse"],
32
+ "env": {
33
+ "X402_PROXY_WALLET_MNEMONIC": "your 24 words here"
34
+ }
35
+ }
36
+ }
37
+ }
38
+ ```
39
+
40
+ The proxy sits between your agent and the remote server, intercepting 402 responses, paying automatically, and forwarding the result. Your agent never touches crypto.
41
+
42
+ ## HTTP Requests
29
43
 
30
- ## Fetch (HTTP Client)
44
+ Works like curl. Response body streams to stdout, payment info goes to stderr.
31
45
 
32
46
  ```bash
33
47
  # GET request
@@ -38,32 +52,26 @@ x402-proxy --method POST \
38
52
  --header "Content-Type: application/json" \
39
53
  --body '{"url":"https://x402.org"}' \
40
54
  https://web.surf.cascade.fyi/v1/crawl
41
- ```
42
-
43
- Response body streams to stdout, payment info goes to stderr. Pipe-safe:
44
55
 
45
- ```bash
56
+ # Pipe-safe
46
57
  x402-proxy https://api.example.com/data | jq '.results'
47
58
  ```
48
59
 
49
- ## MCP Proxy (Alpha)
50
-
51
- Wraps a remote MCP server with automatic x402 payment. Configure in your MCP client:
60
+ ## Commands
52
61
 
53
- ```json
54
- {
55
- "mcpServers": {
56
- "paid-service": {
57
- "command": "npx",
58
- "args": ["x402-proxy", "mcp", "https://mcp.example.com/sse"],
59
- "env": {
60
- "X402_PROXY_WALLET_MNEMONIC": "your 24 words here"
61
- }
62
- }
63
- }
64
- }
62
+ ```bash
63
+ x402-proxy <url> # paid HTTP request (default command)
64
+ x402-proxy mcp <url> # MCP stdio proxy for agents
65
+ x402-proxy setup # onboarding wizard
66
+ x402-proxy status # config + wallet + spend summary
67
+ x402-proxy wallet # show addresses
68
+ x402-proxy wallet history # payment history
69
+ x402-proxy wallet fund # funding instructions
70
+ x402-proxy wallet export-key <chain> # bare key to stdout (evm|solana)
65
71
  ```
66
72
 
73
+ All commands support `--help` for details.
74
+
67
75
  ## Wallet
68
76
 
69
77
  A single BIP-39 mnemonic derives both chains:
package/dist/bin/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { S as saveWalletFile, _ as getConfigDir, a as readHistory, b as isConfigured, c as info, d as buildX402Client, f as resolveWallet, g as ensureConfigDir, h as generateMnemonic, i as formatTxLine, l as isTTY, m as deriveSolanaKeypair, n as appendHistory, o as dim, p as deriveEvmKeypair, r as calcSpend, s as error, t as statusCommand, u as warn, v as getHistoryPath, x as saveConfig, y as getWalletPath } from "../status-DeCY-cLR.js";
2
+ import { C as readHistory, S as formatTxLine, _ as isConfigured, a as deriveEvmKeypair, b as appendHistory, c as dim, d as isTTY, f as warn, g as getWalletPath, h as getHistoryPath, i as resolveWallet, l as error, m as getConfigDir, n as statusCommand, o as deriveSolanaKeypair, p as ensureConfigDir, r as buildX402Client, s as generateMnemonic, u as info, v as saveConfig, x as calcSpend, y as saveWalletFile } from "../status-J-RoszDZ.js";
3
3
  import { buildApplication, buildCommand, buildRouteMap, run } from "@stricli/core";
4
4
  import pc from "picocolors";
5
5
  import { decodePaymentResponseHeader, wrapFetchWithPayment } from "@x402/fetch";
@@ -56,8 +56,7 @@ const fetchCommand = buildCommand({
56
56
  kind: "parsed",
57
57
  brief: "HTTP method",
58
58
  parse: String,
59
- default: "GET",
60
- optional: true
59
+ default: "GET"
61
60
  },
62
61
  body: {
63
62
  kind: "parsed",
@@ -102,8 +101,8 @@ const fetchCommand = buildCommand({
102
101
  async func(flags, url) {
103
102
  if (!url) {
104
103
  if (isConfigured()) {
105
- const { statusCommand } = await import("../status-CPBQ0UBZ.js");
106
- statusCommand.func.call(this, {});
104
+ const { displayStatus } = await import("../status-DnxuVdOP.js");
105
+ displayStatus();
107
106
  } else {
108
107
  console.log();
109
108
  console.log(pc.cyan("x402-proxy") + pc.dim(" - pay for any x402 resource"));
@@ -250,7 +249,7 @@ const mcpCommand = buildCommand({
250
249
  autoPayment: true,
251
250
  onPaymentRequested: (ctx) => {
252
251
  const accept = ctx.paymentRequired.accepts?.[0];
253
- if (accept) warn(` Payment: ${accept.maxAmountRequired ?? accept.amount ?? "?"} on ${accept.network} for tool "${ctx.toolName}"`);
252
+ if (accept) warn(` Payment: ${accept.amount} on ${accept.network} for tool "${ctx.toolName}"`);
254
253
  return true;
255
254
  }
256
255
  });
@@ -365,7 +364,7 @@ const setupCommand = buildCommand({
365
364
  } else {
366
365
  const input = await prompts.text({
367
366
  message: "Enter your 24-word mnemonic:",
368
- validate: (v) => {
367
+ validate: (v = "") => {
369
368
  const words = v.trim().split(/\s+/);
370
369
  if (words.length !== 12 && words.length !== 24) return "Mnemonic must be 12 or 24 words";
371
370
  }
@@ -427,51 +426,44 @@ const walletInfoCommand = buildCommand({
427
426
  });
428
427
 
429
428
  //#endregion
430
- //#region src/commands/wallet-history.ts
431
- const walletHistoryCommand = buildCommand({
432
- docs: { brief: "Show payment history" },
429
+ //#region src/commands/wallet-export.ts
430
+ const walletExportCommand = buildCommand({
431
+ docs: { brief: "Export private key to stdout (pipe-safe)" },
433
432
  parameters: {
434
- flags: {
435
- limit: {
436
- kind: "parsed",
437
- brief: "Number of entries to show",
438
- parse: Number,
439
- default: "20"
440
- },
441
- json: {
442
- kind: "boolean",
443
- brief: "Output raw JSONL",
444
- default: false
445
- }
446
- },
433
+ flags: {},
447
434
  positional: {
448
435
  kind: "tuple",
449
- parameters: []
436
+ parameters: [{
437
+ brief: "Chain to export: evm or solana",
438
+ parse: (input) => {
439
+ const v = input.toLowerCase();
440
+ if (v !== "evm" && v !== "solana") throw new Error("Must be 'evm' or 'solana'");
441
+ return v;
442
+ }
443
+ }]
450
444
  }
451
445
  },
452
- func(flags) {
453
- const records = readHistory(getHistoryPath());
454
- if (records.length === 0) {
455
- console.log(pc.dim("No payment history yet."));
456
- return;
457
- }
458
- if (flags.json) {
459
- const slice = records.slice(-flags.limit);
460
- for (const r of slice) process.stdout.write(`${JSON.stringify(r)}\n`);
461
- return;
446
+ func(flags, chain) {
447
+ const wallet = resolveWallet();
448
+ if (wallet.source === "none") {
449
+ error("No wallet configured.");
450
+ process.exit(1);
462
451
  }
463
- const spend = calcSpend(records);
464
- const slice = records.slice(-flags.limit);
465
- console.log();
466
- info("Payment History");
467
- console.log();
468
- for (const r of slice) {
469
- const line = formatTxLine(r).replace(/\[([^\]]+)\]\([^)]+\)/g, "$1");
470
- console.log(line);
452
+ if (chain === "evm") {
453
+ if (!wallet.evmKey) {
454
+ error("No EVM key available.");
455
+ process.exit(1);
456
+ }
457
+ warn("Warning: private key will be printed to stdout.");
458
+ process.stdout.write(wallet.evmKey);
459
+ } else {
460
+ if (!wallet.solanaKey) {
461
+ error("No Solana key available.");
462
+ process.exit(1);
463
+ }
464
+ warn("Warning: private key will be printed to stdout.");
465
+ process.stdout.write(base58.encode(wallet.solanaKey.slice(0, 32)));
471
466
  }
472
- console.log();
473
- console.log(pc.dim(` Today: ${spend.today.toFixed(4)} USDC | Total: ${spend.total.toFixed(4)} USDC | ${spend.count} transactions`));
474
- console.log();
475
467
  }
476
468
  });
477
469
 
@@ -514,44 +506,51 @@ const walletFundCommand = buildCommand({
514
506
  });
515
507
 
516
508
  //#endregion
517
- //#region src/commands/wallet-export.ts
518
- const walletExportCommand = buildCommand({
519
- docs: { brief: "Export private key to stdout (pipe-safe)" },
509
+ //#region src/commands/wallet-history.ts
510
+ const walletHistoryCommand = buildCommand({
511
+ docs: { brief: "Show payment history" },
520
512
  parameters: {
521
- flags: {},
513
+ flags: {
514
+ limit: {
515
+ kind: "parsed",
516
+ brief: "Number of entries to show",
517
+ parse: Number,
518
+ default: "20"
519
+ },
520
+ json: {
521
+ kind: "boolean",
522
+ brief: "Output raw JSONL",
523
+ default: false
524
+ }
525
+ },
522
526
  positional: {
523
527
  kind: "tuple",
524
- parameters: [{
525
- brief: "Chain to export: evm or solana",
526
- parse: (input) => {
527
- const v = input.toLowerCase();
528
- if (v !== "evm" && v !== "solana") throw new Error("Must be 'evm' or 'solana'");
529
- return v;
530
- }
531
- }]
528
+ parameters: []
532
529
  }
533
530
  },
534
- func(_flags, chain) {
535
- const wallet = resolveWallet();
536
- if (wallet.source === "none") {
537
- error("No wallet configured.");
538
- process.exit(1);
531
+ func(flags) {
532
+ const records = readHistory(getHistoryPath());
533
+ if (records.length === 0) {
534
+ console.log(pc.dim("No payment history yet."));
535
+ return;
539
536
  }
540
- if (chain === "evm") {
541
- if (!wallet.evmKey) {
542
- error("No EVM key available.");
543
- process.exit(1);
544
- }
545
- warn("Warning: private key will be printed to stdout.");
546
- process.stdout.write(wallet.evmKey);
547
- } else {
548
- if (!wallet.solanaKey) {
549
- error("No Solana key available.");
550
- process.exit(1);
551
- }
552
- warn("Warning: private key will be printed to stdout.");
553
- process.stdout.write(base58.encode(wallet.solanaKey.slice(0, 32)));
537
+ if (flags.json) {
538
+ const slice = records.slice(-flags.limit);
539
+ for (const r of slice) process.stdout.write(`${JSON.stringify(r)}\n`);
540
+ return;
554
541
  }
542
+ const spend = calcSpend(records);
543
+ const slice = records.slice(-flags.limit);
544
+ console.log();
545
+ info("Payment History");
546
+ console.log();
547
+ for (const r of slice) {
548
+ const line = formatTxLine(r).replace(/\[([^\]]+)\]\([^)]+\)/g, "$1");
549
+ console.log(line);
550
+ }
551
+ console.log();
552
+ console.log(pc.dim(` Today: ${spend.today.toFixed(4)} USDC | Total: ${spend.total.toFixed(4)} USDC | ${spend.count} transactions`));
553
+ console.log();
555
554
  }
556
555
  });
557
556
 
@@ -575,11 +574,11 @@ const routes = buildRouteMap({
575
574
  status: statusCommand
576
575
  },
577
576
  defaultCommand: "fetch",
578
- docs: { brief: "x402 payment proxy - pay for any x402 resource" }
577
+ docs: { brief: "curl for x402 paid APIs" }
579
578
  });
580
579
  const app = buildApplication(routes, {
581
580
  name: "x402-proxy",
582
- versionInfo: { currentVersion: "0.2.0" },
581
+ versionInfo: { currentVersion: "0.2.1" },
583
582
  scanner: { caseStyle: "allow-kebab-for-camel" }
584
583
  });
585
584
 
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { n as statusCommand, t as displayStatus } from "./status-J-RoszDZ.js";
3
+
4
+ export { displayStatus };
@@ -1,26 +1,127 @@
1
1
  #!/usr/bin/env node
2
2
  import { buildCommand } from "@stricli/core";
3
3
  import pc from "picocolors";
4
+ import { x402Client } from "@x402/fetch";
4
5
  import fs, { appendFileSync, existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
5
6
  import os from "node:os";
6
7
  import path from "node:path";
7
8
  import { parse, stringify } from "yaml";
9
+ import { base58 } from "@scure/base";
8
10
  import { ExactEvmScheme, toClientEvmSigner } from "@x402/evm";
9
- import { x402Client } from "@x402/fetch";
10
11
  import { ExactSvmScheme } from "@x402/svm/exact/client";
11
- import { base58 } from "@scure/base";
12
12
  import { createPublicClient, http } from "viem";
13
13
  import { privateKeyToAccount } from "viem/accounts";
14
14
  import { base } from "viem/chains";
15
15
  import { ed25519 } from "@noble/curves/ed25519.js";
16
16
  import { secp256k1 } from "@noble/curves/secp256k1.js";
17
17
  import { hmac } from "@noble/hashes/hmac.js";
18
- import { keccak_256 } from "@noble/hashes/sha3.js";
19
18
  import { sha512 } from "@noble/hashes/sha2.js";
19
+ import { keccak_256 } from "@noble/hashes/sha3.js";
20
20
  import { HDKey } from "@scure/bip32";
21
21
  import { generateMnemonic, mnemonicToSeedSync } from "@scure/bip39";
22
22
  import { wordlist } from "@scure/bip39/wordlists/english.js";
23
23
 
24
+ //#region src/history.ts
25
+ const HISTORY_MAX_LINES = 1e3;
26
+ const HISTORY_KEEP_LINES = 500;
27
+ function appendHistory(historyPath, record) {
28
+ try {
29
+ appendFileSync(historyPath, `${JSON.stringify(record)}\n`);
30
+ if (existsSync(historyPath)) {
31
+ if (statSync(historyPath).size > HISTORY_MAX_LINES * 200) {
32
+ const lines = readFileSync(historyPath, "utf-8").trimEnd().split("\n");
33
+ if (lines.length > HISTORY_MAX_LINES) writeFileSync(historyPath, `${lines.slice(-HISTORY_KEEP_LINES).join("\n")}\n`);
34
+ }
35
+ }
36
+ } catch {}
37
+ }
38
+ function readHistory(historyPath) {
39
+ try {
40
+ if (!existsSync(historyPath)) return [];
41
+ const content = readFileSync(historyPath, "utf-8").trimEnd();
42
+ if (!content) return [];
43
+ return content.split("\n").flatMap((line) => {
44
+ try {
45
+ const parsed = JSON.parse(line);
46
+ if (typeof parsed.t !== "number" || typeof parsed.kind !== "string") return [];
47
+ return [parsed];
48
+ } catch {
49
+ return [];
50
+ }
51
+ });
52
+ } catch {
53
+ return [];
54
+ }
55
+ }
56
+ function calcSpend(records) {
57
+ const todayStart = /* @__PURE__ */ new Date();
58
+ todayStart.setUTCHours(0, 0, 0, 0);
59
+ const todayMs = todayStart.getTime();
60
+ let today = 0;
61
+ let total = 0;
62
+ let count = 0;
63
+ for (const r of records) {
64
+ if (!r.ok || r.amount == null) continue;
65
+ if (r.token !== "USDC") continue;
66
+ total += r.amount;
67
+ count++;
68
+ if (r.t >= todayMs) today += r.amount;
69
+ }
70
+ return {
71
+ today,
72
+ total,
73
+ count
74
+ };
75
+ }
76
+ function formatAmount(amount, token) {
77
+ if (token === "USDC") {
78
+ if (amount >= .01) return `${amount.toFixed(2)} USDC`;
79
+ if (amount >= .001) return `${amount.toFixed(3)} USDC`;
80
+ if (amount >= 1e-4) return `${amount.toFixed(4)} USDC`;
81
+ return `${amount.toFixed(6)} USDC`;
82
+ }
83
+ if (token === "SOL") return `${amount} SOL`;
84
+ return `${amount} ${token}`;
85
+ }
86
+ const KIND_LABELS = {
87
+ x402_inference: "inference",
88
+ x402_payment: "payment",
89
+ transfer: "transfer",
90
+ buy: "buy",
91
+ sell: "sell",
92
+ mint: "mint",
93
+ swap: "swap"
94
+ };
95
+ function explorerUrl(net, tx) {
96
+ if (net.startsWith("eip155:")) {
97
+ if (net.split(":")[1] === "8453") return `https://basescan.org/tx/${tx}`;
98
+ return `https://basescan.org/tx/${tx}`;
99
+ }
100
+ return `https://solscan.io/tx/${tx}`;
101
+ }
102
+ /** Strip provider prefix and OpenRouter date suffixes from model IDs.
103
+ * e.g. "minimax/minimax-m2.5-20260211" -> "minimax-m2.5"
104
+ * "moonshotai/kimi-k2.5-0127" -> "kimi-k2.5" */
105
+ function shortModel(model) {
106
+ const parts = model.split("/");
107
+ return parts[parts.length - 1].replace(/-\d{6,8}$/, "").replace(/-\d{4}$/, "");
108
+ }
109
+ function formatTxLine(r) {
110
+ const time = new Date(r.t).toLocaleTimeString("en-US", {
111
+ hour: "2-digit",
112
+ minute: "2-digit",
113
+ hour12: false,
114
+ timeZone: "UTC"
115
+ });
116
+ const timeStr = r.tx ? `[${time}](${explorerUrl(r.net, r.tx)})` : time;
117
+ const parts = [r.kind === "x402_inference" && r.model ? shortModel(r.model) : KIND_LABELS[r.kind] ?? r.kind];
118
+ if (r.label) parts.push(r.label);
119
+ if (r.ok && r.amount != null && r.token) parts.push(formatAmount(r.amount, r.token));
120
+ else if (r.ok && r.kind === "sell" && r.meta?.pct != null) parts.push(`${r.meta.pct}%`);
121
+ return ` ${timeStr} ${r.ok ? "" : "✗ "}${parts.join(" · ")}`;
122
+ }
123
+
124
+ //#endregion
24
125
  //#region src/lib/config.ts
25
126
  const APP_NAME = "x402-proxy";
26
127
  function getConfigDir() {
@@ -72,9 +173,7 @@ function loadConfig() {
72
173
  return JSON.parse(stripped);
73
174
  }
74
175
  return JSON.parse(raw);
75
- } catch {
76
- continue;
77
- }
176
+ } catch {}
78
177
  }
79
178
  return null;
80
179
  }
@@ -90,6 +189,24 @@ function isConfigured() {
90
189
  return loadWalletFile() !== null;
91
190
  }
92
191
 
192
+ //#endregion
193
+ //#region src/lib/output.ts
194
+ function isTTY() {
195
+ return !!process.stderr.isTTY;
196
+ }
197
+ function info(msg) {
198
+ process.stderr.write(`${isTTY() ? pc.cyan(msg) : msg}\n`);
199
+ }
200
+ function warn(msg) {
201
+ process.stderr.write(`${isTTY() ? pc.yellow(msg) : msg}\n`);
202
+ }
203
+ function error(msg) {
204
+ process.stderr.write(`${isTTY() ? pc.red(msg) : msg}\n`);
205
+ }
206
+ function dim(msg) {
207
+ process.stderr.write(`${isTTY() ? pc.dim(msg) : msg}\n`);
208
+ }
209
+
93
210
  //#endregion
94
211
  //#region src/lib/derive.ts
95
212
  /**
@@ -244,126 +361,36 @@ async function buildX402Client(wallet) {
244
361
  }
245
362
 
246
363
  //#endregion
247
- //#region src/lib/output.ts
248
- function isTTY() {
249
- return !!process.stderr.isTTY;
250
- }
251
- function info(msg) {
252
- process.stderr.write(`${isTTY() ? pc.cyan(msg) : msg}\n`);
253
- }
254
- function warn(msg) {
255
- process.stderr.write(`${isTTY() ? pc.yellow(msg) : msg}\n`);
256
- }
257
- function error(msg) {
258
- process.stderr.write(`${isTTY() ? pc.red(msg) : msg}\n`);
259
- }
260
- function dim(msg) {
261
- process.stderr.write(`${isTTY() ? pc.dim(msg) : msg}\n`);
262
- }
263
-
264
- //#endregion
265
- //#region src/history.ts
266
- const HISTORY_MAX_LINES = 1e3;
267
- const HISTORY_KEEP_LINES = 500;
268
- function appendHistory(historyPath, record) {
269
- try {
270
- appendFileSync(historyPath, `${JSON.stringify(record)}\n`);
271
- if (existsSync(historyPath)) {
272
- if (statSync(historyPath).size > HISTORY_MAX_LINES * 200) {
273
- const lines = readFileSync(historyPath, "utf-8").trimEnd().split("\n");
274
- if (lines.length > HISTORY_MAX_LINES) writeFileSync(historyPath, `${lines.slice(-HISTORY_KEEP_LINES).join("\n")}\n`);
275
- }
276
- }
277
- } catch {}
278
- }
279
- function readHistory(historyPath) {
280
- try {
281
- if (!existsSync(historyPath)) return [];
282
- const content = readFileSync(historyPath, "utf-8").trimEnd();
283
- if (!content) return [];
284
- return content.split("\n").flatMap((line) => {
285
- try {
286
- const parsed = JSON.parse(line);
287
- if (typeof parsed.t !== "number" || typeof parsed.kind !== "string") return [];
288
- return [parsed];
289
- } catch {
290
- return [];
291
- }
292
- });
293
- } catch {
294
- return [];
295
- }
296
- }
297
- function calcSpend(records) {
298
- const todayStart = /* @__PURE__ */ new Date();
299
- todayStart.setUTCHours(0, 0, 0, 0);
300
- const todayMs = todayStart.getTime();
301
- let today = 0;
302
- let total = 0;
303
- let count = 0;
304
- for (const r of records) {
305
- if (!r.ok || r.amount == null) continue;
306
- if (r.token !== "USDC") continue;
307
- total += r.amount;
308
- count++;
309
- if (r.t >= todayMs) today += r.amount;
364
+ //#region src/commands/status.ts
365
+ function displayStatus() {
366
+ const wallet = resolveWallet();
367
+ const config = loadConfig();
368
+ console.log();
369
+ info("x402-proxy status");
370
+ console.log();
371
+ dim(` Config directory: ${getConfigDir()}`);
372
+ if (config) {
373
+ if (config.spendLimit) dim(` Spend limit: ${config.spendLimit} USDC`);
374
+ if (config.defaultNetwork) dim(` Default network: ${config.defaultNetwork}`);
310
375
  }
311
- return {
312
- today,
313
- total,
314
- count
315
- };
316
- }
317
- function formatAmount(amount, token) {
318
- if (token === "USDC") {
319
- if (amount >= .01) return `${amount.toFixed(2)} USDC`;
320
- if (amount >= .001) return `${amount.toFixed(3)} USDC`;
321
- if (amount >= 1e-4) return `${amount.toFixed(4)} USDC`;
322
- return `${amount.toFixed(6)} USDC`;
376
+ console.log();
377
+ if (wallet.source === "none") {
378
+ console.log(pc.yellow(" No wallet configured."));
379
+ console.log(pc.dim(` Run ${pc.cyan("x402-proxy setup")} to create one.`));
380
+ } else {
381
+ dim(` Wallet source: ${wallet.source}`);
382
+ if (wallet.evmAddress) console.log(` EVM: ${pc.green(wallet.evmAddress)}`);
383
+ if (wallet.solanaAddress) console.log(` Solana: ${pc.green(wallet.solanaAddress)}`);
323
384
  }
324
- if (token === "SOL") return `${amount} SOL`;
325
- return `${amount} ${token}`;
385
+ console.log();
386
+ const spend = calcSpend(readHistory(getHistoryPath()));
387
+ if (spend.count > 0) {
388
+ dim(` Transactions: ${spend.count}`);
389
+ dim(` Today: ${spend.today.toFixed(4)} USDC`);
390
+ dim(` Total: ${spend.total.toFixed(4)} USDC`);
391
+ } else dim(" No payment history yet.");
392
+ console.log();
326
393
  }
327
- const KIND_LABELS = {
328
- x402_inference: "inference",
329
- x402_payment: "payment",
330
- transfer: "transfer",
331
- buy: "buy",
332
- sell: "sell",
333
- mint: "mint",
334
- swap: "swap"
335
- };
336
- function explorerUrl(net, tx) {
337
- if (net.startsWith("eip155:")) {
338
- if (net.split(":")[1] === "8453") return `https://basescan.org/tx/${tx}`;
339
- return `https://basescan.org/tx/${tx}`;
340
- }
341
- return `https://solscan.io/tx/${tx}`;
342
- }
343
- /** Strip provider prefix and OpenRouter date suffixes from model IDs.
344
- * e.g. "minimax/minimax-m2.5-20260211" -> "minimax-m2.5"
345
- * "moonshotai/kimi-k2.5-0127" -> "kimi-k2.5" */
346
- function shortModel(model) {
347
- const parts = model.split("/");
348
- return parts[parts.length - 1].replace(/-\d{6,8}$/, "").replace(/-\d{4}$/, "");
349
- }
350
- function formatTxLine(r) {
351
- const time = new Date(r.t).toLocaleTimeString("en-US", {
352
- hour: "2-digit",
353
- minute: "2-digit",
354
- hour12: false,
355
- timeZone: "UTC"
356
- });
357
- const timeStr = r.tx ? `[${time}](${explorerUrl(r.net, r.tx)})` : time;
358
- const parts = [r.kind === "x402_inference" && r.model ? shortModel(r.model) : KIND_LABELS[r.kind] ?? r.kind];
359
- if (r.label) parts.push(r.label);
360
- if (r.ok && r.amount != null && r.token) parts.push(formatAmount(r.amount, r.token));
361
- else if (r.ok && r.kind === "sell" && r.meta?.pct != null) parts.push(`${r.meta.pct}%`);
362
- return ` ${timeStr} ${r.ok ? "" : "✗ "}${parts.join(" · ")}`;
363
- }
364
-
365
- //#endregion
366
- //#region src/commands/status.ts
367
394
  const statusCommand = buildCommand({
368
395
  docs: { brief: "Show configuration and wallet status" },
369
396
  parameters: {
@@ -374,35 +401,9 @@ const statusCommand = buildCommand({
374
401
  }
375
402
  },
376
403
  func() {
377
- const wallet = resolveWallet();
378
- const config = loadConfig();
379
- console.log();
380
- info("x402-proxy status");
381
- console.log();
382
- dim(` Config directory: ${getConfigDir()}`);
383
- if (config) {
384
- if (config.spendLimit) dim(` Spend limit: ${config.spendLimit} USDC`);
385
- if (config.defaultNetwork) dim(` Default network: ${config.defaultNetwork}`);
386
- }
387
- console.log();
388
- if (wallet.source === "none") {
389
- console.log(pc.yellow(" No wallet configured."));
390
- console.log(pc.dim(` Run ${pc.cyan("x402-proxy setup")} to create one.`));
391
- } else {
392
- dim(` Wallet source: ${wallet.source}`);
393
- if (wallet.evmAddress) console.log(` EVM: ${pc.green(wallet.evmAddress)}`);
394
- if (wallet.solanaAddress) console.log(` Solana: ${pc.green(wallet.solanaAddress)}`);
395
- }
396
- console.log();
397
- const spend = calcSpend(readHistory(getHistoryPath()));
398
- if (spend.count > 0) {
399
- dim(` Transactions: ${spend.count}`);
400
- dim(` Today: ${spend.today.toFixed(4)} USDC`);
401
- dim(` Total: ${spend.total.toFixed(4)} USDC`);
402
- } else dim(" No payment history yet.");
403
- console.log();
404
+ displayStatus();
404
405
  }
405
406
  });
406
407
 
407
408
  //#endregion
408
- export { saveWalletFile as S, getConfigDir as _, readHistory as a, isConfigured as b, info as c, buildX402Client as d, resolveWallet as f, ensureConfigDir as g, generateMnemonic$1 as h, formatTxLine as i, isTTY as l, deriveSolanaKeypair as m, appendHistory as n, dim as o, deriveEvmKeypair as p, calcSpend as r, error as s, statusCommand as t, warn as u, getHistoryPath as v, saveConfig as x, getWalletPath as y };
409
+ export { readHistory as C, formatTxLine as S, isConfigured as _, deriveEvmKeypair as a, appendHistory as b, dim as c, isTTY as d, warn as f, getWalletPath as g, getHistoryPath as h, resolveWallet as i, error as l, getConfigDir as m, statusCommand as n, deriveSolanaKeypair as o, ensureConfigDir as p, buildX402Client as r, generateMnemonic$1 as s, displayStatus as t, info as u, saveConfig as v, calcSpend as x, saveWalletFile as y };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "x402-proxy",
3
- "version": "0.2.0",
4
- "description": "CLI and library for x402 paid HTTP requests and MCP proxy",
3
+ "version": "0.2.1",
4
+ "description": "curl for x402 paid APIs. Auto-pays any endpoint on Base and Solana.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "main": "./dist/index.js",
@@ -54,15 +54,16 @@
54
54
  ],
55
55
  "keywords": [
56
56
  "x402",
57
- "payment",
58
- "proxy",
57
+ "http-402",
59
58
  "mcp",
60
- "solana",
61
- "evm",
59
+ "mcp-proxy",
60
+ "ai-agent",
61
+ "agentic-commerce",
62
62
  "base",
63
+ "solana",
63
64
  "usdc",
64
- "ai-agent",
65
- "micropayment"
65
+ "coinbase",
66
+ "cli"
66
67
  ],
67
68
  "license": "Apache-2.0",
68
69
  "engines": {
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env node
2
- import { t as statusCommand } from "./status-DeCY-cLR.js";
3
-
4
- export { statusCommand };