x402-proxy 0.1.2 → 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,87 +1,117 @@
1
1
  # x402-proxy
2
2
 
3
- Generic [x402](https://www.x402.org/) payment proxy library for Solana and EVM USDC. Wraps `fetch` with automatic x402 payment handling, tracks transaction history, and provides wallet loading utilities.
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
- ## Install
5
+ ## Quick Start
6
6
 
7
7
  ```bash
8
- npm install x402-proxy
8
+ npx x402-proxy https://twitter.surf.cascade.fyi/search?q=cascade_fyi
9
9
  ```
10
10
 
11
- Peer dependencies: `@solana/kit`, `ethers`, `viem`
11
+ That's it. The endpoint returns 402, x402-proxy pays and streams the response.
12
12
 
13
- ## Usage
13
+ First time? Set up a wallet:
14
14
 
15
- ```ts
16
- import {
17
- x402Client,
18
- ExactSvmScheme,
19
- ExactEvmScheme,
20
- createX402ProxyHandler,
21
- extractTxSignature,
22
- loadSvmWallet,
23
- loadEvmWallet,
24
- } from "x402-proxy";
15
+ ```bash
16
+ npx x402-proxy setup # generate wallet from BIP-39 mnemonic
17
+ npx x402-proxy wallet fund # see where to send USDC
18
+ ```
19
+
20
+ One mnemonic derives both EVM (Base) and Solana keypairs. Fund either chain and go.
25
21
 
26
- // 1. Configure the x402 client with payment schemes
27
- const svmWallet = await loadSvmWallet("/path/to/keypair.json");
28
- const client = x402Client([ExactSvmScheme(svmWallet)]);
22
+ ## MCP Proxy
29
23
 
30
- // 2. Create the proxy handler
31
- const { x402Fetch, shiftPayment } = createX402ProxyHandler({ client });
24
+ Let your AI agent consume any paid MCP server. Configure in Claude, Cursor, or any MCP client:
32
25
 
33
- // 3. Make requests - payments are handled automatically
34
- const response = await x402Fetch("https://api.example.com/paid-endpoint");
35
- const payment = shiftPayment(); // { network, payTo, amount, asset }
36
- const txSig = extractTxSignature(response);
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
+ }
37
38
  ```
38
39
 
39
- ### Transaction history
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.
40
41
 
41
- ```ts
42
- import { appendHistory, readHistory, calcSpend, formatTxLine } from "x402-proxy";
43
-
44
- // Append a transaction record
45
- appendHistory("/path/to/history.jsonl", {
46
- t: Date.now(),
47
- ok: true,
48
- kind: "x402_inference",
49
- net: "solana:mainnet",
50
- from: "J5UH...",
51
- tx: "5abc...",
52
- amount: 0.05,
53
- token: "USDC",
54
- model: "claude-sonnet-4-20250514",
55
- });
56
-
57
- // Read and aggregate
58
- const records = readHistory("/path/to/history.jsonl");
59
- const { today, total, count } = calcSpend(records);
42
+ ## HTTP Requests
43
+
44
+ Works like curl. Response body streams to stdout, payment info goes to stderr.
45
+
46
+ ```bash
47
+ # GET request
48
+ x402-proxy https://twitter.surf.cascade.fyi/search?q=x402
49
+
50
+ # POST with body and headers
51
+ x402-proxy --method POST \
52
+ --header "Content-Type: application/json" \
53
+ --body '{"url":"https://x402.org"}' \
54
+ https://web.surf.cascade.fyi/v1/crawl
55
+
56
+ # Pipe-safe
57
+ x402-proxy https://api.example.com/data | jq '.results'
58
+ ```
59
+
60
+ ## Commands
61
+
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)
60
71
  ```
61
72
 
62
- ## API
73
+ All commands support `--help` for details.
74
+
75
+ ## Wallet
76
+
77
+ A single BIP-39 mnemonic derives both chains:
78
+ - **Solana:** SLIP-10 Ed25519 at `m/44'/501'/0'/0'`
79
+ - **EVM:** BIP-32 secp256k1 at `m/44'/60'/0'/0/0`
63
80
 
64
- ### Payment proxy
81
+ Config stored at `$XDG_CONFIG_HOME/x402-proxy/` (default `~/.config/x402-proxy/`).
82
+
83
+ ### Export keys for other tools
84
+
85
+ ```bash
86
+ # Pipe-safe - outputs bare key to stdout
87
+ MY_KEY=$(npx x402-proxy wallet export-key evm)
88
+ ```
65
89
 
66
- - `createX402ProxyHandler(opts)` - wraps fetch with automatic x402 payment. Returns `{ x402Fetch, shiftPayment }`
67
- - `extractTxSignature(response)` - extracts on-chain TX signature from payment response headers
90
+ ## Env Vars
68
91
 
69
- ### Wallet loading
92
+ Override wallet per-instance (useful for MCP configs):
70
93
 
71
- - `loadSvmWallet(path)` - load Solana keypair from solana-keygen JSON file
72
- - `loadEvmWallet(path)` - load EVM wallet from hex private key file
94
+ ```
95
+ X402_PROXY_WALLET_MNEMONIC # BIP-39 mnemonic (derives both chains)
96
+ X402_PROXY_WALLET_EVM_KEY # EVM private key (hex)
97
+ X402_PROXY_WALLET_SOLANA_KEY # Solana private key (base58)
98
+ ```
73
99
 
74
- ### Transaction history
100
+ Resolution order: flags > env vars > mnemonic env > `wallet.json` file.
75
101
 
76
- - `appendHistory(path, record)` - append a `TxRecord` to a JSONL file (auto-truncates at 1000 lines)
77
- - `readHistory(path)` - read all records from a JSONL history file
78
- - `calcSpend(records)` - aggregate USDC spend (today/total/count)
79
- - `explorerUrl(net, tx)` - generate block explorer URL (Solscan, Basescan, Etherscan)
80
- - `formatTxLine(record)` - format a record as a markdown line with explorer link
102
+ ## Library Usage
81
103
 
82
- ### Re-exports
104
+ ```ts
105
+ import {
106
+ createX402ProxyHandler,
107
+ extractTxSignature,
108
+ appendHistory,
109
+ readHistory,
110
+ calcSpend,
111
+ } from "x402-proxy";
112
+ ```
83
113
 
84
- - `x402Client`, `ExactSvmScheme`, `ExactEvmScheme`, `toClientEvmSigner` - from `@x402/fetch`, `@x402/svm`, `@x402/evm`
114
+ See the [library API docs](https://github.com/cascade-protocol/x402-proxy/tree/main/packages/x402-proxy#library-api) for details.
85
115
 
86
116
  ## License
87
117
 
@@ -0,0 +1,596 @@
1
+ #!/usr/bin/env node
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
+ import { buildApplication, buildCommand, buildRouteMap, run } from "@stricli/core";
4
+ import pc from "picocolors";
5
+ import { decodePaymentResponseHeader, wrapFetchWithPayment } from "@x402/fetch";
6
+ import { base58 } from "@scure/base";
7
+ import * as prompts from "@clack/prompts";
8
+
9
+ //#region src/handler.ts
10
+ /**
11
+ * Extract the on-chain transaction signature from an x402 payment response header.
12
+ */
13
+ function extractTxSignature(response) {
14
+ const header = response.headers.get("PAYMENT-RESPONSE") ?? response.headers.get("X-PAYMENT-RESPONSE");
15
+ if (!header) return void 0;
16
+ try {
17
+ return decodePaymentResponseHeader(header).transaction ?? void 0;
18
+ } catch {
19
+ return;
20
+ }
21
+ }
22
+ /**
23
+ * Create an x402 proxy handler that wraps fetch with automatic payment.
24
+ *
25
+ * Chain-agnostic: accepts a pre-configured x402Client with any registered
26
+ * schemes (SVM, EVM, etc). The handler captures payment info via the
27
+ * onAfterPaymentCreation hook. Callers use `x402Fetch` for requests that
28
+ * may require x402 payment, and `shiftPayment` to retrieve captured
29
+ * payment info after each call.
30
+ */
31
+ function createX402ProxyHandler(opts) {
32
+ const { client } = opts;
33
+ const paymentQueue = [];
34
+ client.onAfterPaymentCreation(async (hookCtx) => {
35
+ const raw = hookCtx.selectedRequirements.amount;
36
+ paymentQueue.push({
37
+ network: hookCtx.selectedRequirements.network,
38
+ payTo: hookCtx.selectedRequirements.payTo,
39
+ amount: raw?.startsWith("debug.") ? raw.slice(6) : raw,
40
+ asset: hookCtx.selectedRequirements.asset
41
+ });
42
+ });
43
+ return {
44
+ x402Fetch: wrapFetchWithPayment(globalThis.fetch, client),
45
+ shiftPayment: () => paymentQueue.shift()
46
+ };
47
+ }
48
+
49
+ //#endregion
50
+ //#region src/commands/fetch.ts
51
+ const fetchCommand = buildCommand({
52
+ docs: { brief: "Make a paid HTTP request (default command)" },
53
+ parameters: {
54
+ flags: {
55
+ method: {
56
+ kind: "parsed",
57
+ brief: "HTTP method",
58
+ parse: String,
59
+ default: "GET"
60
+ },
61
+ body: {
62
+ kind: "parsed",
63
+ brief: "Request body",
64
+ parse: String,
65
+ optional: true
66
+ },
67
+ header: {
68
+ kind: "parsed",
69
+ brief: "HTTP header (Key: Value), repeatable",
70
+ parse: String,
71
+ variadic: true,
72
+ optional: true
73
+ },
74
+ evmKey: {
75
+ kind: "parsed",
76
+ brief: "EVM private key (hex)",
77
+ parse: String,
78
+ optional: true
79
+ },
80
+ solanaKey: {
81
+ kind: "parsed",
82
+ brief: "Solana private key (base58)",
83
+ parse: String,
84
+ optional: true
85
+ },
86
+ json: {
87
+ kind: "boolean",
88
+ brief: "Force JSON output",
89
+ default: false
90
+ }
91
+ },
92
+ positional: {
93
+ kind: "tuple",
94
+ parameters: [{
95
+ brief: "URL to request",
96
+ parse: String,
97
+ optional: true
98
+ }]
99
+ }
100
+ },
101
+ async func(flags, url) {
102
+ if (!url) {
103
+ if (isConfigured()) {
104
+ const { displayStatus } = await import("../status-DnxuVdOP.js");
105
+ displayStatus();
106
+ } else {
107
+ console.log();
108
+ console.log(pc.cyan("x402-proxy") + pc.dim(" - pay for any x402 resource"));
109
+ console.log();
110
+ console.log(pc.dim(" Get started:"));
111
+ console.log(` ${pc.cyan("x402-proxy setup")} Create a wallet`);
112
+ console.log(` ${pc.cyan("x402-proxy <url>")} Make a paid request`);
113
+ console.log(` ${pc.cyan("x402-proxy mcp <url>")} MCP proxy for agents`);
114
+ console.log(` ${pc.cyan("x402-proxy wallet")} Wallet info`);
115
+ console.log(` ${pc.cyan("x402-proxy --help")} All commands`);
116
+ console.log();
117
+ }
118
+ return;
119
+ }
120
+ let parsedUrl;
121
+ try {
122
+ parsedUrl = new URL(url);
123
+ } catch {
124
+ error(`Invalid URL: ${url}`);
125
+ process.exit(1);
126
+ }
127
+ const wallet = resolveWallet({
128
+ evmKey: flags.evmKey,
129
+ solanaKey: flags.solanaKey
130
+ });
131
+ if (wallet.source === "none") {
132
+ error("No wallet configured.");
133
+ console.error(pc.dim(`Run ${pc.cyan("x402-proxy setup")} or set X402_PROXY_WALLET_MNEMONIC`));
134
+ process.exit(1);
135
+ }
136
+ const { x402Fetch, shiftPayment } = createX402ProxyHandler({ client: await buildX402Client(wallet) });
137
+ const headers = new Headers();
138
+ if (flags.header) for (const h of flags.header) {
139
+ const idx = h.indexOf(":");
140
+ if (idx > 0) headers.set(h.slice(0, idx).trim(), h.slice(idx + 1).trim());
141
+ }
142
+ const method = flags.method || "GET";
143
+ const init = {
144
+ method,
145
+ headers
146
+ };
147
+ if (flags.body) init.body = flags.body;
148
+ if (isTTY()) dim(` ${method} ${parsedUrl.toString()}`);
149
+ const startMs = Date.now();
150
+ let response;
151
+ try {
152
+ response = await x402Fetch(parsedUrl.toString(), init);
153
+ } catch (err) {
154
+ error(`Request failed: ${err instanceof Error ? err.message : String(err)}`);
155
+ process.exit(1);
156
+ }
157
+ const elapsedMs = Date.now() - startMs;
158
+ const payment = shiftPayment();
159
+ const txSig = extractTxSignature(response);
160
+ if (payment && isTTY()) {
161
+ info(` Payment: ${payment.amount ?? "?"} (${payment.network ?? "unknown"})`);
162
+ if (txSig) dim(` Tx: ${txSig}`);
163
+ }
164
+ if (isTTY()) dim(` ${response.status} ${response.statusText} (${elapsedMs}ms)`);
165
+ if (payment) {
166
+ ensureConfigDir();
167
+ const record = {
168
+ t: Date.now(),
169
+ ok: response.ok,
170
+ kind: "x402_payment",
171
+ net: payment.network ?? "unknown",
172
+ from: wallet.evmAddress ?? wallet.solanaAddress ?? "unknown",
173
+ to: payment.payTo,
174
+ tx: txSig,
175
+ amount: payment.amount ? Number(payment.amount) / 1e6 : void 0,
176
+ token: "USDC",
177
+ ms: elapsedMs,
178
+ label: parsedUrl.hostname
179
+ };
180
+ appendHistory(getHistoryPath(), record);
181
+ }
182
+ if (response.body) {
183
+ const reader = response.body.getReader();
184
+ try {
185
+ while (true) {
186
+ const { done, value } = await reader.read();
187
+ if (done) break;
188
+ process.stdout.write(value);
189
+ }
190
+ } finally {
191
+ reader.releaseLock();
192
+ }
193
+ }
194
+ if (isTTY() && response.body) process.stdout.write("\n");
195
+ }
196
+ });
197
+
198
+ //#endregion
199
+ //#region src/commands/mcp.ts
200
+ const mcpCommand = buildCommand({
201
+ docs: { brief: "Start MCP stdio proxy with x402 payment (alpha)" },
202
+ parameters: {
203
+ flags: {
204
+ evmKey: {
205
+ kind: "parsed",
206
+ brief: "EVM private key (hex)",
207
+ parse: String,
208
+ optional: true
209
+ },
210
+ solanaKey: {
211
+ kind: "parsed",
212
+ brief: "Solana private key (base58)",
213
+ parse: String,
214
+ optional: true
215
+ }
216
+ },
217
+ positional: {
218
+ kind: "tuple",
219
+ parameters: [{
220
+ brief: "Remote MCP server URL",
221
+ parse: String
222
+ }]
223
+ }
224
+ },
225
+ async func(flags, remoteUrl) {
226
+ const wallet = resolveWallet({
227
+ evmKey: flags.evmKey,
228
+ solanaKey: flags.solanaKey
229
+ });
230
+ if (wallet.source === "none") {
231
+ error("No wallet configured. Set X402_PROXY_WALLET_MNEMONIC or run x402-proxy setup.");
232
+ process.exit(1);
233
+ }
234
+ warn("Note: MCP proxy is alpha - please report issues.");
235
+ dim(`x402-proxy MCP proxy -> ${remoteUrl}`);
236
+ if (wallet.evmAddress) dim(` EVM: ${wallet.evmAddress}`);
237
+ if (wallet.solanaAddress) dim(` Solana: ${wallet.solanaAddress}`);
238
+ const x402PaymentClient = await buildX402Client(wallet);
239
+ const { Client } = await import("@modelcontextprotocol/sdk/client/index.js");
240
+ const { SSEClientTransport } = await import("@modelcontextprotocol/sdk/client/sse.js");
241
+ const { StreamableHTTPClientTransport } = await import("@modelcontextprotocol/sdk/client/streamableHttp.js");
242
+ const { McpServer } = await import("@modelcontextprotocol/sdk/server/mcp.js");
243
+ const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
244
+ const { x402MCPClient } = await import("@x402/mcp");
245
+ const x402Mcp = new x402MCPClient(new Client({
246
+ name: "x402-proxy",
247
+ version: "0.2.0"
248
+ }), x402PaymentClient, {
249
+ autoPayment: true,
250
+ onPaymentRequested: (ctx) => {
251
+ const accept = ctx.paymentRequired.accepts?.[0];
252
+ if (accept) warn(` Payment: ${accept.amount} on ${accept.network} for tool "${ctx.toolName}"`);
253
+ return true;
254
+ }
255
+ });
256
+ x402Mcp.onAfterPayment(async (ctx) => {
257
+ ensureConfigDir();
258
+ const tx = ctx.settleResponse?.transaction;
259
+ const accept = ctx.paymentPayload;
260
+ const record = {
261
+ t: Date.now(),
262
+ ok: true,
263
+ kind: "x402_payment",
264
+ net: accept.network ?? "unknown",
265
+ from: wallet.evmAddress ?? wallet.solanaAddress ?? "unknown",
266
+ tx: typeof tx === "string" ? tx : void 0,
267
+ token: "USDC",
268
+ label: `mcp:${ctx.toolName}`
269
+ };
270
+ appendHistory(getHistoryPath(), record);
271
+ });
272
+ let connected = false;
273
+ try {
274
+ const transport = new StreamableHTTPClientTransport(new URL(remoteUrl));
275
+ await x402Mcp.connect(transport);
276
+ connected = true;
277
+ dim(" Connected via StreamableHTTP");
278
+ } catch {}
279
+ if (!connected) try {
280
+ const transport = new SSEClientTransport(new URL(remoteUrl));
281
+ await x402Mcp.connect(transport);
282
+ connected = true;
283
+ dim(" Connected via SSE");
284
+ } catch (err) {
285
+ error(`Failed to connect to ${remoteUrl}: ${err instanceof Error ? err.message : String(err)}`);
286
+ process.exit(1);
287
+ }
288
+ const { tools } = await x402Mcp.listTools();
289
+ dim(` ${tools.length} tools available`);
290
+ const localServer = new McpServer({
291
+ name: "x402-proxy",
292
+ version: "0.2.0"
293
+ });
294
+ for (const tool of tools) localServer.tool(tool.name, tool.description ?? "", tool.inputSchema?.properties ? Object.fromEntries(Object.entries(tool.inputSchema.properties).map(([k, v]) => [k, v])) : {}, async (args) => {
295
+ const result = await x402Mcp.callTool(tool.name, args);
296
+ return {
297
+ content: result.content,
298
+ isError: result.isError
299
+ };
300
+ });
301
+ try {
302
+ const { resources } = await x402Mcp.listResources();
303
+ if (resources.length > 0) {
304
+ dim(` ${resources.length} resources available`);
305
+ for (const resource of resources) localServer.resource(resource.name, resource.uri, resource.description ? { description: resource.description } : {}, async (uri) => {
306
+ return { contents: (await x402Mcp.readResource({ uri: uri.href })).contents.map((c) => ({
307
+ uri: c.uri,
308
+ text: "text" in c ? c.text : ""
309
+ })) };
310
+ });
311
+ }
312
+ } catch {}
313
+ const stdioTransport = new StdioServerTransport();
314
+ await localServer.connect(stdioTransport);
315
+ dim(" MCP proxy running (stdio)");
316
+ process.stdin.on("end", async () => {
317
+ await x402Mcp.close();
318
+ process.exit(0);
319
+ });
320
+ }
321
+ });
322
+
323
+ //#endregion
324
+ //#region src/commands/setup.ts
325
+ const setupCommand = buildCommand({
326
+ docs: { brief: "Set up x402-proxy with a new wallet" },
327
+ parameters: {
328
+ flags: { force: {
329
+ kind: "boolean",
330
+ brief: "Overwrite existing configuration",
331
+ default: false
332
+ } },
333
+ positional: {
334
+ kind: "tuple",
335
+ parameters: []
336
+ }
337
+ },
338
+ async func(flags) {
339
+ if (isConfigured() && !flags.force) {
340
+ prompts.log.warn(`Already configured. Wallet at ${pc.dim(getWalletPath())}\nUse ${pc.cyan("x402-proxy setup --force")} to reconfigure.`);
341
+ return;
342
+ }
343
+ prompts.intro(pc.cyan("x402-proxy setup"));
344
+ prompts.log.info("This will generate a single BIP-39 mnemonic that derives wallets for both Solana and EVM chains.");
345
+ const action = await prompts.select({
346
+ message: "How would you like to set up your wallet?",
347
+ options: [{
348
+ value: "generate",
349
+ label: "Generate a new mnemonic"
350
+ }, {
351
+ value: "import",
352
+ label: "Import an existing mnemonic"
353
+ }]
354
+ });
355
+ if (prompts.isCancel(action)) {
356
+ prompts.cancel("Setup cancelled.");
357
+ process.exit(0);
358
+ }
359
+ let mnemonic;
360
+ if (action === "generate") {
361
+ mnemonic = generateMnemonic();
362
+ prompts.log.warn("Write down your mnemonic and store it safely. It will NOT be shown again.");
363
+ prompts.log.message(pc.bold(mnemonic));
364
+ } else {
365
+ const input = await prompts.text({
366
+ message: "Enter your 24-word mnemonic:",
367
+ validate: (v = "") => {
368
+ const words = v.trim().split(/\s+/);
369
+ if (words.length !== 12 && words.length !== 24) return "Mnemonic must be 12 or 24 words";
370
+ }
371
+ });
372
+ if (prompts.isCancel(input)) {
373
+ prompts.cancel("Setup cancelled.");
374
+ process.exit(0);
375
+ }
376
+ mnemonic = input.trim();
377
+ }
378
+ const evm = deriveEvmKeypair(mnemonic);
379
+ const sol = deriveSolanaKeypair(mnemonic);
380
+ prompts.log.success(`EVM address: ${pc.green(evm.address)}`);
381
+ prompts.log.success(`Solana address: ${pc.green(sol.address)}`);
382
+ saveWalletFile({
383
+ version: 1,
384
+ mnemonic,
385
+ addresses: {
386
+ evm: evm.address,
387
+ solana: sol.address
388
+ }
389
+ });
390
+ saveConfig({});
391
+ prompts.log.info(`Config directory: ${pc.dim(getConfigDir())}`);
392
+ prompts.log.step("Fund your wallets to start using x402 resources:");
393
+ prompts.log.message(` Solana (USDC): Send USDC to ${pc.cyan(sol.address)}`);
394
+ prompts.log.message(` EVM (USDC): Send USDC to ${pc.cyan(evm.address)} on Base`);
395
+ prompts.outro(pc.green("Setup complete!"));
396
+ }
397
+ });
398
+
399
+ //#endregion
400
+ //#region src/commands/wallet.ts
401
+ const walletInfoCommand = buildCommand({
402
+ docs: { brief: "Show wallet addresses" },
403
+ parameters: {
404
+ flags: {},
405
+ positional: {
406
+ kind: "tuple",
407
+ parameters: []
408
+ }
409
+ },
410
+ func() {
411
+ const wallet = resolveWallet();
412
+ if (wallet.source === "none") {
413
+ console.log(pc.yellow("No wallet configured."));
414
+ console.log(pc.dim(`Run ${pc.cyan("x402-proxy setup")} to create one.`));
415
+ console.log(pc.dim(`Or set ${pc.cyan("X402_PROXY_WALLET_MNEMONIC")} environment variable.`));
416
+ process.exit(1);
417
+ }
418
+ console.log();
419
+ info("Wallet");
420
+ console.log();
421
+ console.log(pc.dim(` Source: ${wallet.source}`));
422
+ if (wallet.evmAddress) console.log(` EVM: ${pc.green(wallet.evmAddress)}`);
423
+ if (wallet.solanaAddress) console.log(` Solana: ${pc.green(wallet.solanaAddress)}`);
424
+ console.log();
425
+ }
426
+ });
427
+
428
+ //#endregion
429
+ //#region src/commands/wallet-export.ts
430
+ const walletExportCommand = buildCommand({
431
+ docs: { brief: "Export private key to stdout (pipe-safe)" },
432
+ parameters: {
433
+ flags: {},
434
+ positional: {
435
+ kind: "tuple",
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
+ }]
444
+ }
445
+ },
446
+ func(flags, chain) {
447
+ const wallet = resolveWallet();
448
+ if (wallet.source === "none") {
449
+ error("No wallet configured.");
450
+ process.exit(1);
451
+ }
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)));
466
+ }
467
+ }
468
+ });
469
+
470
+ //#endregion
471
+ //#region src/commands/wallet-fund.ts
472
+ const walletFundCommand = buildCommand({
473
+ docs: { brief: "Show wallet funding instructions" },
474
+ parameters: {
475
+ flags: {},
476
+ positional: {
477
+ kind: "tuple",
478
+ parameters: []
479
+ }
480
+ },
481
+ func() {
482
+ const wallet = resolveWallet();
483
+ if (wallet.source === "none") {
484
+ console.log(pc.yellow("No wallet configured."));
485
+ console.log(pc.dim(`Run ${pc.cyan("x402-proxy setup")} to create one.`));
486
+ process.exit(1);
487
+ }
488
+ console.log();
489
+ info("Funding Instructions");
490
+ console.log();
491
+ if (wallet.solanaAddress) {
492
+ console.log(pc.bold(" Solana (USDC):"));
493
+ console.log(` Send USDC to: ${pc.green(wallet.solanaAddress)}`);
494
+ console.log(pc.dim(" Network: Solana Mainnet"));
495
+ console.log();
496
+ }
497
+ if (wallet.evmAddress) {
498
+ console.log(pc.bold(" Base (USDC):"));
499
+ console.log(` Send USDC to: ${pc.green(wallet.evmAddress)}`);
500
+ console.log(pc.dim(" Network: Base (Chain ID 8453)"));
501
+ console.log();
502
+ }
503
+ console.log(pc.dim(" Tip: Most x402 services accept USDC on Base or Solana."));
504
+ console.log();
505
+ }
506
+ });
507
+
508
+ //#endregion
509
+ //#region src/commands/wallet-history.ts
510
+ const walletHistoryCommand = buildCommand({
511
+ docs: { brief: "Show payment history" },
512
+ parameters: {
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
+ },
526
+ positional: {
527
+ kind: "tuple",
528
+ parameters: []
529
+ }
530
+ },
531
+ func(flags) {
532
+ const records = readHistory(getHistoryPath());
533
+ if (records.length === 0) {
534
+ console.log(pc.dim("No payment history yet."));
535
+ return;
536
+ }
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;
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();
554
+ }
555
+ });
556
+
557
+ //#endregion
558
+ //#region src/app.ts
559
+ const routes = buildRouteMap({
560
+ routes: {
561
+ fetch: fetchCommand,
562
+ mcp: mcpCommand,
563
+ wallet: buildRouteMap({
564
+ routes: {
565
+ info: walletInfoCommand,
566
+ history: walletHistoryCommand,
567
+ fund: walletFundCommand,
568
+ "export-key": walletExportCommand
569
+ },
570
+ defaultCommand: "info",
571
+ docs: { brief: "Wallet management" }
572
+ }),
573
+ setup: setupCommand,
574
+ status: statusCommand
575
+ },
576
+ defaultCommand: "fetch",
577
+ docs: { brief: "curl for x402 paid APIs" }
578
+ });
579
+ const app = buildApplication(routes, {
580
+ name: "x402-proxy",
581
+ versionInfo: { currentVersion: "0.2.1" },
582
+ scanner: { caseStyle: "allow-kebab-for-camel" }
583
+ });
584
+
585
+ //#endregion
586
+ //#region src/context.ts
587
+ function buildContext(process) {
588
+ return { process };
589
+ }
590
+
591
+ //#endregion
592
+ //#region src/bin/cli.ts
593
+ await run(app, process.argv.slice(2), buildContext(process));
594
+
595
+ //#endregion
596
+ export { };
package/dist/index.js CHANGED
@@ -119,10 +119,7 @@ const KIND_LABELS = {
119
119
  };
120
120
  function explorerUrl(net, tx) {
121
121
  if (net.startsWith("eip155:")) {
122
- const chainId = net.split(":")[1];
123
- if (chainId === "8453") return `https://basescan.org/tx/${tx}`;
124
- if (chainId === "84532") return `https://sepolia.basescan.org/tx/${tx}`;
125
- if (chainId === "1") return `https://etherscan.io/tx/${tx}`;
122
+ if (net.split(":")[1] === "8453") return `https://basescan.org/tx/${tx}`;
126
123
  return `https://basescan.org/tx/${tx}`;
127
124
  }
128
125
  return `https://solscan.io/tx/${tx}`;
@@ -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 };
@@ -0,0 +1,409 @@
1
+ #!/usr/bin/env node
2
+ import { buildCommand } from "@stricli/core";
3
+ import pc from "picocolors";
4
+ import { x402Client } from "@x402/fetch";
5
+ import fs, { appendFileSync, existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
6
+ import os from "node:os";
7
+ import path from "node:path";
8
+ import { parse, stringify } from "yaml";
9
+ import { base58 } from "@scure/base";
10
+ import { ExactEvmScheme, toClientEvmSigner } from "@x402/evm";
11
+ import { ExactSvmScheme } from "@x402/svm/exact/client";
12
+ import { createPublicClient, http } from "viem";
13
+ import { privateKeyToAccount } from "viem/accounts";
14
+ import { base } from "viem/chains";
15
+ import { ed25519 } from "@noble/curves/ed25519.js";
16
+ import { secp256k1 } from "@noble/curves/secp256k1.js";
17
+ import { hmac } from "@noble/hashes/hmac.js";
18
+ import { sha512 } from "@noble/hashes/sha2.js";
19
+ import { keccak_256 } from "@noble/hashes/sha3.js";
20
+ import { HDKey } from "@scure/bip32";
21
+ import { generateMnemonic, mnemonicToSeedSync } from "@scure/bip39";
22
+ import { wordlist } from "@scure/bip39/wordlists/english.js";
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
125
+ //#region src/lib/config.ts
126
+ const APP_NAME = "x402-proxy";
127
+ function getConfigDir() {
128
+ const xdg = process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config");
129
+ return path.join(xdg, APP_NAME);
130
+ }
131
+ function getWalletPath() {
132
+ return path.join(getConfigDir(), "wallet.json");
133
+ }
134
+ function getHistoryPath() {
135
+ return path.join(getConfigDir(), "history.jsonl");
136
+ }
137
+ function ensureConfigDir() {
138
+ fs.mkdirSync(getConfigDir(), { recursive: true });
139
+ }
140
+ function loadWalletFile() {
141
+ const p = getWalletPath();
142
+ if (!fs.existsSync(p)) return null;
143
+ try {
144
+ const data = JSON.parse(fs.readFileSync(p, "utf-8"));
145
+ if (data.version === 1 && typeof data.mnemonic === "string") return data;
146
+ return null;
147
+ } catch {
148
+ return null;
149
+ }
150
+ }
151
+ function saveWalletFile(wallet) {
152
+ ensureConfigDir();
153
+ fs.writeFileSync(getWalletPath(), JSON.stringify(wallet, null, 2), {
154
+ mode: 384,
155
+ encoding: "utf-8"
156
+ });
157
+ }
158
+ function loadConfig() {
159
+ const dir = getConfigDir();
160
+ for (const name of [
161
+ "config.yaml",
162
+ "config.yml",
163
+ "config.jsonc",
164
+ "config.json"
165
+ ]) {
166
+ const p = path.join(dir, name);
167
+ if (!fs.existsSync(p)) continue;
168
+ try {
169
+ const raw = fs.readFileSync(p, "utf-8");
170
+ if (name.endsWith(".yaml") || name.endsWith(".yml")) return parse(raw);
171
+ if (name.endsWith(".jsonc")) {
172
+ const stripped = raw.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
173
+ return JSON.parse(stripped);
174
+ }
175
+ return JSON.parse(raw);
176
+ } catch {}
177
+ }
178
+ return null;
179
+ }
180
+ function saveConfig(config) {
181
+ ensureConfigDir();
182
+ const p = path.join(getConfigDir(), "config.yaml");
183
+ fs.writeFileSync(p, stringify(config), "utf-8");
184
+ }
185
+ function isConfigured() {
186
+ if (process.env.X402_PROXY_WALLET_MNEMONIC) return true;
187
+ if (process.env.X402_PROXY_WALLET_EVM_KEY) return true;
188
+ if (process.env.X402_PROXY_WALLET_SOLANA_KEY) return true;
189
+ return loadWalletFile() !== null;
190
+ }
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
+
210
+ //#endregion
211
+ //#region src/lib/derive.ts
212
+ /**
213
+ * Wallet derivation from BIP-39 mnemonic.
214
+ *
215
+ * A single 24-word mnemonic is the root secret. Both Solana and EVM keypairs
216
+ * are deterministically derived from it.
217
+ *
218
+ * Solana: SLIP-10 Ed25519 at m/44'/501'/0'/0'
219
+ * EVM: BIP-32 secp256k1 at m/44'/60'/0'/0/0
220
+ *
221
+ * Ported from agentbox/packages/openclaw-x402/src/wallet.ts
222
+ */
223
+ function generateMnemonic$1() {
224
+ return generateMnemonic(wordlist, 256);
225
+ }
226
+ /**
227
+ * SLIP-10 Ed25519 derivation at m/44'/501'/0'/0' (Phantom/Backpack compatible).
228
+ */
229
+ const enc = new TextEncoder();
230
+ function deriveSolanaKeypair(mnemonic) {
231
+ const seed = mnemonicToSeedSync(mnemonic);
232
+ let I = hmac(sha512, enc.encode("ed25519 seed"), seed);
233
+ let key = I.slice(0, 32);
234
+ let chainCode = I.slice(32);
235
+ for (const index of [
236
+ 2147483692,
237
+ 2147484149,
238
+ 2147483648,
239
+ 2147483648
240
+ ]) {
241
+ const data = new Uint8Array(37);
242
+ data[0] = 0;
243
+ data.set(key, 1);
244
+ data[33] = index >>> 24 & 255;
245
+ data[34] = index >>> 16 & 255;
246
+ data[35] = index >>> 8 & 255;
247
+ data[36] = index & 255;
248
+ I = hmac(sha512, chainCode, data);
249
+ key = I.slice(0, 32);
250
+ chainCode = I.slice(32);
251
+ }
252
+ const secretKey = new Uint8Array(key);
253
+ const publicKey = ed25519.getPublicKey(secretKey);
254
+ return {
255
+ secretKey,
256
+ publicKey,
257
+ address: base58.encode(publicKey)
258
+ };
259
+ }
260
+ /**
261
+ * BIP-32 secp256k1 derivation at m/44'/60'/0'/0/0.
262
+ */
263
+ function deriveEvmKeypair(mnemonic) {
264
+ const seed = mnemonicToSeedSync(mnemonic);
265
+ const derived = HDKey.fromMasterSeed(seed).derive("m/44'/60'/0'/0/0");
266
+ if (!derived.privateKey) throw new Error("Failed to derive EVM private key");
267
+ const privateKey = `0x${Buffer.from(derived.privateKey).toString("hex")}`;
268
+ const hash = keccak_256(secp256k1.getPublicKey(derived.privateKey, false).slice(1));
269
+ return {
270
+ privateKey,
271
+ address: checksumAddress(Buffer.from(hash.slice(-20)).toString("hex"))
272
+ };
273
+ }
274
+ function checksumAddress(addr) {
275
+ const hash = Buffer.from(keccak_256(enc.encode(addr))).toString("hex");
276
+ let out = "0x";
277
+ for (let i = 0; i < 40; i++) out += Number.parseInt(hash[i], 16) >= 8 ? addr[i].toUpperCase() : addr[i];
278
+ return out;
279
+ }
280
+
281
+ //#endregion
282
+ //#region src/lib/resolve-wallet.ts
283
+ /**
284
+ * Resolve wallet keys following the priority cascade:
285
+ * 1. Flags (--evm-key / --solana-key as raw key strings)
286
+ * 2. X402_PROXY_WALLET_EVM_KEY / X402_PROXY_WALLET_SOLANA_KEY env vars
287
+ * 3. X402_PROXY_WALLET_MNEMONIC env var (derives both)
288
+ * 4. ~/.config/x402-proxy/wallet.json (mnemonic file)
289
+ */
290
+ function resolveWallet(opts) {
291
+ if (opts?.evmKey || opts?.solanaKey) {
292
+ const result = { source: "flag" };
293
+ if (opts.evmKey) {
294
+ const hex = opts.evmKey.startsWith("0x") ? opts.evmKey : `0x${opts.evmKey}`;
295
+ result.evmKey = hex;
296
+ result.evmAddress = privateKeyToAccount(hex).address;
297
+ }
298
+ if (opts.solanaKey) result.solanaKey = parsesolanaKey(opts.solanaKey);
299
+ return result;
300
+ }
301
+ const envEvm = process.env.X402_PROXY_WALLET_EVM_KEY;
302
+ const envSol = process.env.X402_PROXY_WALLET_SOLANA_KEY;
303
+ if (envEvm || envSol) {
304
+ const result = { source: "env" };
305
+ if (envEvm) {
306
+ const hex = envEvm.startsWith("0x") ? envEvm : `0x${envEvm}`;
307
+ result.evmKey = hex;
308
+ result.evmAddress = privateKeyToAccount(hex).address;
309
+ }
310
+ if (envSol) result.solanaKey = parsesolanaKey(envSol);
311
+ return result;
312
+ }
313
+ const envMnemonic = process.env.X402_PROXY_WALLET_MNEMONIC;
314
+ if (envMnemonic) return resolveFromMnemonic(envMnemonic, "mnemonic-env");
315
+ const walletFile = loadWalletFile();
316
+ if (walletFile) return resolveFromMnemonic(walletFile.mnemonic, "wallet-file");
317
+ return { source: "none" };
318
+ }
319
+ function resolveFromMnemonic(mnemonic, source) {
320
+ const evm = deriveEvmKeypair(mnemonic);
321
+ const sol = deriveSolanaKeypair(mnemonic);
322
+ const solanaKey = new Uint8Array(64);
323
+ solanaKey.set(sol.secretKey, 0);
324
+ solanaKey.set(sol.publicKey, 32);
325
+ return {
326
+ evmKey: evm.privateKey,
327
+ evmAddress: evm.address,
328
+ solanaKey,
329
+ solanaAddress: sol.address,
330
+ source
331
+ };
332
+ }
333
+ function parsesolanaKey(input) {
334
+ const trimmed = input.trim();
335
+ if (trimmed.startsWith("[")) {
336
+ const arr = JSON.parse(trimmed);
337
+ return new Uint8Array(arr);
338
+ }
339
+ return base58.decode(trimmed);
340
+ }
341
+ /**
342
+ * Build a configured x402Client from resolved wallet keys.
343
+ */
344
+ async function buildX402Client(wallet) {
345
+ const client = new x402Client();
346
+ if (wallet.evmKey) {
347
+ const hex = wallet.evmKey;
348
+ const signer = toClientEvmSigner(privateKeyToAccount(hex), createPublicClient({
349
+ chain: base,
350
+ transport: http()
351
+ }));
352
+ client.register("eip155:8453", new ExactEvmScheme(signer));
353
+ }
354
+ if (wallet.solanaKey) {
355
+ const { createKeyPairSignerFromBytes } = await import("@solana/kit");
356
+ const signer = await createKeyPairSignerFromBytes(wallet.solanaKey);
357
+ client.register("solana:mainnet", new ExactSvmScheme(signer));
358
+ client.register("solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp", new ExactSvmScheme(signer));
359
+ }
360
+ return client;
361
+ }
362
+
363
+ //#endregion
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}`);
375
+ }
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)}`);
384
+ }
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();
393
+ }
394
+ const statusCommand = buildCommand({
395
+ docs: { brief: "Show configuration and wallet status" },
396
+ parameters: {
397
+ flags: {},
398
+ positional: {
399
+ kind: "tuple",
400
+ parameters: []
401
+ }
402
+ },
403
+ func() {
404
+ displayStatus();
405
+ }
406
+ });
407
+
408
+ //#endregion
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,18 +1,38 @@
1
1
  {
2
2
  "name": "x402-proxy",
3
- "version": "0.1.2",
4
- "description": "Generic x402 payment proxy library for Solana USDC",
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",
8
+ "types": "./dist/index.d.ts",
8
9
  "exports": {
9
- ".": "./dist/index.js",
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ }
15
+ },
10
16
  "./package.json": "./package.json"
11
17
  },
18
+ "bin": {
19
+ "x402-proxy": "./dist/bin/cli.js"
20
+ },
12
21
  "dependencies": {
22
+ "@clack/prompts": "^1.1.0",
23
+ "@modelcontextprotocol/sdk": "^1.27.1",
24
+ "@noble/curves": "^2.0.1",
25
+ "@noble/hashes": "^2.0.1",
26
+ "@scure/base": "^2.0.0",
27
+ "@scure/bip32": "^2.0.1",
28
+ "@scure/bip39": "^2.0.1",
29
+ "@stricli/core": "^1.2.6",
13
30
  "@x402/evm": "^2.6.0",
14
31
  "@x402/fetch": "^2.6.0",
15
- "@x402/svm": "^2.6.0"
32
+ "@x402/mcp": "^2.6.0",
33
+ "@x402/svm": "^2.6.0",
34
+ "picocolors": "^1.1.1",
35
+ "yaml": "^2.8.2"
16
36
  },
17
37
  "peerDependencies": {
18
38
  "@solana/kit": "^6.0.0",
@@ -32,6 +52,19 @@
32
52
  "README.md",
33
53
  "LICENSE"
34
54
  ],
55
+ "keywords": [
56
+ "x402",
57
+ "http-402",
58
+ "mcp",
59
+ "mcp-proxy",
60
+ "ai-agent",
61
+ "agentic-commerce",
62
+ "base",
63
+ "solana",
64
+ "usdc",
65
+ "coinbase",
66
+ "cli"
67
+ ],
35
68
  "license": "Apache-2.0",
36
69
  "engines": {
37
70
  "node": ">=22.0.0"