x402-proxy 0.9.4 → 0.10.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/dist/bin/cli.js CHANGED
@@ -1,16 +1,1063 @@
1
1
  #!/usr/bin/env node
2
- import { a as getHistoryPath, c as loadConfig, i as getConfigDirShort, l as loadWalletFile, s as isConfigured, u as saveConfig } from "../derive-EDXzwKW2.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, n as fetchAllBalances, o as walletInfoCommand, p as formatTxLine, s as buildX402Client, u as calcSpend, v as info, y as isTTY } from "../wallet-CqUc-ZFn.js";
4
- import { n as setupCommand, t as runSetup } from "../setup-Dp5fS7ob.js";
5
- import { n as statusCommand } from "../status-BZTToWE_.js";
2
+ import { _ as error, b as success, c as resolveWallet, d as displayNetwork, f as formatAmount, g as dim, h as readHistory, l as appendHistory, m as formatUsdcValue, n as fetchAllBalances, o as walletInfoCommand, p as formatTxLine, s as buildX402Client, u as calcSpend, v as info, x as warn, y as isTTY } from "../wallet-DqGROXlC.js";
3
+ import { a as getHistoryPath, c as loadConfig, i as getConfigDirShort, l as loadWalletFile, s as isConfigured, u as saveConfig } from "../derive-CY0En_Y3.js";
4
+ import { n as setupCommand, t as runSetup } from "../setup-DtKrojW1.js";
5
+ import { n as statusCommand } from "../status-B5oH1nHv.js";
6
6
  import { dirname, join, normalize, resolve } from "node:path";
7
7
  import { buildApplication, buildCommand, buildRouteMap, run } from "@stricli/core";
8
+ import { spawn } from "node:child_process";
8
9
  import pc from "picocolors";
10
+ import { once } from "node:events";
11
+ import http from "node:http";
12
+ import { decodePaymentResponseHeader, wrapFetchWithPayment } from "@x402/fetch";
9
13
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
10
14
  import { homedir } from "node:os";
11
- import { decodePaymentResponseHeader, wrapFetchWithPayment } from "@x402/fetch";
12
15
  import { base58 } from "@scure/base";
16
+ import "@sinclair/typebox";
13
17
  import * as prompts from "@clack/prompts";
18
+ //#region src/openclaw/defaults.ts
19
+ const DEFAULT_SURF_PROVIDER_ID = "surf";
20
+ const DEFAULT_SURF_BASE_URL = "/x402-proxy/v1";
21
+ const DEFAULT_SURF_UPSTREAM_URL = "https://surf.cascade.fyi/api/v1/inference";
22
+ const DEFAULT_SURF_MODELS = [
23
+ {
24
+ id: "anthropic/claude-opus-4.6",
25
+ name: "Claude Opus 4.6",
26
+ maxTokens: 2e5,
27
+ reasoning: true,
28
+ input: ["text", "image"],
29
+ cost: {
30
+ input: .015,
31
+ output: .075,
32
+ cacheRead: .0015,
33
+ cacheWrite: .01875
34
+ },
35
+ contextWindow: 2e5
36
+ },
37
+ {
38
+ id: "anthropic/claude-sonnet-4.6",
39
+ name: "Claude Sonnet 4.6",
40
+ maxTokens: 2e5,
41
+ reasoning: true,
42
+ input: ["text", "image"],
43
+ cost: {
44
+ input: .003,
45
+ output: .015,
46
+ cacheRead: 3e-4,
47
+ cacheWrite: .00375
48
+ },
49
+ contextWindow: 2e5
50
+ },
51
+ {
52
+ id: "x-ai/grok-4.20-beta",
53
+ name: "Grok 4.20 Beta",
54
+ maxTokens: 131072,
55
+ reasoning: true,
56
+ input: ["text"],
57
+ cost: {
58
+ input: .003,
59
+ output: .015,
60
+ cacheRead: 0,
61
+ cacheWrite: 0
62
+ },
63
+ contextWindow: 131072
64
+ },
65
+ {
66
+ id: "minimax/minimax-m2.5",
67
+ name: "MiniMax M2.5",
68
+ maxTokens: 1e6,
69
+ reasoning: false,
70
+ input: ["text"],
71
+ cost: {
72
+ input: .001,
73
+ output: .005,
74
+ cacheRead: 0,
75
+ cacheWrite: 0
76
+ },
77
+ contextWindow: 1e6
78
+ },
79
+ {
80
+ id: "moonshotai/kimi-k2.5",
81
+ name: "Kimi K2.5",
82
+ maxTokens: 131072,
83
+ reasoning: true,
84
+ input: ["text"],
85
+ cost: {
86
+ input: .002,
87
+ output: .008,
88
+ cacheRead: 0,
89
+ cacheWrite: 0
90
+ },
91
+ contextWindow: 131072
92
+ },
93
+ {
94
+ id: "z-ai/glm-5",
95
+ name: "GLM-5",
96
+ maxTokens: 128e3,
97
+ reasoning: false,
98
+ input: ["text"],
99
+ cost: {
100
+ input: .001,
101
+ output: .005,
102
+ cacheRead: 0,
103
+ cacheWrite: 0
104
+ },
105
+ contextWindow: 128e3
106
+ }
107
+ ];
108
+ function resolveProviders(config) {
109
+ const defaultProtocol = resolveProtocol(config.protocol);
110
+ const defaultMppSessionBudget = resolveMppSessionBudget(config.mppSessionBudget);
111
+ const raw = config.providers ?? {};
112
+ const entries = Object.entries(raw).length > 0 ? Object.entries(raw).map(([id, provider]) => ({
113
+ id,
114
+ baseUrl: provider.baseUrl || "/x402-proxy/v1",
115
+ upstreamUrl: provider.upstreamUrl || "https://surf.cascade.fyi/api/v1/inference",
116
+ protocol: resolveProtocol(provider.protocol, defaultProtocol),
117
+ mppSessionBudget: resolveMppSessionBudget(provider.mppSessionBudget, defaultMppSessionBudget),
118
+ models: provider.models && provider.models.length > 0 ? provider.models : DEFAULT_SURF_MODELS
119
+ })) : [{
120
+ id: DEFAULT_SURF_PROVIDER_ID,
121
+ baseUrl: DEFAULT_SURF_BASE_URL,
122
+ upstreamUrl: DEFAULT_SURF_UPSTREAM_URL,
123
+ protocol: defaultProtocol,
124
+ mppSessionBudget: defaultMppSessionBudget,
125
+ models: DEFAULT_SURF_MODELS
126
+ }];
127
+ return {
128
+ providers: entries,
129
+ models: entries.flatMap((provider) => provider.models.map((model) => ({
130
+ ...model,
131
+ provider: provider.id
132
+ })))
133
+ };
134
+ }
135
+ function resolveProtocol(value, fallback = "mpp") {
136
+ return value === "x402" || value === "mpp" || value === "auto" ? value : fallback;
137
+ }
138
+ function resolveMppSessionBudget(value, fallback = "0.5") {
139
+ return typeof value === "string" && value.trim().length > 0 ? value : fallback;
140
+ }
141
+ //#endregion
142
+ //#region src/handler.ts
143
+ /**
144
+ * Detect which payment protocols a 402 response advertises.
145
+ * - x402: PAYMENT-REQUIRED or X-PAYMENT-REQUIRED header
146
+ * - MPP: WWW-Authenticate header with Payment scheme
147
+ */
148
+ function detectProtocols(response) {
149
+ const pr = response.headers.get("PAYMENT-REQUIRED") ?? response.headers.get("X-PAYMENT-REQUIRED");
150
+ const wwwAuth = response.headers.get("WWW-Authenticate");
151
+ return {
152
+ x402: !!pr,
153
+ mpp: !!(wwwAuth && /^Payment\b/i.test(wwwAuth.trim()))
154
+ };
155
+ }
156
+ /**
157
+ * Extract the on-chain transaction signature from an x402 payment response header.
158
+ */
159
+ function extractTxSignature(response) {
160
+ const x402Header = response.headers.get("PAYMENT-RESPONSE") ?? response.headers.get("X-PAYMENT-RESPONSE");
161
+ if (x402Header) try {
162
+ return decodePaymentResponseHeader(x402Header).transaction ?? void 0;
163
+ } catch {}
164
+ const mppHeader = response.headers.get("Payment-Receipt");
165
+ if (mppHeader) try {
166
+ return JSON.parse(Buffer.from(mppHeader, "base64url").toString()).reference ?? void 0;
167
+ } catch {
168
+ return;
169
+ }
170
+ }
171
+ /**
172
+ * Create an x402 proxy handler that wraps fetch with automatic payment.
173
+ */
174
+ function createX402ProxyHandler(opts) {
175
+ const { client } = opts;
176
+ const paymentQueue = [];
177
+ client.onAfterPaymentCreation(async (hookCtx) => {
178
+ const raw = hookCtx.selectedRequirements.amount;
179
+ paymentQueue.push({
180
+ protocol: "x402",
181
+ network: hookCtx.selectedRequirements.network,
182
+ payTo: hookCtx.selectedRequirements.payTo,
183
+ amount: raw,
184
+ asset: hookCtx.selectedRequirements.asset
185
+ });
186
+ });
187
+ return {
188
+ x402Fetch: wrapFetchWithPayment(globalThis.fetch, client),
189
+ shiftPayment: () => paymentQueue.shift()
190
+ };
191
+ }
192
+ const TEMPO_NETWORK = "eip155:4217";
193
+ /**
194
+ * Create an MPP proxy handler using mppx client.
195
+ * Dynamically imports mppx/client to keep startup fast.
196
+ */
197
+ async function createMppProxyHandler(opts) {
198
+ const { Mppx, tempo } = await import("mppx/client");
199
+ const { privateKeyToAccount } = await import("viem/accounts");
200
+ const account = privateKeyToAccount(opts.evmKey);
201
+ const maxDeposit = opts.maxDeposit ?? "1";
202
+ const paymentQueue = [];
203
+ let lastChallengeAmount;
204
+ const mppx = Mppx.create({
205
+ methods: [tempo({
206
+ account,
207
+ maxDeposit
208
+ })],
209
+ polyfill: false,
210
+ onChallenge: async (challenge) => {
211
+ const req = challenge.request;
212
+ if (req.amount) lastChallengeAmount = (Number(req.amount) / 10 ** (req.decimals ?? 6)).toString();
213
+ }
214
+ });
215
+ let session;
216
+ return {
217
+ async fetch(input, init) {
218
+ const response = await mppx.fetch(typeof input === "string" ? input : input.toString(), init);
219
+ const receiptHeader = response.headers.get("Payment-Receipt");
220
+ if (receiptHeader) {
221
+ try {
222
+ const receipt = JSON.parse(Buffer.from(receiptHeader, "base64url").toString());
223
+ paymentQueue.push({
224
+ protocol: "mpp",
225
+ network: TEMPO_NETWORK,
226
+ amount: lastChallengeAmount,
227
+ receipt
228
+ });
229
+ } catch {
230
+ paymentQueue.push({
231
+ protocol: "mpp",
232
+ network: TEMPO_NETWORK,
233
+ amount: lastChallengeAmount
234
+ });
235
+ }
236
+ lastChallengeAmount = void 0;
237
+ }
238
+ return response;
239
+ },
240
+ async sse(input, init) {
241
+ session ??= tempo.session({
242
+ account,
243
+ maxDeposit
244
+ });
245
+ const url = typeof input === "string" ? input : input.toString();
246
+ const iterable = await session.sse(url, init);
247
+ paymentQueue.push({
248
+ protocol: "mpp",
249
+ network: TEMPO_NETWORK,
250
+ intent: "session"
251
+ });
252
+ return iterable;
253
+ },
254
+ shiftPayment: () => paymentQueue.shift(),
255
+ async close() {
256
+ if (session?.opened) {
257
+ const receipt = await session.close();
258
+ if (receipt) {
259
+ const spentUsdc = receipt.spent ? (Number(receipt.spent) / 1e6).toString() : void 0;
260
+ paymentQueue.push({
261
+ protocol: "mpp",
262
+ network: TEMPO_NETWORK,
263
+ intent: "session",
264
+ amount: spentUsdc,
265
+ channelId: session.channelId ?? void 0,
266
+ receipt: {
267
+ method: receipt.method,
268
+ reference: receipt.reference,
269
+ status: receipt.status,
270
+ timestamp: receipt.timestamp,
271
+ acceptedCumulative: receipt.acceptedCumulative,
272
+ txHash: receipt.txHash
273
+ }
274
+ });
275
+ }
276
+ }
277
+ }
278
+ };
279
+ }
280
+ //#endregion
281
+ //#region src/openclaw/tools.ts
282
+ const SOL_MAINNET = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
283
+ const USDC_DECIMALS = 6;
284
+ function paymentAmount(payment) {
285
+ if (!payment?.amount) return void 0;
286
+ const parsed = Number.parseFloat(payment.amount);
287
+ return Number.isNaN(parsed) ? void 0 : parsed / 10 ** USDC_DECIMALS;
288
+ }
289
+ function parseMppAmount(value) {
290
+ if (!value) return void 0;
291
+ const parsed = Number(value);
292
+ return Number.isFinite(parsed) ? parsed : void 0;
293
+ }
294
+ function addressForNetwork(evmAddress, solanaAddress, network) {
295
+ if (network?.startsWith("eip155:")) return evmAddress ?? solanaAddress;
296
+ if (network?.startsWith("solana:")) return solanaAddress ?? evmAddress;
297
+ return solanaAddress ?? evmAddress;
298
+ }
299
+ //#endregion
300
+ //#region src/openclaw/route.ts
301
+ function createInferenceProxyRouteHandler(opts) {
302
+ const { providers, getX402Proxy, getWalletAddress, getWalletAddressForNetwork, getEvmKey, historyPath, allModels, logger } = opts;
303
+ const sortedProviders = providers.slice().sort((left, right) => right.baseUrl.length - left.baseUrl.length);
304
+ return async (req, res) => {
305
+ const url = new URL(req.url ?? "/", "http://localhost");
306
+ const provider = sortedProviders.find((entry) => entry.baseUrl === "/" || url.pathname.startsWith(entry.baseUrl));
307
+ if (!provider) {
308
+ res.writeHead(404, { "Content-Type": "application/json" });
309
+ res.end(JSON.stringify({ error: {
310
+ message: "Unknown inference route",
311
+ code: "not_found"
312
+ } }));
313
+ return true;
314
+ }
315
+ const walletAddress = getWalletAddress();
316
+ if (!walletAddress) {
317
+ res.writeHead(503, { "Content-Type": "application/json" });
318
+ res.end(JSON.stringify({ error: {
319
+ message: "Wallet not loaded yet",
320
+ code: "not_ready"
321
+ } }));
322
+ return true;
323
+ }
324
+ const pathSuffix = provider.baseUrl === "/" ? url.pathname : url.pathname.slice(provider.baseUrl.length);
325
+ const upstreamUrl = `${provider.upstreamUrl.replace(/\/+$/, "")}${pathSuffix.startsWith("/") ? pathSuffix : `/${pathSuffix}`}${url.search}`;
326
+ logger.info(`proxy: intercepting ${upstreamUrl.substring(0, 80)}`);
327
+ const chunks = [];
328
+ for await (const chunk of req) chunks.push(Buffer.from(chunk));
329
+ let body = Buffer.concat(chunks).toString("utf-8");
330
+ const HOP_BY_HOP = new Set([
331
+ "authorization",
332
+ "host",
333
+ "connection",
334
+ "content-length",
335
+ "transfer-encoding",
336
+ "keep-alive",
337
+ "te",
338
+ "upgrade"
339
+ ]);
340
+ const headers = {};
341
+ for (const [key, val] of Object.entries(req.headers)) {
342
+ if (HOP_BY_HOP.has(key)) continue;
343
+ if (typeof val === "string") headers[key] = val;
344
+ }
345
+ const isChatCompletion = pathSuffix.includes("/chat/completions");
346
+ let thinkingMode;
347
+ if (isChatCompletion && body) try {
348
+ const parsed = JSON.parse(body);
349
+ if (parsed.reasoning_effort) thinkingMode = String(parsed.reasoning_effort);
350
+ if (!parsed.stream_options) {
351
+ parsed.stream_options = { include_usage: true };
352
+ body = JSON.stringify(parsed);
353
+ }
354
+ } catch {}
355
+ const method = req.method ?? "GET";
356
+ const startMs = Date.now();
357
+ try {
358
+ const requestInit = {
359
+ method,
360
+ headers,
361
+ body: ["GET", "HEAD"].includes(method) ? void 0 : body
362
+ };
363
+ const useMpp = provider.protocol === "mpp" || provider.protocol === "auto";
364
+ const wantsStreaming = isChatCompletion && /"stream"\s*:\s*true/.test(body);
365
+ if (useMpp) {
366
+ const evmKey = getEvmKey();
367
+ if (!evmKey) {
368
+ res.writeHead(503, { "Content-Type": "application/json" });
369
+ res.end(JSON.stringify({ error: {
370
+ message: "MPP inference requires an EVM wallet. Configure X402_PROXY_WALLET_MNEMONIC or X402_PROXY_WALLET_EVM_KEY.",
371
+ code: "mpp_wallet_missing"
372
+ } }));
373
+ return true;
374
+ }
375
+ return await handleMppRequest({
376
+ req,
377
+ res,
378
+ upstreamUrl,
379
+ requestInit,
380
+ walletAddress,
381
+ historyPath,
382
+ logger,
383
+ allModels,
384
+ thinkingMode,
385
+ wantsStreaming,
386
+ startMs,
387
+ evmKey,
388
+ mppSessionBudget: provider.mppSessionBudget
389
+ });
390
+ }
391
+ const proxy = getX402Proxy();
392
+ if (!proxy) {
393
+ res.writeHead(503, { "Content-Type": "application/json" });
394
+ res.end(JSON.stringify({ error: {
395
+ message: "x402 wallet not loaded yet. Configure a Solana wallet or switch the provider to mpp.",
396
+ code: "x402_wallet_missing"
397
+ } }));
398
+ return true;
399
+ }
400
+ const response = await proxy.x402Fetch(upstreamUrl, requestInit);
401
+ if (response.status === 402) {
402
+ const responseBody = await response.text();
403
+ logger.error(`x402: payment failed, raw response: ${responseBody}`);
404
+ const payment = proxy.shiftPayment();
405
+ const amount = paymentAmount(payment);
406
+ const paymentFrom = (payment?.network && getWalletAddressForNetwork?.(payment.network)) ?? walletAddress;
407
+ const paymentNetwork = payment?.network ?? "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
408
+ appendHistory(historyPath, {
409
+ t: Date.now(),
410
+ ok: false,
411
+ kind: "x402_inference",
412
+ net: paymentNetwork,
413
+ from: paymentFrom,
414
+ to: payment?.payTo,
415
+ amount,
416
+ token: amount != null ? "USDC" : void 0,
417
+ ms: Date.now() - startMs,
418
+ error: "payment_required"
419
+ });
420
+ let userMessage;
421
+ if (responseBody.includes("simulation") || responseBody.includes("Simulation")) userMessage = `Insufficient USDC or SOL in wallet ${walletAddress}. Fund it with USDC (SPL token) to pay for inference.`;
422
+ else if (responseBody.includes("insufficient") || responseBody.includes("balance")) userMessage = `Insufficient funds in wallet ${walletAddress}. Top up with USDC on Solana mainnet.`;
423
+ else userMessage = `x402 payment failed: ${responseBody.substring(0, 200) || "unknown error"}. Wallet: ${walletAddress}`;
424
+ res.writeHead(402, { "Content-Type": "application/json" });
425
+ res.end(JSON.stringify({ error: {
426
+ message: userMessage,
427
+ type: "x402_payment_error",
428
+ code: "payment_failed"
429
+ } }));
430
+ return true;
431
+ }
432
+ if (!response.ok && isChatCompletion) {
433
+ const responseBody = await response.text();
434
+ logger.error(`x402: upstream error ${response.status}: ${responseBody.substring(0, 300)}`);
435
+ const payment = proxy.shiftPayment();
436
+ const amount = paymentAmount(payment);
437
+ const paymentFrom = (payment?.network && getWalletAddressForNetwork?.(payment.network)) ?? walletAddress;
438
+ const paymentNetwork = payment?.network ?? "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
439
+ appendHistory(historyPath, {
440
+ t: Date.now(),
441
+ ok: false,
442
+ kind: "x402_inference",
443
+ net: paymentNetwork,
444
+ from: paymentFrom,
445
+ to: payment?.payTo,
446
+ amount,
447
+ token: amount != null ? "USDC" : void 0,
448
+ ms: Date.now() - startMs,
449
+ error: `upstream_${response.status}`
450
+ });
451
+ res.writeHead(502, { "Content-Type": "application/json" });
452
+ res.end(JSON.stringify({ error: {
453
+ message: `LLM provider temporarily unavailable (HTTP ${response.status}). Try again shortly.`,
454
+ type: "x402_upstream_error",
455
+ code: "upstream_failed"
456
+ } }));
457
+ return true;
458
+ }
459
+ logger.info(`x402: response ${response.status}`);
460
+ const txSig = extractTxSignature(response);
461
+ const payment = proxy.shiftPayment();
462
+ const amount = paymentAmount(payment);
463
+ const paymentFrom = (payment?.network && getWalletAddressForNetwork?.(payment.network)) ?? walletAddress;
464
+ const paymentNetwork = payment?.network ?? "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
465
+ const resHeaders = {};
466
+ for (const [key, val] of response.headers.entries()) resHeaders[key] = val;
467
+ res.writeHead(response.status, resHeaders);
468
+ if (!response.body) {
469
+ res.end();
470
+ appendHistory(historyPath, {
471
+ t: Date.now(),
472
+ ok: true,
473
+ kind: "x402_inference",
474
+ net: paymentNetwork,
475
+ from: paymentFrom,
476
+ to: payment?.payTo,
477
+ tx: txSig,
478
+ amount,
479
+ token: "USDC",
480
+ ms: Date.now() - startMs
481
+ });
482
+ return true;
483
+ }
484
+ const ct = response.headers.get("content-type") || "";
485
+ const sse = isChatCompletion && ct.includes("text/event-stream") ? createSseTracker() : null;
486
+ const reader = response.body.getReader();
487
+ const decoder = new TextDecoder();
488
+ try {
489
+ while (true) {
490
+ const { done, value } = await reader.read();
491
+ if (done) break;
492
+ res.write(value);
493
+ sse?.push(decoder.decode(value, { stream: true }));
494
+ }
495
+ } finally {
496
+ reader.releaseLock();
497
+ }
498
+ res.end();
499
+ const durationMs = Date.now() - startMs;
500
+ if (sse?.lastData) try {
501
+ const parsed = JSON.parse(sse.lastData);
502
+ const usage = parsed.usage;
503
+ const model = parsed.model ?? "";
504
+ appendHistory(historyPath, {
505
+ t: Date.now(),
506
+ ok: true,
507
+ kind: "x402_inference",
508
+ net: paymentNetwork,
509
+ from: paymentFrom,
510
+ to: payment?.payTo,
511
+ tx: txSig,
512
+ amount,
513
+ token: "USDC",
514
+ provider: allModels.find((m) => m.id === model || `${m.provider}/${m.id}` === model)?.provider,
515
+ model,
516
+ inputTokens: usage?.prompt_tokens ?? 0,
517
+ outputTokens: usage?.completion_tokens ?? 0,
518
+ reasoningTokens: usage?.completion_tokens_details?.reasoning_tokens,
519
+ cacheRead: usage?.prompt_tokens_details?.cached_tokens,
520
+ cacheWrite: usage?.prompt_tokens_details?.cache_creation_input_tokens,
521
+ thinking: thinkingMode,
522
+ ms: durationMs
523
+ });
524
+ } catch {
525
+ appendHistory(historyPath, {
526
+ t: Date.now(),
527
+ ok: true,
528
+ kind: "x402_inference",
529
+ net: paymentNetwork,
530
+ from: paymentFrom,
531
+ to: payment?.payTo,
532
+ tx: txSig,
533
+ amount,
534
+ token: "USDC",
535
+ ms: durationMs
536
+ });
537
+ }
538
+ else appendHistory(historyPath, {
539
+ t: Date.now(),
540
+ ok: true,
541
+ kind: "x402_inference",
542
+ net: paymentNetwork,
543
+ from: paymentFrom,
544
+ to: payment?.payTo,
545
+ tx: txSig,
546
+ amount,
547
+ token: "USDC",
548
+ ms: durationMs
549
+ });
550
+ return true;
551
+ } catch (err) {
552
+ const msg = String(err);
553
+ logger.error(`x402: fetch threw: ${msg}`);
554
+ getX402Proxy()?.shiftPayment();
555
+ appendHistory(historyPath, {
556
+ t: Date.now(),
557
+ ok: false,
558
+ kind: "x402_inference",
559
+ net: SOL_MAINNET,
560
+ from: walletAddress,
561
+ ms: Date.now() - startMs,
562
+ error: msg.substring(0, 200)
563
+ });
564
+ let userMessage;
565
+ if (msg.includes("Simulation failed") || msg.includes("simulation")) userMessage = `Insufficient USDC or SOL in wallet ${walletAddress}. Fund it with USDC and SOL to pay for inference.`;
566
+ else if (msg.includes("Failed to create payment")) userMessage = `x402 payment creation failed: ${msg}. Wallet: ${walletAddress}`;
567
+ else userMessage = `x402 request failed: ${msg}`;
568
+ if (!res.headersSent) {
569
+ res.writeHead(402, { "Content-Type": "application/json" });
570
+ res.end(JSON.stringify({ error: {
571
+ message: userMessage,
572
+ type: "x402_payment_error",
573
+ code: "payment_failed"
574
+ } }));
575
+ }
576
+ return true;
577
+ }
578
+ };
579
+ }
580
+ function createSseTracker() {
581
+ let residual = "";
582
+ let lastDataLine = "";
583
+ return {
584
+ push(text) {
585
+ const lines = (residual + text).split("\n");
586
+ residual = lines.pop() ?? "";
587
+ for (const line of lines) if (line.startsWith("data: ") && line !== "data: [DONE]") lastDataLine = line.slice(6);
588
+ },
589
+ get lastData() {
590
+ return lastDataLine;
591
+ }
592
+ };
593
+ }
594
+ async function closeMppSession(handler) {
595
+ try {
596
+ await handler.close();
597
+ } catch {}
598
+ }
599
+ async function handleMppRequest(opts) {
600
+ const { req, res, upstreamUrl, requestInit, walletAddress, historyPath, logger, allModels, thinkingMode, wantsStreaming, startMs, evmKey, mppSessionBudget } = opts;
601
+ const mpp = await createMppProxyHandler({
602
+ evmKey,
603
+ maxDeposit: mppSessionBudget
604
+ });
605
+ try {
606
+ if (wantsStreaming) {
607
+ res.writeHead(200, {
608
+ "Content-Type": "text/event-stream; charset=utf-8",
609
+ "Cache-Control": "no-cache, no-transform",
610
+ Connection: "keep-alive"
611
+ });
612
+ const sse = createSseTracker();
613
+ const stream = await mpp.sse(upstreamUrl, requestInit);
614
+ for await (const chunk of stream) {
615
+ const text = String(chunk);
616
+ res.write(text);
617
+ sse.push(text);
618
+ }
619
+ res.end();
620
+ mpp.shiftPayment();
621
+ const payment = mpp.shiftPayment();
622
+ appendInferenceHistory({
623
+ historyPath,
624
+ allModels,
625
+ walletAddress,
626
+ paymentNetwork: payment?.network ?? "eip155:4217",
627
+ paymentTo: void 0,
628
+ tx: payment?.receipt?.reference ?? payment?.channelId,
629
+ amount: parseMppAmount(payment?.amount),
630
+ thinkingMode,
631
+ lastDataLine: sse.lastData,
632
+ durationMs: Date.now() - startMs
633
+ });
634
+ return true;
635
+ }
636
+ const response = await mpp.fetch(upstreamUrl, requestInit);
637
+ const tx = extractTxSignature(response);
638
+ if (response.status === 402) {
639
+ const responseBody = await response.text();
640
+ logger.error(`mpp: payment failed, raw response: ${responseBody}`);
641
+ appendHistory(historyPath, {
642
+ t: Date.now(),
643
+ ok: false,
644
+ kind: "x402_inference",
645
+ net: TEMPO_NETWORK,
646
+ from: walletAddress,
647
+ ms: Date.now() - startMs,
648
+ error: "payment_required"
649
+ });
650
+ res.writeHead(402, { "Content-Type": "application/json" });
651
+ res.end(JSON.stringify({ error: {
652
+ message: `MPP payment failed: ${responseBody.substring(0, 200) || "unknown error"}. Wallet: ${walletAddress}`,
653
+ type: "mpp_payment_error",
654
+ code: "payment_failed"
655
+ } }));
656
+ return true;
657
+ }
658
+ const resHeaders = {};
659
+ for (const [key, value] of response.headers.entries()) resHeaders[key] = value;
660
+ res.writeHead(response.status, resHeaders);
661
+ const responseBody = await response.text();
662
+ res.end(responseBody);
663
+ const payment = mpp.shiftPayment();
664
+ appendInferenceHistory({
665
+ historyPath,
666
+ allModels,
667
+ walletAddress,
668
+ paymentNetwork: payment?.network ?? "eip155:4217",
669
+ paymentTo: void 0,
670
+ tx: tx ?? payment?.receipt?.reference,
671
+ amount: parseMppAmount(payment?.amount),
672
+ thinkingMode,
673
+ lastDataLine: responseBody,
674
+ durationMs: Date.now() - startMs
675
+ });
676
+ return true;
677
+ } catch (err) {
678
+ logger.error(`mpp: fetch threw: ${String(err)}`);
679
+ appendHistory(historyPath, {
680
+ t: Date.now(),
681
+ ok: false,
682
+ kind: "x402_inference",
683
+ net: TEMPO_NETWORK,
684
+ from: walletAddress,
685
+ ms: Date.now() - startMs,
686
+ error: String(err).substring(0, 200)
687
+ });
688
+ if (!res.headersSent) {
689
+ res.writeHead(402, { "Content-Type": "application/json" });
690
+ res.end(JSON.stringify({ error: {
691
+ message: `MPP request failed: ${String(err)}`,
692
+ type: "mpp_payment_error",
693
+ code: "payment_failed"
694
+ } }));
695
+ }
696
+ return true;
697
+ } finally {
698
+ await closeMppSession(mpp);
699
+ if (!res.writableEnded) res.end();
700
+ req.resume();
701
+ }
702
+ }
703
+ function appendInferenceHistory(opts) {
704
+ const { historyPath, allModels, walletAddress, paymentNetwork, paymentTo, tx, amount, thinkingMode, lastDataLine, durationMs } = opts;
705
+ try {
706
+ const parsed = JSON.parse(lastDataLine);
707
+ const usage = parsed.usage;
708
+ const model = parsed.model ?? "";
709
+ appendHistory(historyPath, {
710
+ t: Date.now(),
711
+ ok: true,
712
+ kind: "x402_inference",
713
+ net: paymentNetwork,
714
+ from: walletAddress,
715
+ to: paymentTo,
716
+ tx,
717
+ amount,
718
+ token: "USDC",
719
+ provider: allModels.find((entry) => entry.id === model || `${entry.provider}/${entry.id}` === model)?.provider,
720
+ model,
721
+ inputTokens: usage?.prompt_tokens ?? 0,
722
+ outputTokens: usage?.completion_tokens ?? 0,
723
+ reasoningTokens: usage?.completion_tokens_details?.reasoning_tokens,
724
+ cacheRead: usage?.prompt_tokens_details?.cached_tokens,
725
+ cacheWrite: usage?.prompt_tokens_details?.cache_creation_input_tokens,
726
+ thinking: thinkingMode,
727
+ ms: durationMs
728
+ });
729
+ } catch {
730
+ appendHistory(historyPath, {
731
+ t: Date.now(),
732
+ ok: true,
733
+ kind: "x402_inference",
734
+ net: paymentNetwork,
735
+ from: walletAddress,
736
+ to: paymentTo,
737
+ tx,
738
+ amount,
739
+ token: "USDC",
740
+ ms: durationMs
741
+ });
742
+ }
743
+ }
744
+ //#endregion
745
+ //#region src/commands/serve.ts
746
+ async function resolveWalletForServe(flags) {
747
+ let wallet = resolveWallet({
748
+ evmKey: flags.evmKey,
749
+ solanaKey: flags.solanaKey
750
+ });
751
+ if (wallet.source !== "none") return wallet;
752
+ const { runSetup } = await import("../setup-EX1_teNg.js");
753
+ if (isTTY()) {
754
+ dim(" No wallet found. Let's set one up first.\n");
755
+ await runSetup();
756
+ } else {
757
+ dim("No wallet found. Auto-generating...");
758
+ await runSetup({ nonInteractive: true });
759
+ }
760
+ wallet = resolveWallet({
761
+ evmKey: flags.evmKey,
762
+ solanaKey: flags.solanaKey
763
+ });
764
+ if (wallet.source === "none") {
765
+ error("Wallet setup failed. Run: $ npx x402-proxy setup");
766
+ process.exit(1);
767
+ }
768
+ return wallet;
769
+ }
770
+ async function detectPreferredNetwork(wallet) {
771
+ if (!wallet.evmAddress || !wallet.solanaAddress) return;
772
+ const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
773
+ const evmUsdc = balances.evm ? Number(balances.evm.usdc) : 0;
774
+ const solUsdc = balances.sol ? Number(balances.sol.usdc) : 0;
775
+ if (evmUsdc > solUsdc) return "base";
776
+ if (solUsdc > evmUsdc) return "solana";
777
+ }
778
+ function walletAddressForNetwork(wallet, network) {
779
+ return addressForNetwork(wallet.evmAddress ?? null, wallet.solanaAddress ?? null, network);
780
+ }
781
+ function createRequestHandler(routeHandler) {
782
+ return (req, res) => {
783
+ routeHandler(req, res).then((handled) => {
784
+ if (!handled && !res.headersSent) {
785
+ res.writeHead(404, { "Content-Type": "application/json" });
786
+ res.end(JSON.stringify({ error: {
787
+ message: "Not found",
788
+ code: "not_found"
789
+ } }));
790
+ }
791
+ }).catch((err) => {
792
+ if (!res.headersSent) res.writeHead(500, { "Content-Type": "application/json" });
793
+ res.end(JSON.stringify({ error: {
794
+ message: err instanceof Error ? err.message : String(err),
795
+ code: "proxy_failed"
796
+ } }));
797
+ });
798
+ };
799
+ }
800
+ async function startServeServer(options = {}) {
801
+ const config = loadConfig();
802
+ const wallet = await resolveWalletForServe(options);
803
+ const resolvedProtocol = resolveProtocol(options.protocol ?? config?.preferredProtocol);
804
+ const configuredMppBudget = resolveMppSessionBudget(config?.mppSessionBudget);
805
+ const preferredNetwork = config?.defaultNetwork ?? await detectPreferredNetwork(wallet);
806
+ const upstreamUrl = options.upstreamUrl ?? "https://surf.cascade.fyi/api/v1/inference";
807
+ const x402Proxy = createX402ProxyHandler({ client: await buildX402Client(wallet, {
808
+ preferredNetwork: preferredNetwork || void 0,
809
+ network: options.network,
810
+ spendLimitDaily: config?.spendLimitDaily,
811
+ spendLimitPerTx: config?.spendLimitPerTx
812
+ }) });
813
+ const { providers, models } = resolveProviders({
814
+ protocol: resolvedProtocol,
815
+ mppSessionBudget: configuredMppBudget,
816
+ providers: { [DEFAULT_SURF_PROVIDER_ID]: {
817
+ baseUrl: "/",
818
+ upstreamUrl,
819
+ protocol: resolvedProtocol,
820
+ mppSessionBudget: configuredMppBudget
821
+ } }
822
+ });
823
+ const routeHandler = createInferenceProxyRouteHandler({
824
+ providers,
825
+ getX402Proxy: () => x402Proxy,
826
+ getWalletAddress: () => wallet.solanaAddress ?? wallet.evmAddress ?? null,
827
+ getWalletAddressForNetwork: (network) => walletAddressForNetwork(wallet, network),
828
+ getEvmKey: () => wallet.evmKey ?? null,
829
+ historyPath: getHistoryPath(),
830
+ allModels: models,
831
+ logger: {
832
+ info: (msg) => {
833
+ if (!options.quiet) dim(msg);
834
+ },
835
+ error: (msg) => {
836
+ if (!options.quiet) error(msg);
837
+ }
838
+ }
839
+ });
840
+ const server = http.createServer(createRequestHandler(routeHandler));
841
+ server.on("clientError", (err, socket) => {
842
+ socket.end("HTTP/1.1 400 Bad Request\r\n\r\n");
843
+ if (!options.quiet) error(`client error: ${err.message}`);
844
+ });
845
+ server.listen(options.port ?? 0, "127.0.0.1");
846
+ await once(server, "listening");
847
+ const address = server.address();
848
+ const port = address && typeof address === "object" && typeof address.port === "number" ? address.port : 0;
849
+ if (!options.quiet) {
850
+ success(`Proxy listening on http://127.0.0.1:${port}`);
851
+ info(`Upstream: ${upstreamUrl}`);
852
+ info(`Protocol: ${resolvedProtocol}`);
853
+ }
854
+ return {
855
+ server,
856
+ port,
857
+ close: async () => {
858
+ if (!server.listening) return;
859
+ server.close();
860
+ await once(server, "close");
861
+ }
862
+ };
863
+ }
864
+ function waitForSignal() {
865
+ return new Promise((resolve) => {
866
+ const onSignal = (signal) => {
867
+ process.off("SIGINT", onSigInt);
868
+ process.off("SIGTERM", onSigTerm);
869
+ resolve(signal);
870
+ };
871
+ const onSigInt = () => onSignal("SIGINT");
872
+ const onSigTerm = () => onSignal("SIGTERM");
873
+ process.on("SIGINT", onSigInt);
874
+ process.on("SIGTERM", onSigTerm);
875
+ });
876
+ }
877
+ const serveCommand = buildCommand({
878
+ docs: {
879
+ brief: "Start a local paid inference proxy",
880
+ fullDescription: `Start a local HTTP proxy that forwards inference requests upstream and auto-pays x402 or MPP 402 challenges.
881
+
882
+ Examples:
883
+ $ x402-proxy serve
884
+ $ x402-proxy serve --port 8402
885
+ $ x402-proxy serve https://surf.cascade.fyi/api/v1/inference --protocol mpp`
886
+ },
887
+ parameters: {
888
+ flags: {
889
+ port: {
890
+ kind: "parsed",
891
+ brief: "Listen port (0 = ephemeral)",
892
+ parse: String,
893
+ default: "0"
894
+ },
895
+ protocol: {
896
+ kind: "parsed",
897
+ brief: "Payment protocol (x402, mpp, auto)",
898
+ parse: String,
899
+ optional: true
900
+ },
901
+ network: {
902
+ kind: "parsed",
903
+ brief: "Preferred or required network (base, solana, tempo)",
904
+ parse: String,
905
+ optional: true
906
+ },
907
+ evmKey: {
908
+ kind: "parsed",
909
+ brief: "EVM private key (hex)",
910
+ parse: String,
911
+ optional: true
912
+ },
913
+ solanaKey: {
914
+ kind: "parsed",
915
+ brief: "Solana private key (base58)",
916
+ parse: String,
917
+ optional: true
918
+ }
919
+ },
920
+ positional: {
921
+ kind: "tuple",
922
+ parameters: [{
923
+ brief: "Upstream inference URL",
924
+ parse: String,
925
+ optional: true
926
+ }]
927
+ }
928
+ },
929
+ async func(flags, upstreamUrl) {
930
+ const started = await startServeServer({
931
+ upstreamUrl,
932
+ port: Number(flags.port),
933
+ protocol: flags.protocol,
934
+ network: flags.network,
935
+ evmKey: flags.evmKey,
936
+ solanaKey: flags.solanaKey
937
+ });
938
+ const signal = await waitForSignal();
939
+ dim(`Received ${signal}, shutting down...`);
940
+ await started.close();
941
+ process.exit(signal === "SIGINT" ? 130 : 143);
942
+ }
943
+ });
944
+ //#endregion
945
+ //#region src/commands/claude.ts
946
+ const DEFAULT_MODEL = "minimax/minimax-m2.7";
947
+ function normalizeClaudeArgs(args) {
948
+ return args[0] === "--" ? args.slice(1) : args;
949
+ }
950
+ const claudeCommand = buildCommand({
951
+ docs: {
952
+ brief: "Run Claude Code through a paid local proxy",
953
+ fullDescription: `Start a local x402-proxy server and launch Claude Code with ANTHROPIC_BASE_URL pointed at it.
954
+
955
+ Examples:
956
+ $ x402-proxy claude
957
+ $ x402-proxy claude --model z-ai/glm-5
958
+ $ x402-proxy claude -- --print "explain this codebase"`
959
+ },
960
+ parameters: {
961
+ flags: {
962
+ model: {
963
+ kind: "parsed",
964
+ brief: "Model to use (default: minimax/minimax-m2.7)",
965
+ parse: String,
966
+ default: DEFAULT_MODEL
967
+ },
968
+ upstream: {
969
+ kind: "parsed",
970
+ brief: "Upstream inference URL",
971
+ parse: String,
972
+ optional: true
973
+ },
974
+ protocol: {
975
+ kind: "parsed",
976
+ brief: "Payment protocol (x402, mpp, auto)",
977
+ parse: String,
978
+ optional: true
979
+ },
980
+ network: {
981
+ kind: "parsed",
982
+ brief: "Preferred or required network (base, solana, tempo)",
983
+ parse: String,
984
+ optional: true
985
+ },
986
+ port: {
987
+ kind: "parsed",
988
+ brief: "Proxy listen port (0 = ephemeral)",
989
+ parse: String,
990
+ default: "0"
991
+ },
992
+ evmKey: {
993
+ kind: "parsed",
994
+ brief: "EVM private key (hex)",
995
+ parse: String,
996
+ optional: true
997
+ },
998
+ solanaKey: {
999
+ kind: "parsed",
1000
+ brief: "Solana private key (base58)",
1001
+ parse: String,
1002
+ optional: true
1003
+ }
1004
+ },
1005
+ positional: {
1006
+ kind: "array",
1007
+ parameter: {
1008
+ brief: "Arguments to forward to claude",
1009
+ parse: String
1010
+ }
1011
+ }
1012
+ },
1013
+ async func(flags, ...rawClaudeArgs) {
1014
+ const started = await startServeServer({
1015
+ upstreamUrl: flags.upstream ?? "https://surf.cascade.fyi/api/v1/inference",
1016
+ port: Number(flags.port),
1017
+ protocol: flags.protocol,
1018
+ network: flags.network,
1019
+ evmKey: flags.evmKey,
1020
+ solanaKey: flags.solanaKey,
1021
+ quiet: false
1022
+ });
1023
+ const child = spawn("claude", normalizeClaudeArgs(rawClaudeArgs), {
1024
+ stdio: "inherit",
1025
+ env: {
1026
+ ...process.env,
1027
+ ANTHROPIC_BASE_URL: `http://127.0.0.1:${started.port}`,
1028
+ ANTHROPIC_MODEL: flags.model,
1029
+ ANTHROPIC_CUSTOM_MODEL_OPTION: flags.model
1030
+ }
1031
+ });
1032
+ const stopProxy = async () => {
1033
+ await started.close().catch(() => {});
1034
+ };
1035
+ const forwardSignal = (signal) => {
1036
+ dim(`Forwarding ${signal} to claude...`);
1037
+ child.kill(signal);
1038
+ };
1039
+ const onSigInt = () => forwardSignal("SIGINT");
1040
+ const onSigTerm = () => forwardSignal("SIGTERM");
1041
+ process.on("SIGINT", onSigInt);
1042
+ process.on("SIGTERM", onSigTerm);
1043
+ child.once("error", async (err) => {
1044
+ process.off("SIGINT", onSigInt);
1045
+ process.off("SIGTERM", onSigTerm);
1046
+ await stopProxy();
1047
+ if (err.code === "ENOENT") error("Claude Code CLI not found. Install: npm i -g @anthropic-ai/claude-code");
1048
+ else error(err instanceof Error ? err.message : String(err));
1049
+ process.exit(1);
1050
+ });
1051
+ child.once("exit", async (code, signal) => {
1052
+ process.off("SIGINT", onSigInt);
1053
+ process.off("SIGTERM", onSigTerm);
1054
+ await stopProxy();
1055
+ if (signal) process.exit(signal === "SIGINT" ? 130 : 143);
1056
+ process.exit(code ?? 1);
1057
+ });
1058
+ }
1059
+ });
1060
+ //#endregion
14
1061
  //#region src/commands/config.ts
15
1062
  const VALID_KEYS = {
16
1063
  defaultNetwork: {
@@ -161,145 +1208,6 @@ const configUnsetCommand = buildCommand({
161
1208
  }
162
1209
  });
163
1210
  //#endregion
164
- //#region src/handler.ts
165
- /**
166
- * Detect which payment protocols a 402 response advertises.
167
- * - x402: PAYMENT-REQUIRED or X-PAYMENT-REQUIRED header
168
- * - MPP: WWW-Authenticate header with Payment scheme
169
- */
170
- function detectProtocols(response) {
171
- const pr = response.headers.get("PAYMENT-REQUIRED") ?? response.headers.get("X-PAYMENT-REQUIRED");
172
- const wwwAuth = response.headers.get("WWW-Authenticate");
173
- return {
174
- x402: !!pr,
175
- mpp: !!(wwwAuth && /^Payment\b/i.test(wwwAuth.trim()))
176
- };
177
- }
178
- /**
179
- * Extract the on-chain transaction signature from an x402 payment response header.
180
- */
181
- function extractTxSignature(response) {
182
- const x402Header = response.headers.get("PAYMENT-RESPONSE") ?? response.headers.get("X-PAYMENT-RESPONSE");
183
- if (x402Header) try {
184
- return decodePaymentResponseHeader(x402Header).transaction ?? void 0;
185
- } catch {}
186
- const mppHeader = response.headers.get("Payment-Receipt");
187
- if (mppHeader) try {
188
- return JSON.parse(Buffer.from(mppHeader, "base64url").toString()).reference ?? void 0;
189
- } catch {
190
- return;
191
- }
192
- }
193
- /**
194
- * Create an x402 proxy handler that wraps fetch with automatic payment.
195
- */
196
- function createX402ProxyHandler(opts) {
197
- const { client } = opts;
198
- const paymentQueue = [];
199
- client.onAfterPaymentCreation(async (hookCtx) => {
200
- const raw = hookCtx.selectedRequirements.amount;
201
- paymentQueue.push({
202
- protocol: "x402",
203
- network: hookCtx.selectedRequirements.network,
204
- payTo: hookCtx.selectedRequirements.payTo,
205
- amount: raw,
206
- asset: hookCtx.selectedRequirements.asset
207
- });
208
- });
209
- return {
210
- x402Fetch: wrapFetchWithPayment(globalThis.fetch, client),
211
- shiftPayment: () => paymentQueue.shift()
212
- };
213
- }
214
- const TEMPO_NETWORK = "eip155:4217";
215
- /**
216
- * Create an MPP proxy handler using mppx client.
217
- * Dynamically imports mppx/client to keep startup fast.
218
- */
219
- async function createMppProxyHandler(opts) {
220
- const { Mppx, tempo } = await import("mppx/client");
221
- const { privateKeyToAccount } = await import("viem/accounts");
222
- const account = privateKeyToAccount(opts.evmKey);
223
- const maxDeposit = opts.maxDeposit ?? "1";
224
- const paymentQueue = [];
225
- let lastChallengeAmount;
226
- const mppx = Mppx.create({
227
- methods: [tempo({
228
- account,
229
- maxDeposit
230
- })],
231
- polyfill: false,
232
- onChallenge: async (challenge) => {
233
- const req = challenge.request;
234
- if (req.amount) lastChallengeAmount = (Number(req.amount) / 10 ** (req.decimals ?? 6)).toString();
235
- }
236
- });
237
- let session;
238
- return {
239
- async fetch(input, init) {
240
- const response = await mppx.fetch(typeof input === "string" ? input : input.toString(), init);
241
- const receiptHeader = response.headers.get("Payment-Receipt");
242
- if (receiptHeader) {
243
- try {
244
- const receipt = JSON.parse(Buffer.from(receiptHeader, "base64url").toString());
245
- paymentQueue.push({
246
- protocol: "mpp",
247
- network: TEMPO_NETWORK,
248
- amount: lastChallengeAmount,
249
- receipt
250
- });
251
- } catch {
252
- paymentQueue.push({
253
- protocol: "mpp",
254
- network: TEMPO_NETWORK,
255
- amount: lastChallengeAmount
256
- });
257
- }
258
- lastChallengeAmount = void 0;
259
- }
260
- return response;
261
- },
262
- async sse(input, init) {
263
- session ??= tempo.session({
264
- account,
265
- maxDeposit
266
- });
267
- const url = typeof input === "string" ? input : input.toString();
268
- const iterable = await session.sse(url, init);
269
- paymentQueue.push({
270
- protocol: "mpp",
271
- network: TEMPO_NETWORK,
272
- intent: "session"
273
- });
274
- return iterable;
275
- },
276
- shiftPayment: () => paymentQueue.shift(),
277
- async close() {
278
- if (session?.opened) {
279
- const receipt = await session.close();
280
- if (receipt) {
281
- const spentUsdc = receipt.spent ? (Number(receipt.spent) / 1e6).toString() : void 0;
282
- paymentQueue.push({
283
- protocol: "mpp",
284
- network: TEMPO_NETWORK,
285
- intent: "session",
286
- amount: spentUsdc,
287
- channelId: session.channelId ?? void 0,
288
- receipt: {
289
- method: receipt.method,
290
- reference: receipt.reference,
291
- status: receipt.status,
292
- timestamp: receipt.timestamp,
293
- acceptedCumulative: receipt.acceptedCumulative,
294
- txHash: receipt.txHash
295
- }
296
- });
297
- }
298
- }
299
- }
300
- };
301
- }
302
- //#endregion
303
1211
  //#region src/commands/fetch.ts
304
1212
  function isStreamingResponse(res) {
305
1213
  return (res.headers.get("content-type") ?? "").includes("text/event-stream");
@@ -398,7 +1306,7 @@ Examples:
398
1306
  };
399
1307
  if (!url) {
400
1308
  if (isConfigured()) {
401
- const { displayStatus } = await import("../status-Bj3U-W2M.js");
1309
+ const { displayStatus } = await import("../status-DlR8yBrK.js");
402
1310
  await displayStatus();
403
1311
  console.log();
404
1312
  console.log(pc.dim(" Commands:"));
@@ -450,7 +1358,7 @@ Examples:
450
1358
  process.exit(1);
451
1359
  }
452
1360
  dim(" No wallet found. Let's set one up first.\n");
453
- const { runSetup } = await import("../setup-B6xRV8Ue.js");
1361
+ const { runSetup } = await import("../setup-EX1_teNg.js");
454
1362
  await runSetup();
455
1363
  console.log();
456
1364
  wallet = resolveWallet();
@@ -463,7 +1371,7 @@ Examples:
463
1371
  verbose(`protocol: ${resolvedProtocol ?? "auto-detect"}, maxDeposit: ${maxDeposit}`);
464
1372
  let preferredNetwork = config?.defaultNetwork;
465
1373
  if (!preferredNetwork && wallet.evmAddress && wallet.solanaAddress) {
466
- const { fetchAllBalances } = await import("../wallet-4C9EL4Iv.js");
1374
+ const { fetchAllBalances } = await import("../wallet-7XKcknNZ.js");
467
1375
  const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
468
1376
  const evmUsdc = balances.evm ? Number(balances.evm.usdc) : 0;
469
1377
  const solUsdc = balances.sol ? Number(balances.sol.usdc) : 0;
@@ -630,7 +1538,7 @@ Examples:
630
1538
  const hasSolana = accepts.some((a) => a.network.startsWith("solana:"));
631
1539
  const hasMpp = detected.mpp;
632
1540
  const hasOther = accepts.some((a) => !a.network.startsWith("eip155:") && !a.network.startsWith("solana:"));
633
- const { fetchAllBalances } = await import("../wallet-4C9EL4Iv.js");
1541
+ const { fetchAllBalances } = await import("../wallet-7XKcknNZ.js");
634
1542
  const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
635
1543
  const evmUsdc = hasEvm && balances.evm ? Number(balances.evm.usdc) : 0;
636
1544
  const solUsdc = hasSolana && balances.sol ? Number(balances.sol.usdc) : 0;
@@ -806,7 +1714,7 @@ Wallet is auto-generated on first run. No env vars needed.`
806
1714
  });
807
1715
  if (wallet.source === "none") {
808
1716
  dim("No wallet found. Auto-generating...");
809
- const { runSetup } = await import("../setup-B6xRV8Ue.js");
1717
+ const { runSetup } = await import("../setup-EX1_teNg.js");
810
1718
  await runSetup({ nonInteractive: true });
811
1719
  const fresh = resolveWallet({
812
1720
  evmKey: flags.evmKey,
@@ -850,7 +1758,7 @@ Wallet is auto-generated on first run. No env vars needed.`
850
1758
  async function startX402Proxy() {
851
1759
  let preferredNetwork = config?.defaultNetwork;
852
1760
  if (!preferredNetwork && wallet.evmAddress && wallet.solanaAddress) {
853
- const { fetchAllBalances } = await import("../wallet-4C9EL4Iv.js");
1761
+ const { fetchAllBalances } = await import("../wallet-7XKcknNZ.js");
854
1762
  const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
855
1763
  const evmUsdc = balances.evm ? Number(balances.evm.usdc) : 0;
856
1764
  const solUsdc = balances.sol ? Number(balances.sol.usdc) : 0;
@@ -870,7 +1778,7 @@ Wallet is auto-generated on first run. No env vars needed.`
870
1778
  }
871
1779
  const remoteClient = new Client({
872
1780
  name: "x402-proxy",
873
- version: "0.9.4"
1781
+ version: "0.10.1"
874
1782
  });
875
1783
  const x402Mcp = new x402MCPClient(remoteClient, x402PaymentClient, {
876
1784
  autoPayment: true,
@@ -908,7 +1816,7 @@ Wallet is auto-generated on first run. No env vars needed.`
908
1816
  }
909
1817
  const localServer = new Server({
910
1818
  name: "x402-proxy",
911
- version: "0.9.4"
1819
+ version: "0.10.1"
912
1820
  }, { capabilities: {
913
1821
  tools: tools.length > 0 ? {} : void 0,
914
1822
  resources: remoteResources.length > 0 ? {} : void 0
@@ -1003,7 +1911,7 @@ Wallet is auto-generated on first run. No env vars needed.`
1003
1911
  }));
1004
1912
  const remoteClient = new Client({
1005
1913
  name: "x402-proxy",
1006
- version: "0.9.4"
1914
+ version: "0.10.1"
1007
1915
  });
1008
1916
  await connectTransport(remoteClient);
1009
1917
  const mppClient = McpClient.wrap(remoteClient, { methods: wrappedMethods });
@@ -1018,7 +1926,7 @@ Wallet is auto-generated on first run. No env vars needed.`
1018
1926
  }
1019
1927
  const localServer = new Server({
1020
1928
  name: "x402-proxy",
1021
- version: "0.9.4"
1929
+ version: "0.10.1"
1022
1930
  }, { capabilities: {
1023
1931
  tools: tools.length > 0 ? {} : void 0,
1024
1932
  resources: remoteResources.length > 0 ? {} : void 0
@@ -1398,6 +2306,8 @@ const configRoutes = buildRouteMap({
1398
2306
  const app = buildApplication(buildRouteMap({
1399
2307
  routes: {
1400
2308
  fetch: fetchCommand,
2309
+ serve: serveCommand,
2310
+ claude: claudeCommand,
1401
2311
  mcp: buildRouteMap({
1402
2312
  routes: {
1403
2313
  proxy: mcpCommand,
@@ -1415,7 +2325,7 @@ const app = buildApplication(buildRouteMap({
1415
2325
  docs: { brief: "curl for x402 paid APIs" }
1416
2326
  }), {
1417
2327
  name: "x402-proxy",
1418
- versionInfo: { currentVersion: "0.9.4" },
2328
+ versionInfo: { currentVersion: "0.10.1" },
1419
2329
  scanner: { caseStyle: "allow-kebab-for-camel" }
1420
2330
  });
1421
2331
  //#endregion
@@ -1425,7 +2335,6 @@ function buildContext(process) {
1425
2335
  }
1426
2336
  //#endregion
1427
2337
  //#region src/bin/cli.ts
1428
- process.on("SIGINT", () => process.exit(130));
1429
2338
  const rawArgs = process.argv.slice(2);
1430
2339
  const args = [];
1431
2340
  for (let i = 0; i < rawArgs.length; i++) {
@@ -1441,6 +2350,8 @@ for (let i = 0; i < rawArgs.length; i++) {
1441
2350
  process.env.X402_PROXY_CONFIG_DIR_OVERRIDE = dir;
1442
2351
  } else args.push(a);
1443
2352
  }
2353
+ const topLevelCommand = args[0];
2354
+ if (topLevelCommand !== "serve" && topLevelCommand !== "claude") process.on("SIGINT", () => process.exit(130));
1444
2355
  await run(app, args, buildContext(process));
1445
2356
  //#endregion
1446
2357
  export {};