x402-proxy 0.10.7 → 0.10.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -1
- package/README.md +7 -1
- package/dist/Credential-COZQnr1-.js +2055 -0
- package/dist/Mcp-CrCEqLqO.js +10 -0
- package/dist/Sse-ChldYgU7.js +9742 -0
- package/dist/Sse-kCB38G56.js +16482 -0
- package/dist/accounts-DsuvWwph.js +232 -0
- package/dist/accounts-DzvAlQRn.js +5 -0
- package/dist/accounts-IG-Cmrwy.js +229 -0
- package/dist/api-CUzmQvTQ.js +2802 -0
- package/dist/auth-DTzQmnZ_.js +1196 -0
- package/dist/bin/cli.js +585 -242
- package/dist/ccip-Bx-zoUCJ.js +240 -0
- package/dist/ccip-C2k1DD1T.js +153 -0
- package/dist/ccip-C6CQOJYv.js +152 -0
- package/dist/ccip-RZzsZ5Mv.js +156 -0
- package/dist/chain-CafcHffR.js +1997 -0
- package/dist/chain-DwfP5RGZ.js +1968 -0
- package/dist/chunk-DBEY4PJZ.js +16 -0
- package/dist/chunk-DjEMn6fM.js +36 -0
- package/dist/client-Blw2V7LF.js +657 -0
- package/dist/client-C37gWJOZ.js +102 -0
- package/dist/client-CEc4NYAA.js +6388 -0
- package/dist/client-CVDTUY0l.js +5152 -0
- package/dist/config-BUQsit4s.js +3 -0
- package/dist/config-DR1Fs_wL.js +6600 -0
- package/dist/{config-D9wIR3xc.js → config-rvKA3SYT.js} +10 -5
- package/dist/decodeFunctionData-DuFcwhC_.js +4510 -0
- package/dist/decodeFunctionData-JPOUdvil.js +4394 -0
- package/dist/derive-DNUl8LU9.js +9109 -0
- package/dist/dist-C2YO6HSQ.js +6581 -0
- package/dist/dist-DM5_F3r5.js +4 -0
- package/dist/dist-DxJCYyL5.js +1388 -0
- package/dist/hashTypedData-BHmP9dBd.js +859 -0
- package/dist/hashTypedData-CtEdfx4y.js +846 -0
- package/dist/helpers-CuUSw-tH.js +7125 -0
- package/dist/hmac-59IlS_by.js +648 -0
- package/dist/http-BAtucMbS.js +2060 -0
- package/dist/index.d.ts +1903 -9
- package/dist/index.js +18006 -50
- package/dist/index.node-CxkL0OFh.js +3592 -0
- package/dist/index.node-DvmeuZBj.js +3 -0
- package/dist/isAddressEqual-BLrd1Hg1.js +9 -0
- package/dist/isAddressEqual-DsAqfQOD.js +10 -0
- package/dist/localBatchGatewayRequest-C-RPJyDO.js +6260 -0
- package/dist/localBatchGatewayRequest-DOdQ9bR7.js +93 -0
- package/dist/localBatchGatewayRequest-DQkbZaSy.js +6261 -0
- package/dist/parseUnits-CApwcKSD.js +49 -0
- package/dist/parseUnits-cMO2udMe.js +48 -0
- package/dist/schemas-BxMFYNbH.js +1270 -0
- package/dist/secp256k1-BZpiyffY.js +2525 -0
- package/dist/secp256k1-BjenrLl5.js +1877 -0
- package/dist/secp256k1-CLPUX17u.js +3 -0
- package/dist/sendRawTransactionSync-DvSkhZtW.js +3612 -0
- package/dist/server-CSq0IuUq.js +565 -0
- package/dist/setup-BY4J49Lv.js +1110 -0
- package/dist/setup-wMOAgrsN.js +3 -0
- package/dist/sha256-FAs0qeni.js +17 -0
- package/dist/sha3-CYkWM8Xa.js +195 -0
- package/dist/sha3-DbMJRJ3C.js +194 -0
- package/dist/sse-B4LLqBQm.js +408 -0
- package/dist/status-Bu23RjW6.js +3 -0
- package/dist/{status-DihAcUSC.js → status-X21VnGUO.js} +16 -15
- package/dist/stdio-BADqxZdZ.js +85 -0
- package/dist/streamableHttp-BHkJypcI.js +358 -0
- package/dist/tempo-3nttrxgQ.js +17 -0
- package/dist/tempo-DER0P-ul.js +18 -0
- package/dist/types-BEKUz-Mf.js +1240 -0
- package/dist/types-DatK5vR5.js +3 -0
- package/dist/utils-BYjkXZDF.js +444 -0
- package/dist/utils-SeGHMW9O.js +445 -0
- package/dist/wallet-DKVlrR1S.js +3 -0
- package/dist/wallet-DSyht15_.js +17759 -0
- package/package.json +18 -71
- package/dist/config-B_upkJeK.js +0 -66
- package/dist/config-Be35NM5s.js +0 -3
- package/dist/config-J1m-CWXT.js +0 -27
- package/dist/derive-CL6e8K0Z.js +0 -81
- package/dist/openclaw/plugin.d.ts +0 -15
- package/dist/openclaw/plugin.js +0 -2067
- package/dist/openclaw.plugin.json +0 -93
- package/dist/setup-CNyMLnM-.js +0 -197
- package/dist/setup-DTIxPe58.js +0 -3
- package/dist/status-DZlJ4pS7.js +0 -3
- package/dist/wallet-B0S-rma9.js +0 -544
- package/dist/wallet-DBrVZJqe.js +0 -3
- package/openclaw.plugin.json +0 -93
- package/skills/SKILL.md +0 -183
- package/skills/references/library.md +0 -85
- package/skills/references/openclaw-plugin.md +0 -145
package/dist/bin/cli.js
CHANGED
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { n as
|
|
5
|
-
import {
|
|
2
|
+
import { i as __toESM } from "../chunk-DjEMn6fM.js";
|
|
3
|
+
import { c as buildCommand, i as base58, l as buildRouteMap, o as require_picocolors, s as buildApplication, u as run } from "../derive-DNUl8LU9.js";
|
|
4
|
+
import { C as warn, S as success, _ as decodePaymentResponseHeader, b as info, c as resolveWallet, d as displayNetwork, f as formatAmount, g as wrapFetchWithPayment, 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 dim, x as isTTY, y as error } from "../wallet-DSyht15_.js";
|
|
5
|
+
import { t as parseUnits } from "../parseUnits-CApwcKSD.js";
|
|
6
|
+
import { a as getDebugLogPath, d as loadWalletFile, f as saveConfig, i as getConfigDirShort, l as isConfigured, n as ensureConfigDir, o as getHistoryPath, u as loadConfig } from "../config-rvKA3SYT.js";
|
|
7
|
+
import { a as Nt, c as Wt, i as Jt, l as Ct, n as setupCommand, o as R, r as Gt, s as Rt, t as runSetup } from "../setup-BY4J49Lv.js";
|
|
8
|
+
import { n as statusCommand } from "../status-X21VnGUO.js";
|
|
6
9
|
import { dirname, join, normalize, resolve } from "node:path";
|
|
7
|
-
import { buildApplication, buildCommand, buildRouteMap, run } from "@stricli/core";
|
|
8
10
|
import { spawn } from "node:child_process";
|
|
9
|
-
import pc from "picocolors";
|
|
10
11
|
import { once } from "node:events";
|
|
11
12
|
import http from "node:http";
|
|
12
|
-
import {
|
|
13
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
13
|
+
import { randomUUID } from "node:crypto";
|
|
14
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
14
15
|
import { homedir } from "node:os";
|
|
15
|
-
|
|
16
|
-
import "@sinclair/typebox";
|
|
17
|
-
import * as prompts from "@clack/prompts";
|
|
18
|
-
//#region src/openclaw/defaults.ts
|
|
16
|
+
//#region packages/x402-proxy/src/openclaw/defaults.ts
|
|
19
17
|
const DEFAULT_SURF_PROVIDER_ID = "surf";
|
|
20
18
|
const DEFAULT_SURF_BASE_URL = "/x402-proxy/v1";
|
|
21
19
|
const DEFAULT_SURF_UPSTREAM_URL = "https://surf.cascade.fyi/api/v1/inference";
|
|
@@ -139,7 +137,15 @@ function resolveMppSessionBudget(value, fallback = "0.5") {
|
|
|
139
137
|
return typeof value === "string" && value.trim().length > 0 ? value : fallback;
|
|
140
138
|
}
|
|
141
139
|
//#endregion
|
|
142
|
-
//#region src/
|
|
140
|
+
//#region packages/x402-proxy/src/lib/env.ts
|
|
141
|
+
function isDebugEnabled() {
|
|
142
|
+
return process.env.X402_PROXY_DEBUG === "1";
|
|
143
|
+
}
|
|
144
|
+
function getMppVoucherHeadroomUsdc() {
|
|
145
|
+
return process.env.X402_PROXY_MPP_VOUCHER_HEADROOM_USDC;
|
|
146
|
+
}
|
|
147
|
+
//#endregion
|
|
148
|
+
//#region packages/x402-proxy/src/handler.ts
|
|
143
149
|
/**
|
|
144
150
|
* Detect which payment protocols a 402 response advertises.
|
|
145
151
|
* - x402: PAYMENT-REQUIRED or X-PAYMENT-REQUIRED header
|
|
@@ -190,19 +196,38 @@ function createX402ProxyHandler(opts) {
|
|
|
190
196
|
};
|
|
191
197
|
}
|
|
192
198
|
const TEMPO_NETWORK = "eip155:4217";
|
|
199
|
+
const MPP_VOUCHER_HEADROOM_USDC_DEFAULT = "0.005";
|
|
200
|
+
function computeMppVoucherTarget(params) {
|
|
201
|
+
const { requiredCumulative, deposit, headroom } = params;
|
|
202
|
+
if (deposit <= requiredCumulative) return requiredCumulative;
|
|
203
|
+
if (headroom <= 0n) return requiredCumulative;
|
|
204
|
+
const target = requiredCumulative + headroom;
|
|
205
|
+
return target > deposit ? deposit : target;
|
|
206
|
+
}
|
|
207
|
+
function parseVoucherHeadroom(value) {
|
|
208
|
+
const configured = value?.trim() || MPP_VOUCHER_HEADROOM_USDC_DEFAULT;
|
|
209
|
+
try {
|
|
210
|
+
const parsed = parseUnits(configured, 6);
|
|
211
|
+
return parsed > 0n ? parsed : 0n;
|
|
212
|
+
} catch {
|
|
213
|
+
return parseUnits(MPP_VOUCHER_HEADROOM_USDC_DEFAULT, 6);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
193
216
|
/**
|
|
194
217
|
* Create an MPP proxy handler using mppx client.
|
|
195
218
|
* Dynamically imports mppx/client to keep startup fast.
|
|
196
219
|
*/
|
|
197
220
|
async function createMppProxyHandler(opts) {
|
|
198
|
-
const { Mppx, tempo } = await import("
|
|
199
|
-
const {
|
|
200
|
-
const {
|
|
221
|
+
const { Mppx, tempo } = await import("../client-CVDTUY0l.js");
|
|
222
|
+
const { Session } = await import("../tempo-DER0P-ul.js");
|
|
223
|
+
const { privateKeyToAccount } = await import("../accounts-DzvAlQRn.js");
|
|
224
|
+
const { saveSession, clearSession } = await import("../config-BUQsit4s.js");
|
|
201
225
|
const account = privateKeyToAccount(opts.evmKey);
|
|
202
226
|
const maxDeposit = opts.maxDeposit ?? "1";
|
|
227
|
+
const voucherHeadroomRaw = parseVoucherHeadroom(getMppVoucherHeadroomUsdc());
|
|
203
228
|
const paymentQueue = [];
|
|
204
229
|
let lastChallengeAmount;
|
|
205
|
-
const debug =
|
|
230
|
+
const debug = isDebugEnabled();
|
|
206
231
|
const mppx = Mppx.create({
|
|
207
232
|
methods: [tempo({
|
|
208
233
|
account,
|
|
@@ -225,8 +250,40 @@ async function createMppProxyHandler(opts) {
|
|
|
225
250
|
}
|
|
226
251
|
};
|
|
227
252
|
}
|
|
228
|
-
|
|
253
|
+
const activeSessions = /* @__PURE__ */ new Set();
|
|
229
254
|
let persistedChannelId;
|
|
255
|
+
function rememberSession(session) {
|
|
256
|
+
activeSessions.add(session);
|
|
257
|
+
if (session.channelId && session.channelId !== persistedChannelId) {
|
|
258
|
+
persistedChannelId = session.channelId;
|
|
259
|
+
if (debug) process.stderr.write(`[x402-proxy] channelId: ${persistedChannelId}\n`);
|
|
260
|
+
try {
|
|
261
|
+
saveSession({
|
|
262
|
+
channelId: session.channelId,
|
|
263
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
264
|
+
});
|
|
265
|
+
} catch {}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
function pushCloseReceipt(session, receipt) {
|
|
269
|
+
if (!receipt) return;
|
|
270
|
+
const spentUsdc = receipt.spent ? (Number(receipt.spent) / 1e6).toString() : void 0;
|
|
271
|
+
paymentQueue.push({
|
|
272
|
+
protocol: "mpp",
|
|
273
|
+
network: TEMPO_NETWORK,
|
|
274
|
+
intent: "session",
|
|
275
|
+
amount: spentUsdc,
|
|
276
|
+
channelId: session.channelId ?? void 0,
|
|
277
|
+
receipt: {
|
|
278
|
+
method: receipt.method,
|
|
279
|
+
reference: receipt.reference,
|
|
280
|
+
status: receipt.status,
|
|
281
|
+
timestamp: receipt.timestamp,
|
|
282
|
+
acceptedCumulative: receipt.acceptedCumulative,
|
|
283
|
+
txHash: receipt.txHash
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
}
|
|
230
287
|
return {
|
|
231
288
|
async fetch(input, init) {
|
|
232
289
|
const response = await mppx.fetch(typeof input === "string" ? input : input.toString(), injectPayerHeader(init));
|
|
@@ -252,60 +309,197 @@ async function createMppProxyHandler(opts) {
|
|
|
252
309
|
return response;
|
|
253
310
|
},
|
|
254
311
|
async sse(input, init) {
|
|
255
|
-
session ??= tempo.session({
|
|
256
|
-
account,
|
|
257
|
-
maxDeposit
|
|
258
|
-
});
|
|
259
312
|
const url = typeof input === "string" ? input : input.toString();
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
313
|
+
let createCredential;
|
|
314
|
+
let spent = 0n;
|
|
315
|
+
const session = {
|
|
316
|
+
channelId: void 0,
|
|
317
|
+
cumulative: 0n,
|
|
318
|
+
opened: false,
|
|
319
|
+
async close() {
|
|
320
|
+
if (!session.opened || !session.channelId || !createCredential) return void 0;
|
|
321
|
+
const credential = await createCredential({
|
|
322
|
+
action: "close",
|
|
266
323
|
channelId: session.channelId,
|
|
267
|
-
|
|
324
|
+
cumulativeAmountRaw: spent.toString()
|
|
268
325
|
});
|
|
269
|
-
|
|
270
|
-
|
|
326
|
+
const receiptHeader = (await mppx.rawFetch(url, {
|
|
327
|
+
method: "POST",
|
|
328
|
+
headers: {
|
|
329
|
+
Authorization: credential,
|
|
330
|
+
"X-Payer-Address": payerAddress
|
|
331
|
+
}
|
|
332
|
+
})).headers.get("Payment-Receipt");
|
|
333
|
+
if (!receiptHeader) return void 0;
|
|
334
|
+
try {
|
|
335
|
+
const receipt = Session.Receipt.deserializeSessionReceipt(receiptHeader);
|
|
336
|
+
spent = spent > BigInt(receipt.spent) ? spent : BigInt(receipt.spent);
|
|
337
|
+
return receipt;
|
|
338
|
+
} catch {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
const sessionMppx = Mppx.create({
|
|
344
|
+
methods: [tempo({
|
|
345
|
+
account,
|
|
346
|
+
maxDeposit,
|
|
347
|
+
onChannelUpdate(entry) {
|
|
348
|
+
session.channelId = entry.channelId;
|
|
349
|
+
session.cumulative = entry.cumulativeAmount;
|
|
350
|
+
session.opened = entry.opened;
|
|
351
|
+
if (entry.channelId && entry.channelId !== persistedChannelId) {
|
|
352
|
+
persistedChannelId = entry.channelId;
|
|
353
|
+
if (debug) process.stderr.write(`[x402-proxy] channelId: ${persistedChannelId}\n`);
|
|
354
|
+
try {
|
|
355
|
+
saveSession({
|
|
356
|
+
channelId: entry.channelId,
|
|
357
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
358
|
+
});
|
|
359
|
+
} catch {}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
})],
|
|
363
|
+
polyfill: false,
|
|
364
|
+
onChallenge: async (challenge, helpers) => {
|
|
365
|
+
const req = challenge.request;
|
|
366
|
+
if (req.amount) lastChallengeAmount = (Number(req.amount) / 10 ** (req.decimals ?? 6)).toString();
|
|
367
|
+
createCredential = helpers.createCredential;
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
const response = await sessionMppx.fetch(url, injectPayerHeader({
|
|
371
|
+
...init,
|
|
372
|
+
headers: {
|
|
373
|
+
...init?.headers instanceof Headers ? Object.fromEntries(init.headers.entries()) : init?.headers ?? {},
|
|
374
|
+
Accept: "text/event-stream"
|
|
375
|
+
}
|
|
376
|
+
}));
|
|
377
|
+
if (!response.body) throw new Error("MPP SSE response has no body");
|
|
378
|
+
rememberSession(session);
|
|
271
379
|
paymentQueue.push({
|
|
272
380
|
protocol: "mpp",
|
|
273
381
|
network: TEMPO_NETWORK,
|
|
274
382
|
intent: "session"
|
|
275
383
|
});
|
|
276
|
-
|
|
384
|
+
const reader = response.body.getReader();
|
|
385
|
+
const decoder = new TextDecoder();
|
|
386
|
+
async function* iterate() {
|
|
387
|
+
let buffer = "";
|
|
388
|
+
try {
|
|
389
|
+
while (true) {
|
|
390
|
+
const { done, value } = await reader.read();
|
|
391
|
+
if (done) break;
|
|
392
|
+
buffer += decoder.decode(value, { stream: true });
|
|
393
|
+
const parts = buffer.split("\n\n");
|
|
394
|
+
buffer = parts.pop() ?? "";
|
|
395
|
+
for (const part of parts) {
|
|
396
|
+
if (!part.trim()) continue;
|
|
397
|
+
const event = Session.Sse.parseEvent(part);
|
|
398
|
+
if (!event) continue;
|
|
399
|
+
switch (event.type) {
|
|
400
|
+
case "message":
|
|
401
|
+
yield event.data;
|
|
402
|
+
break;
|
|
403
|
+
case "payment-receipt":
|
|
404
|
+
spent = spent > BigInt(event.data.spent) ? spent : BigInt(event.data.spent);
|
|
405
|
+
break;
|
|
406
|
+
case "payment-need-voucher": {
|
|
407
|
+
if (!createCredential) throw new Error("MPP voucher requested before challenge helper was captured");
|
|
408
|
+
const target = computeMppVoucherTarget({
|
|
409
|
+
requiredCumulative: BigInt(event.data.requiredCumulative),
|
|
410
|
+
deposit: BigInt(event.data.deposit),
|
|
411
|
+
headroom: voucherHeadroomRaw
|
|
412
|
+
});
|
|
413
|
+
const credential = await createCredential({
|
|
414
|
+
action: "voucher",
|
|
415
|
+
channelId: event.data.channelId,
|
|
416
|
+
cumulativeAmountRaw: target.toString()
|
|
417
|
+
});
|
|
418
|
+
const voucherResponse = await sessionMppx.rawFetch(url, {
|
|
419
|
+
method: "POST",
|
|
420
|
+
headers: {
|
|
421
|
+
Authorization: credential,
|
|
422
|
+
"X-Payer-Address": payerAddress
|
|
423
|
+
},
|
|
424
|
+
signal: init?.signal
|
|
425
|
+
});
|
|
426
|
+
if (!voucherResponse.ok) {
|
|
427
|
+
const body = await voucherResponse.text().catch(() => "");
|
|
428
|
+
throw new Error(`Voucher POST failed with status ${voucherResponse.status}${body ? `: ${body.slice(0, 200)}` : ""}`);
|
|
429
|
+
}
|
|
430
|
+
const receiptHeader = voucherResponse.headers.get("Payment-Receipt");
|
|
431
|
+
if (receiptHeader) try {
|
|
432
|
+
const receipt = Session.Receipt.deserializeSessionReceipt(receiptHeader);
|
|
433
|
+
spent = spent > BigInt(receipt.spent) ? spent : BigInt(receipt.spent);
|
|
434
|
+
} catch {}
|
|
435
|
+
break;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
} finally {
|
|
441
|
+
reader.releaseLock();
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return iterate();
|
|
277
445
|
},
|
|
278
446
|
shiftPayment: () => paymentQueue.shift(),
|
|
279
447
|
async close() {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
if (
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
network: TEMPO_NETWORK,
|
|
290
|
-
intent: "session",
|
|
291
|
-
amount: spentUsdc,
|
|
292
|
-
channelId: session.channelId ?? void 0,
|
|
293
|
-
receipt: {
|
|
294
|
-
method: receipt.method,
|
|
295
|
-
reference: receipt.reference,
|
|
296
|
-
status: receipt.status,
|
|
297
|
-
timestamp: receipt.timestamp,
|
|
298
|
-
acceptedCumulative: receipt.acceptedCumulative,
|
|
299
|
-
txHash: receipt.txHash
|
|
300
|
-
}
|
|
301
|
-
});
|
|
448
|
+
const sessions = Array.from(activeSessions);
|
|
449
|
+
if (sessions.length === 0) return;
|
|
450
|
+
const byChannelId = /* @__PURE__ */ new Map();
|
|
451
|
+
const sessionsWithoutChannel = [];
|
|
452
|
+
for (const session of sessions) {
|
|
453
|
+
if (!session.opened) continue;
|
|
454
|
+
if (!session.channelId) {
|
|
455
|
+
sessionsWithoutChannel.push(session);
|
|
456
|
+
continue;
|
|
302
457
|
}
|
|
458
|
+
const existing = byChannelId.get(session.channelId);
|
|
459
|
+
if (!existing || session.cumulative > existing.cumulative) byChannelId.set(session.channelId, session);
|
|
303
460
|
}
|
|
461
|
+
for (const session of sessions) activeSessions.delete(session);
|
|
462
|
+
const sessionsToClose = [...byChannelId.values(), ...sessionsWithoutChannel];
|
|
463
|
+
let shouldClearPersistedSession = false;
|
|
464
|
+
for (const session of sessionsToClose) {
|
|
465
|
+
const receipt = await session.close();
|
|
466
|
+
if (session.channelId && session.channelId === persistedChannelId) shouldClearPersistedSession = true;
|
|
467
|
+
pushCloseReceipt(session, receipt);
|
|
468
|
+
}
|
|
469
|
+
if (shouldClearPersistedSession) try {
|
|
470
|
+
clearSession();
|
|
471
|
+
} catch {}
|
|
304
472
|
}
|
|
305
473
|
};
|
|
306
474
|
}
|
|
307
475
|
//#endregion
|
|
308
|
-
//#region src/
|
|
476
|
+
//#region packages/x402-proxy/src/lib/debug-log.ts
|
|
477
|
+
const MAX_DEBUG_LOG_BYTES = 10 * 1024 * 1024;
|
|
478
|
+
const ROTATED_DEBUG_LOG_SUFFIX = ".1";
|
|
479
|
+
function rotateIfNeeded(path) {
|
|
480
|
+
if (!existsSync(path)) return;
|
|
481
|
+
const { size } = statSync(path);
|
|
482
|
+
if (size <= MAX_DEBUG_LOG_BYTES) return;
|
|
483
|
+
const rotated = `${path}${ROTATED_DEBUG_LOG_SUFFIX}`;
|
|
484
|
+
try {
|
|
485
|
+
rmSync(rotated, { force: true });
|
|
486
|
+
} catch {}
|
|
487
|
+
renameSync(path, rotated);
|
|
488
|
+
}
|
|
489
|
+
function writeDebugLog(event, fields = {}) {
|
|
490
|
+
try {
|
|
491
|
+
ensureConfigDir();
|
|
492
|
+
const path = getDebugLogPath();
|
|
493
|
+
rotateIfNeeded(path);
|
|
494
|
+
appendFileSync(path, `${JSON.stringify({
|
|
495
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
496
|
+
event,
|
|
497
|
+
...fields
|
|
498
|
+
})}\n`, "utf-8");
|
|
499
|
+
} catch {}
|
|
500
|
+
}
|
|
501
|
+
//#endregion
|
|
502
|
+
//#region packages/x402-proxy/src/openclaw/tools.ts
|
|
309
503
|
const SOL_MAINNET = "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp";
|
|
310
504
|
const USDC_DECIMALS = 6;
|
|
311
505
|
function paymentAmount(payment) {
|
|
@@ -324,9 +518,9 @@ function addressForNetwork(evmAddress, solanaAddress, network) {
|
|
|
324
518
|
return solanaAddress ?? evmAddress;
|
|
325
519
|
}
|
|
326
520
|
//#endregion
|
|
327
|
-
//#region src/openclaw/route.ts
|
|
521
|
+
//#region packages/x402-proxy/src/openclaw/route.ts
|
|
328
522
|
function dbg(msg) {
|
|
329
|
-
if (
|
|
523
|
+
if (isDebugEnabled()) process.stderr.write(`[x402-proxy] ${msg}\n`);
|
|
330
524
|
}
|
|
331
525
|
function createDownstreamAbort(req, res) {
|
|
332
526
|
const controller = new AbortController();
|
|
@@ -350,6 +544,7 @@ function createInferenceProxyRouteHandler(opts) {
|
|
|
350
544
|
const sortedProviders = providers.slice().sort((left, right) => right.baseUrl.length - left.baseUrl.length);
|
|
351
545
|
return async (req, res) => {
|
|
352
546
|
const downstream = createDownstreamAbort(req, res);
|
|
547
|
+
const requestId = randomUUID().slice(0, 8);
|
|
353
548
|
const url = new URL(req.url ?? "/", "http://localhost");
|
|
354
549
|
const provider = sortedProviders.find((entry) => entry.baseUrl === "/" || url.pathname.startsWith(entry.baseUrl));
|
|
355
550
|
if (!provider) {
|
|
@@ -409,6 +604,15 @@ function createInferenceProxyRouteHandler(opts) {
|
|
|
409
604
|
} catch {}
|
|
410
605
|
const method = req.method ?? "GET";
|
|
411
606
|
const startMs = Date.now();
|
|
607
|
+
writeDebugLog("proxy.request.start", {
|
|
608
|
+
requestId,
|
|
609
|
+
method,
|
|
610
|
+
path: url.pathname,
|
|
611
|
+
upstreamUrl,
|
|
612
|
+
protocol: provider.protocol,
|
|
613
|
+
isMessagesApi,
|
|
614
|
+
isLlmEndpoint
|
|
615
|
+
});
|
|
412
616
|
try {
|
|
413
617
|
const requestInit = {
|
|
414
618
|
method,
|
|
@@ -435,6 +639,7 @@ function createInferenceProxyRouteHandler(opts) {
|
|
|
435
639
|
requestInit,
|
|
436
640
|
abortSignal: downstream.signal,
|
|
437
641
|
isLlmEndpoint,
|
|
642
|
+
requestId,
|
|
438
643
|
walletAddress: mppWalletAddress,
|
|
439
644
|
historyPath,
|
|
440
645
|
logger,
|
|
@@ -536,18 +741,43 @@ function createInferenceProxyRouteHandler(opts) {
|
|
|
536
741
|
const sse = isLlmEndpoint && ct.includes("text/event-stream") ? createSseTracker() : null;
|
|
537
742
|
const reader = response.body.getReader();
|
|
538
743
|
const decoder = new TextDecoder();
|
|
744
|
+
let sawFirstChunk = false;
|
|
745
|
+
let sawFirstWrite = false;
|
|
539
746
|
try {
|
|
540
747
|
while (true) {
|
|
541
748
|
if (downstream.signal.aborted) {
|
|
749
|
+
writeDebugLog("proxy.x402.aborted", { requestId });
|
|
542
750
|
await reader.cancel().catch(() => {});
|
|
543
751
|
break;
|
|
544
752
|
}
|
|
545
753
|
const { done, value } = await reader.read();
|
|
546
754
|
if (done) break;
|
|
755
|
+
if (!sawFirstChunk) {
|
|
756
|
+
sawFirstChunk = true;
|
|
757
|
+
writeDebugLog("proxy.x402.first_chunk", {
|
|
758
|
+
requestId,
|
|
759
|
+
ms: Date.now() - startMs
|
|
760
|
+
});
|
|
761
|
+
}
|
|
547
762
|
res.write(value);
|
|
763
|
+
if (!sawFirstWrite) {
|
|
764
|
+
sawFirstWrite = true;
|
|
765
|
+
writeDebugLog("proxy.x402.first_write", {
|
|
766
|
+
requestId,
|
|
767
|
+
ms: Date.now() - startMs
|
|
768
|
+
});
|
|
769
|
+
}
|
|
548
770
|
sse?.push(decoder.decode(value, { stream: true }));
|
|
549
771
|
if (isMessagesApi && sse?.sawAnthropicMessageStop) {
|
|
772
|
+
writeDebugLog("proxy.x402.message_stop", {
|
|
773
|
+
requestId,
|
|
774
|
+
ms: Date.now() - startMs
|
|
775
|
+
});
|
|
550
776
|
await reader.cancel().catch(() => {});
|
|
777
|
+
writeDebugLog("proxy.x402.semantic_close", {
|
|
778
|
+
requestId,
|
|
779
|
+
ms: Date.now() - startMs
|
|
780
|
+
});
|
|
551
781
|
break;
|
|
552
782
|
}
|
|
553
783
|
}
|
|
@@ -555,6 +785,11 @@ function createInferenceProxyRouteHandler(opts) {
|
|
|
555
785
|
reader.releaseLock();
|
|
556
786
|
}
|
|
557
787
|
if (!res.writableEnded) res.end();
|
|
788
|
+
writeDebugLog("proxy.x402.end", {
|
|
789
|
+
requestId,
|
|
790
|
+
ms: Date.now() - startMs,
|
|
791
|
+
hasUsage: sse?.result != null
|
|
792
|
+
});
|
|
558
793
|
if (shouldAppendInferenceHistory({
|
|
559
794
|
isLlmEndpoint,
|
|
560
795
|
amount,
|
|
@@ -574,6 +809,11 @@ function createInferenceProxyRouteHandler(opts) {
|
|
|
574
809
|
return true;
|
|
575
810
|
} catch (err) {
|
|
576
811
|
const msg = String(err);
|
|
812
|
+
writeDebugLog("proxy.request.error", {
|
|
813
|
+
requestId,
|
|
814
|
+
ms: Date.now() - startMs,
|
|
815
|
+
error: msg.substring(0, 200)
|
|
816
|
+
});
|
|
577
817
|
logger.error(`x402: fetch threw: ${msg}`);
|
|
578
818
|
getX402Proxy()?.shiftPayment();
|
|
579
819
|
if (isLlmEndpoint) appendHistory(historyPath, {
|
|
@@ -592,6 +832,11 @@ function createInferenceProxyRouteHandler(opts) {
|
|
|
592
832
|
if (!res.headersSent) writeErrorResponse(res, 402, userMessage, "x402_payment_error", "payment_failed", isMessagesApi);
|
|
593
833
|
return true;
|
|
594
834
|
} finally {
|
|
835
|
+
writeDebugLog("proxy.request.finish", {
|
|
836
|
+
requestId,
|
|
837
|
+
ms: Date.now() - startMs,
|
|
838
|
+
aborted: downstream.signal.aborted
|
|
839
|
+
});
|
|
595
840
|
downstream.cleanup();
|
|
596
841
|
}
|
|
597
842
|
};
|
|
@@ -711,7 +956,7 @@ function createSseTracker() {
|
|
|
711
956
|
};
|
|
712
957
|
}
|
|
713
958
|
async function handleMppRequest(opts) {
|
|
714
|
-
const { res, upstreamUrl, requestInit, abortSignal, isLlmEndpoint, walletAddress, historyPath, logger, allModels, thinkingMode, wantsStreaming, isMessagesApi, startMs, mpp } = opts;
|
|
959
|
+
const { res, upstreamUrl, requestInit, abortSignal, isLlmEndpoint, requestId, walletAddress, historyPath, logger, allModels, thinkingMode, wantsStreaming, isMessagesApi, startMs, mpp } = opts;
|
|
715
960
|
try {
|
|
716
961
|
if (wantsStreaming) {
|
|
717
962
|
res.writeHead(200, {
|
|
@@ -721,46 +966,100 @@ async function handleMppRequest(opts) {
|
|
|
721
966
|
});
|
|
722
967
|
const sse = createSseTracker();
|
|
723
968
|
dbg(`mpp.sse() calling ${upstreamUrl}`);
|
|
969
|
+
writeDebugLog("proxy.mpp.sse.open", {
|
|
970
|
+
requestId,
|
|
971
|
+
upstreamUrl
|
|
972
|
+
});
|
|
724
973
|
const stream = await mpp.sse(upstreamUrl, requestInit);
|
|
725
974
|
dbg("mpp.sse() resolved, iterating stream");
|
|
726
975
|
const iterator = stream[Symbol.asyncIterator]();
|
|
727
976
|
let semanticComplete = false;
|
|
977
|
+
let sawFirstChunk = false;
|
|
978
|
+
let sawFirstWrite = false;
|
|
728
979
|
try {
|
|
729
980
|
if (isMessagesApi) while (true) {
|
|
730
|
-
if (abortSignal.aborted)
|
|
981
|
+
if (abortSignal.aborted) {
|
|
982
|
+
writeDebugLog("proxy.mpp.aborted", { requestId });
|
|
983
|
+
break;
|
|
984
|
+
}
|
|
731
985
|
const { done, value } = await iterator.next();
|
|
732
986
|
if (done) break;
|
|
733
987
|
const text = String(value);
|
|
988
|
+
if (!sawFirstChunk) {
|
|
989
|
+
sawFirstChunk = true;
|
|
990
|
+
writeDebugLog("proxy.mpp.first_chunk", {
|
|
991
|
+
requestId,
|
|
992
|
+
ms: Date.now() - startMs
|
|
993
|
+
});
|
|
994
|
+
}
|
|
734
995
|
let eventType = "unknown";
|
|
735
996
|
try {
|
|
736
997
|
eventType = JSON.parse(text).type ?? "unknown";
|
|
737
998
|
} catch {}
|
|
738
999
|
res.write(`event: ${eventType}\ndata: ${text}\n\n`);
|
|
1000
|
+
if (!sawFirstWrite) {
|
|
1001
|
+
sawFirstWrite = true;
|
|
1002
|
+
writeDebugLog("proxy.mpp.first_write", {
|
|
1003
|
+
requestId,
|
|
1004
|
+
ms: Date.now() - startMs
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
739
1007
|
sse.pushJson(text);
|
|
740
1008
|
if (sse.sawAnthropicMessageStop) {
|
|
741
1009
|
semanticComplete = true;
|
|
1010
|
+
writeDebugLog("proxy.mpp.message_stop", {
|
|
1011
|
+
requestId,
|
|
1012
|
+
ms: Date.now() - startMs
|
|
1013
|
+
});
|
|
742
1014
|
break;
|
|
743
1015
|
}
|
|
744
1016
|
}
|
|
745
1017
|
else {
|
|
746
1018
|
while (true) {
|
|
747
|
-
if (abortSignal.aborted)
|
|
1019
|
+
if (abortSignal.aborted) {
|
|
1020
|
+
writeDebugLog("proxy.mpp.aborted", { requestId });
|
|
1021
|
+
break;
|
|
1022
|
+
}
|
|
748
1023
|
const { done, value } = await iterator.next();
|
|
749
1024
|
if (done) break;
|
|
750
1025
|
const text = String(value);
|
|
1026
|
+
if (!sawFirstChunk) {
|
|
1027
|
+
sawFirstChunk = true;
|
|
1028
|
+
writeDebugLog("proxy.mpp.first_chunk", {
|
|
1029
|
+
requestId,
|
|
1030
|
+
ms: Date.now() - startMs
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
751
1033
|
res.write(`data: ${text}\n\n`);
|
|
1034
|
+
if (!sawFirstWrite) {
|
|
1035
|
+
sawFirstWrite = true;
|
|
1036
|
+
writeDebugLog("proxy.mpp.first_write", {
|
|
1037
|
+
requestId,
|
|
1038
|
+
ms: Date.now() - startMs
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
752
1041
|
sse.pushJson(text);
|
|
753
1042
|
}
|
|
754
1043
|
if (!abortSignal.aborted) res.write("data: [DONE]\n\n");
|
|
755
1044
|
}
|
|
756
1045
|
} catch (err) {
|
|
757
1046
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1047
|
+
writeDebugLog("proxy.mpp.stream_error", {
|
|
1048
|
+
requestId,
|
|
1049
|
+
ms: Date.now() - startMs,
|
|
1050
|
+
error: msg.substring(0, 200),
|
|
1051
|
+
semanticComplete
|
|
1052
|
+
});
|
|
758
1053
|
if (!(abortSignal.aborted || semanticComplete) || !msg.includes("terminated")) throw err;
|
|
759
1054
|
} finally {
|
|
760
1055
|
await iterator.return?.().catch(() => {});
|
|
761
1056
|
}
|
|
762
1057
|
dbg(`stream done, ${sse.result ? `${sse.result.model} ${sse.result.inputTokens}+${sse.result.outputTokens}t` : "no usage"}`);
|
|
763
1058
|
if (!res.writableEnded) res.end();
|
|
1059
|
+
if (semanticComplete) writeDebugLog("proxy.mpp.semantic_close", {
|
|
1060
|
+
requestId,
|
|
1061
|
+
ms: Date.now() - startMs
|
|
1062
|
+
});
|
|
764
1063
|
mpp.shiftPayment();
|
|
765
1064
|
if (shouldAppendInferenceHistory({
|
|
766
1065
|
isLlmEndpoint,
|
|
@@ -775,9 +1074,19 @@ async function handleMppRequest(opts) {
|
|
|
775
1074
|
usage: sse.result,
|
|
776
1075
|
durationMs: Date.now() - startMs
|
|
777
1076
|
});
|
|
1077
|
+
writeDebugLog("proxy.mpp.end", {
|
|
1078
|
+
requestId,
|
|
1079
|
+
ms: Date.now() - startMs,
|
|
1080
|
+
hasUsage: sse.result != null
|
|
1081
|
+
});
|
|
778
1082
|
return true;
|
|
779
1083
|
}
|
|
780
1084
|
const response = await mpp.fetch(upstreamUrl, requestInit);
|
|
1085
|
+
writeDebugLog("proxy.mpp.fetch.response", {
|
|
1086
|
+
requestId,
|
|
1087
|
+
status: response.status,
|
|
1088
|
+
ms: Date.now() - startMs
|
|
1089
|
+
});
|
|
781
1090
|
const tx = extractTxSignature(response);
|
|
782
1091
|
if (response.status === 402) {
|
|
783
1092
|
const responseBody = await response.text();
|
|
@@ -873,14 +1182,14 @@ function appendInferenceHistory(opts) {
|
|
|
873
1182
|
});
|
|
874
1183
|
}
|
|
875
1184
|
//#endregion
|
|
876
|
-
//#region src/commands/serve.ts
|
|
1185
|
+
//#region packages/x402-proxy/src/commands/serve.ts
|
|
877
1186
|
async function resolveWalletForServe(flags) {
|
|
878
1187
|
let wallet = resolveWallet({
|
|
879
1188
|
evmKey: flags.evmKey,
|
|
880
1189
|
solanaKey: flags.solanaKey
|
|
881
1190
|
});
|
|
882
1191
|
if (wallet.source !== "none") return wallet;
|
|
883
|
-
const { runSetup } = await import("../setup-
|
|
1192
|
+
const { runSetup } = await import("../setup-wMOAgrsN.js");
|
|
884
1193
|
if (isTTY()) {
|
|
885
1194
|
dim(" No wallet found. Let's set one up first.\n");
|
|
886
1195
|
await runSetup();
|
|
@@ -1081,7 +1390,7 @@ Examples:
|
|
|
1081
1390
|
}
|
|
1082
1391
|
});
|
|
1083
1392
|
//#endregion
|
|
1084
|
-
//#region src/commands/claude.ts
|
|
1393
|
+
//#region packages/x402-proxy/src/commands/claude.ts
|
|
1085
1394
|
const DEFAULT_MODEL = "stepfun/step-3.5-flash";
|
|
1086
1395
|
const modelList = [
|
|
1087
1396
|
"stepfun/step-3.5-flash",
|
|
@@ -1217,7 +1526,8 @@ not listed above.`
|
|
|
1217
1526
|
}
|
|
1218
1527
|
});
|
|
1219
1528
|
//#endregion
|
|
1220
|
-
//#region src/commands/config.ts
|
|
1529
|
+
//#region packages/x402-proxy/src/commands/config.ts
|
|
1530
|
+
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
1221
1531
|
const VALID_KEYS = {
|
|
1222
1532
|
defaultNetwork: {
|
|
1223
1533
|
description: "Preferred network (base, solana, tempo)",
|
|
@@ -1277,23 +1587,23 @@ const configShowCommand = buildCommand({
|
|
|
1277
1587
|
async func() {
|
|
1278
1588
|
const config = loadConfig();
|
|
1279
1589
|
console.log();
|
|
1280
|
-
console.log(
|
|
1590
|
+
console.log(import_picocolors.default.bold("Configuration"));
|
|
1281
1591
|
dim(` ${getConfigDirShort()}/config.yaml`);
|
|
1282
1592
|
console.log();
|
|
1283
1593
|
if (!config || Object.keys(config).length === 0) {
|
|
1284
1594
|
dim(" No configuration set. Using defaults.");
|
|
1285
1595
|
console.log();
|
|
1286
1596
|
dim(" Available keys:");
|
|
1287
|
-
for (const [key, meta] of Object.entries(VALID_KEYS)) dim(` ${
|
|
1597
|
+
for (const [key, meta] of Object.entries(VALID_KEYS)) dim(` ${import_picocolors.default.cyan(key)} - ${meta.description}`);
|
|
1288
1598
|
console.log();
|
|
1289
|
-
dim(` Set with: ${
|
|
1599
|
+
dim(` Set with: ${import_picocolors.default.cyan("npx x402-proxy config set <key> <value>")}`);
|
|
1290
1600
|
console.log();
|
|
1291
1601
|
return;
|
|
1292
1602
|
}
|
|
1293
1603
|
for (const key of Object.keys(VALID_KEYS)) {
|
|
1294
1604
|
const value = config[key];
|
|
1295
|
-
if (value !== void 0) console.log(` ${
|
|
1296
|
-
else dim(` ${key}: ${
|
|
1605
|
+
if (value !== void 0) console.log(` ${import_picocolors.default.cyan(key)}: ${import_picocolors.default.green(String(value))}`);
|
|
1606
|
+
else dim(` ${key}: ${import_picocolors.default.dim("(not set)")}`);
|
|
1297
1607
|
}
|
|
1298
1608
|
console.log();
|
|
1299
1609
|
}
|
|
@@ -1326,7 +1636,7 @@ To unset a value, use: npx x402-proxy config unset <key>`
|
|
|
1326
1636
|
error(`Unknown config key: ${key}`);
|
|
1327
1637
|
console.error();
|
|
1328
1638
|
dim(" Available keys:");
|
|
1329
|
-
for (const [k, m] of Object.entries(VALID_KEYS)) dim(` ${
|
|
1639
|
+
for (const [k, m] of Object.entries(VALID_KEYS)) dim(` ${import_picocolors.default.cyan(k)} - ${m.description}`);
|
|
1330
1640
|
process.exit(1);
|
|
1331
1641
|
}
|
|
1332
1642
|
const meta = VALID_KEYS[key];
|
|
@@ -1340,7 +1650,7 @@ To unset a value, use: npx x402-proxy config unset <key>`
|
|
|
1340
1650
|
const config = loadConfig() ?? {};
|
|
1341
1651
|
config[key] = parsed;
|
|
1342
1652
|
saveConfig(config);
|
|
1343
|
-
console.log(` ${
|
|
1653
|
+
console.log(` ${import_picocolors.default.cyan(key)} = ${import_picocolors.default.green(String(parsed))}`);
|
|
1344
1654
|
}
|
|
1345
1655
|
});
|
|
1346
1656
|
const configUnsetCommand = buildCommand({
|
|
@@ -1367,7 +1677,7 @@ const configUnsetCommand = buildCommand({
|
|
|
1367
1677
|
}
|
|
1368
1678
|
});
|
|
1369
1679
|
//#endregion
|
|
1370
|
-
//#region src/commands/fetch.ts
|
|
1680
|
+
//#region packages/x402-proxy/src/commands/fetch.ts
|
|
1371
1681
|
function isStreamingResponse(res) {
|
|
1372
1682
|
return (res.headers.get("content-type") ?? "").includes("text/event-stream");
|
|
1373
1683
|
}
|
|
@@ -1465,39 +1775,39 @@ Examples:
|
|
|
1465
1775
|
};
|
|
1466
1776
|
if (!url) {
|
|
1467
1777
|
if (isConfigured()) {
|
|
1468
|
-
const { displayStatus } = await import("../status-
|
|
1778
|
+
const { displayStatus } = await import("../status-Bu23RjW6.js");
|
|
1469
1779
|
await displayStatus();
|
|
1470
1780
|
console.log();
|
|
1471
|
-
console.log(
|
|
1472
|
-
console.log(` ${
|
|
1473
|
-
console.log(` ${
|
|
1474
|
-
console.log(` ${
|
|
1475
|
-
console.log(` ${
|
|
1476
|
-
console.log(` ${
|
|
1477
|
-
console.log(` ${
|
|
1478
|
-
console.log(` ${
|
|
1781
|
+
console.log(import_picocolors.default.dim(" Commands:"));
|
|
1782
|
+
console.log(` ${import_picocolors.default.cyan("$ npx x402-proxy <url>")} Fetch a paid API`);
|
|
1783
|
+
console.log(` ${import_picocolors.default.cyan("$ npx x402-proxy mcp <url>")} MCP proxy for AI agents`);
|
|
1784
|
+
console.log(` ${import_picocolors.default.cyan("$ npx x402-proxy claude")} Run Claude Code via paid proxy`);
|
|
1785
|
+
console.log(` ${import_picocolors.default.cyan("$ npx x402-proxy claude --model <id>")} Use a specific model`);
|
|
1786
|
+
console.log(` ${import_picocolors.default.cyan("$ npx x402-proxy setup")} Reconfigure wallet`);
|
|
1787
|
+
console.log(` ${import_picocolors.default.cyan("$ npx x402-proxy wallet")} Addresses and balances`);
|
|
1788
|
+
console.log(` ${import_picocolors.default.cyan("$ npx x402-proxy wallet history")} Full payment history`);
|
|
1479
1789
|
console.log();
|
|
1480
|
-
console.log(
|
|
1790
|
+
console.log(import_picocolors.default.dim(" try: ") + import_picocolors.default.cyan(`$ npx x402-proxy -X POST -d '{"ref":"CoinbaseDev"}' https://surf.cascade.fyi/api/v1/twitter/user`));
|
|
1481
1791
|
console.log();
|
|
1482
|
-
console.log(
|
|
1792
|
+
console.log(import_picocolors.default.dim(" https://github.com/cascade-protocol/x402-proxy"));
|
|
1483
1793
|
console.log();
|
|
1484
1794
|
} else {
|
|
1485
1795
|
console.log();
|
|
1486
|
-
console.log(
|
|
1487
|
-
console.log(
|
|
1796
|
+
console.log(import_picocolors.default.cyan(import_picocolors.default.bold("x402-proxy")));
|
|
1797
|
+
console.log(import_picocolors.default.dim("curl for x402 paid APIs"));
|
|
1488
1798
|
console.log();
|
|
1489
|
-
console.log(
|
|
1490
|
-
console.log(` ${
|
|
1491
|
-
console.log(` ${
|
|
1492
|
-
console.log(` ${
|
|
1493
|
-
console.log(` ${
|
|
1494
|
-
console.log(` ${
|
|
1495
|
-
console.log(` ${
|
|
1496
|
-
console.log(` ${
|
|
1799
|
+
console.log(import_picocolors.default.dim(" Commands:"));
|
|
1800
|
+
console.log(` ${import_picocolors.default.cyan("$ npx x402-proxy setup")} Create a wallet`);
|
|
1801
|
+
console.log(` ${import_picocolors.default.cyan("$ npx x402-proxy <url>")} Fetch a paid API`);
|
|
1802
|
+
console.log(` ${import_picocolors.default.cyan("$ npx x402-proxy mcp <url>")} MCP proxy for AI agents`);
|
|
1803
|
+
console.log(` ${import_picocolors.default.cyan("$ npx x402-proxy claude")} Run Claude Code via paid proxy`);
|
|
1804
|
+
console.log(` ${import_picocolors.default.cyan("$ npx x402-proxy wallet")} Addresses and balances`);
|
|
1805
|
+
console.log(` ${import_picocolors.default.cyan("$ npx x402-proxy wallet history")} Payment history`);
|
|
1806
|
+
console.log(` ${import_picocolors.default.cyan("$ npx x402-proxy --help")} All options`);
|
|
1497
1807
|
console.log();
|
|
1498
|
-
console.log(
|
|
1808
|
+
console.log(import_picocolors.default.dim(" try: ") + import_picocolors.default.cyan("$ npx x402-proxy setup"));
|
|
1499
1809
|
console.log();
|
|
1500
|
-
console.log(
|
|
1810
|
+
console.log(import_picocolors.default.dim(" https://github.com/cascade-protocol/x402-proxy"));
|
|
1501
1811
|
console.log();
|
|
1502
1812
|
}
|
|
1503
1813
|
return;
|
|
@@ -1516,11 +1826,11 @@ Examples:
|
|
|
1516
1826
|
if (wallet.source === "none") {
|
|
1517
1827
|
if (!isTTY()) {
|
|
1518
1828
|
error("No wallet configured.");
|
|
1519
|
-
console.error(
|
|
1829
|
+
console.error(import_picocolors.default.dim(`Run:\n ${import_picocolors.default.cyan("$ npx x402-proxy setup")}\n\nOr set X402_PROXY_WALLET_MNEMONIC`));
|
|
1520
1830
|
process.exit(1);
|
|
1521
1831
|
}
|
|
1522
1832
|
dim(" No wallet found. Let's set one up first.\n");
|
|
1523
|
-
const { runSetup } = await import("../setup-
|
|
1833
|
+
const { runSetup } = await import("../setup-wMOAgrsN.js");
|
|
1524
1834
|
await runSetup();
|
|
1525
1835
|
console.log();
|
|
1526
1836
|
wallet = resolveWallet();
|
|
@@ -1533,7 +1843,7 @@ Examples:
|
|
|
1533
1843
|
verbose(`protocol: ${resolvedProtocol ?? "auto-detect"}, maxDeposit: ${maxDeposit}`);
|
|
1534
1844
|
let preferredNetwork = config?.defaultNetwork;
|
|
1535
1845
|
if (!preferredNetwork && wallet.evmAddress && wallet.solanaAddress) {
|
|
1536
|
-
const { fetchAllBalances } = await import("../wallet-
|
|
1846
|
+
const { fetchAllBalances } = await import("../wallet-DKVlrR1S.js");
|
|
1537
1847
|
const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
|
|
1538
1848
|
const evmUsdc = balances.evm ? Number(balances.evm.usdc) : 0;
|
|
1539
1849
|
const solUsdc = balances.sol ? Number(balances.sol.usdc) : 0;
|
|
@@ -1700,7 +2010,7 @@ Examples:
|
|
|
1700
2010
|
const hasSolana = accepts.some((a) => a.network.startsWith("solana:"));
|
|
1701
2011
|
const hasMpp = detected.mpp;
|
|
1702
2012
|
const hasOther = accepts.some((a) => !a.network.startsWith("eip155:") && !a.network.startsWith("solana:"));
|
|
1703
|
-
const { fetchAllBalances } = await import("../wallet-
|
|
2013
|
+
const { fetchAllBalances } = await import("../wallet-DKVlrR1S.js");
|
|
1704
2014
|
const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
|
|
1705
2015
|
const evmUsdc = hasEvm && balances.evm ? Number(balances.evm.usdc) : 0;
|
|
1706
2016
|
const solUsdc = hasSolana && balances.sol ? Number(balances.sol.usdc) : 0;
|
|
@@ -1719,9 +2029,9 @@ Examples:
|
|
|
1719
2029
|
if (payment) dim(" Payment was signed and sent but rejected by the server.");
|
|
1720
2030
|
else dim(" Payment was not attempted despite sufficient balance.");
|
|
1721
2031
|
if (serverReason) dim(` Reason: ${serverReason}`);
|
|
1722
|
-
if (hasEvm && wallet.evmAddress && evmUsdc > 0) console.error(` Base: ${
|
|
1723
|
-
if (hasMpp && wallet.evmAddress && tempoUsdc > 0) console.error(` Tempo: ${
|
|
1724
|
-
if (hasSolana && wallet.solanaAddress && solUsdc > 0) console.error(` Solana: ${
|
|
2032
|
+
if (hasEvm && wallet.evmAddress && evmUsdc > 0) console.error(` Base: ${import_picocolors.default.cyan(wallet.evmAddress)} ${import_picocolors.default.dim(`(${formatAmount(evmUsdc, "USDC")})`)}`);
|
|
2033
|
+
if (hasMpp && wallet.evmAddress && tempoUsdc > 0) console.error(` Tempo: ${import_picocolors.default.cyan(wallet.evmAddress)} ${import_picocolors.default.dim(`(${formatAmount(tempoUsdc, "USDC")})`)}`);
|
|
2034
|
+
if (hasSolana && wallet.solanaAddress && solUsdc > 0) console.error(` Solana: ${import_picocolors.default.cyan(wallet.solanaAddress)} ${import_picocolors.default.dim(`(${formatAmount(solUsdc, "USDC")})`)}`);
|
|
1725
2035
|
console.error();
|
|
1726
2036
|
dim(" This may be a temporary server-side issue. Try again in a moment.");
|
|
1727
2037
|
console.error();
|
|
@@ -1731,16 +2041,16 @@ Examples:
|
|
|
1731
2041
|
console.error();
|
|
1732
2042
|
dim(" Fund your wallet with USDC:");
|
|
1733
2043
|
if (hasEvm && wallet.evmAddress) {
|
|
1734
|
-
const balHint = evmUsdc > 0 ?
|
|
1735
|
-
console.error(` Base: ${
|
|
2044
|
+
const balHint = evmUsdc > 0 ? import_picocolors.default.dim(` (${formatAmount(evmUsdc, "USDC")})`) : "";
|
|
2045
|
+
console.error(` Base: ${import_picocolors.default.cyan(wallet.evmAddress)}${balHint}`);
|
|
1736
2046
|
}
|
|
1737
2047
|
if (hasMpp && wallet.evmAddress) {
|
|
1738
|
-
const balHint = tempoUsdc > 0 ?
|
|
1739
|
-
console.error(` Tempo: ${
|
|
2048
|
+
const balHint = tempoUsdc > 0 ? import_picocolors.default.dim(` (${formatAmount(tempoUsdc, "USDC")})`) : "";
|
|
2049
|
+
console.error(` Tempo: ${import_picocolors.default.cyan(wallet.evmAddress)}${balHint}`);
|
|
1740
2050
|
}
|
|
1741
2051
|
if (hasSolana && wallet.solanaAddress) {
|
|
1742
|
-
const balHint = solUsdc > 0 ?
|
|
1743
|
-
console.error(` Solana: ${
|
|
2052
|
+
const balHint = solUsdc > 0 ? import_picocolors.default.dim(` (${formatAmount(solUsdc, "USDC")})`) : "";
|
|
2053
|
+
console.error(` Solana: ${import_picocolors.default.cyan(wallet.solanaAddress)}${balHint}`);
|
|
1744
2054
|
}
|
|
1745
2055
|
if (hasEvm && !wallet.evmAddress) dim(" Base: endpoint accepts EVM but no EVM wallet configured");
|
|
1746
2056
|
if (hasMpp && !wallet.evmAddress) dim(" Tempo: endpoint accepts MPP but no EVM wallet configured");
|
|
@@ -1752,7 +2062,7 @@ Examples:
|
|
|
1752
2062
|
}
|
|
1753
2063
|
console.error();
|
|
1754
2064
|
dim(" Then re-run:");
|
|
1755
|
-
console.error(` ${
|
|
2065
|
+
console.error(` ${import_picocolors.default.cyan(`$ npx x402-proxy ${url}`)}`);
|
|
1756
2066
|
console.error();
|
|
1757
2067
|
}
|
|
1758
2068
|
return;
|
|
@@ -1764,7 +2074,7 @@ Examples:
|
|
|
1764
2074
|
}
|
|
1765
2075
|
if (isTTY()) {
|
|
1766
2076
|
const statusText = ` ${response.status} ${response.statusText} (${elapsedMs}ms)`;
|
|
1767
|
-
const colorFn = response.status < 300 ?
|
|
2077
|
+
const colorFn = response.status < 300 ? import_picocolors.default.green : response.status < 400 ? import_picocolors.default.yellow : import_picocolors.default.red;
|
|
1768
2078
|
process.stderr.write(`${colorFn(statusText)}\n`);
|
|
1769
2079
|
}
|
|
1770
2080
|
if (x402Payment) {
|
|
@@ -1822,7 +2132,83 @@ Examples:
|
|
|
1822
2132
|
}
|
|
1823
2133
|
});
|
|
1824
2134
|
//#endregion
|
|
1825
|
-
//#region src/commands/mcp.ts
|
|
2135
|
+
//#region packages/x402-proxy/src/commands/mcp.ts
|
|
2136
|
+
const X402_PAYMENT_META_KEY = "x402/payment";
|
|
2137
|
+
const X402_PAYMENT_RESPONSE_META_KEY = "x402/payment-response";
|
|
2138
|
+
function cloneTool(tool) {
|
|
2139
|
+
return { ...tool };
|
|
2140
|
+
}
|
|
2141
|
+
function cloneResource(resource) {
|
|
2142
|
+
return { ...resource };
|
|
2143
|
+
}
|
|
2144
|
+
function normalizeCallToolResult(result) {
|
|
2145
|
+
return {
|
|
2146
|
+
content: result.content,
|
|
2147
|
+
...result.structuredContent !== void 0 ? { structuredContent: result.structuredContent } : {},
|
|
2148
|
+
...result.isError !== void 0 ? { isError: result.isError } : {},
|
|
2149
|
+
...result._meta !== void 0 ? { _meta: result._meta } : {}
|
|
2150
|
+
};
|
|
2151
|
+
}
|
|
2152
|
+
function isTextContent(content) {
|
|
2153
|
+
return content.type === "text" && typeof content.text === "string";
|
|
2154
|
+
}
|
|
2155
|
+
function isX402PaymentRequired(value) {
|
|
2156
|
+
if (typeof value !== "object" || value === null) return false;
|
|
2157
|
+
const candidate = value;
|
|
2158
|
+
return typeof candidate.x402Version === "number" && Array.isArray(candidate.accepts);
|
|
2159
|
+
}
|
|
2160
|
+
function extractPaymentRequiredFromObject(value) {
|
|
2161
|
+
if (isX402PaymentRequired(value)) return value;
|
|
2162
|
+
if (typeof value !== "object" || value === null) return void 0;
|
|
2163
|
+
const wrappedError = value["x402/error"];
|
|
2164
|
+
if (typeof wrappedError === "object" && wrappedError !== null) {
|
|
2165
|
+
const errorData = wrappedError.data;
|
|
2166
|
+
if (isX402PaymentRequired(errorData)) return errorData;
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
function extractPaymentRequiredFromResult(result) {
|
|
2170
|
+
if (!result.isError) return void 0;
|
|
2171
|
+
const structured = extractPaymentRequiredFromObject(result.structuredContent);
|
|
2172
|
+
if (structured) return structured;
|
|
2173
|
+
const first = result.content[0];
|
|
2174
|
+
if (!first || !isTextContent(first)) return void 0;
|
|
2175
|
+
try {
|
|
2176
|
+
return extractPaymentRequiredFromObject(JSON.parse(first.text));
|
|
2177
|
+
} catch {
|
|
2178
|
+
return;
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
function extractPaymentRequiredFromError(error) {
|
|
2182
|
+
if (typeof error !== "object" || error === null) return void 0;
|
|
2183
|
+
return error.data?.x402;
|
|
2184
|
+
}
|
|
2185
|
+
async function callX402ToolWithAutoPayment(opts) {
|
|
2186
|
+
let paymentRequired;
|
|
2187
|
+
try {
|
|
2188
|
+
const result = await opts.remoteClient.callTool({
|
|
2189
|
+
name: opts.name,
|
|
2190
|
+
arguments: opts.args
|
|
2191
|
+
});
|
|
2192
|
+
paymentRequired = extractPaymentRequiredFromResult(result);
|
|
2193
|
+
if (!paymentRequired) return result;
|
|
2194
|
+
} catch (error) {
|
|
2195
|
+
paymentRequired = extractPaymentRequiredFromError(error);
|
|
2196
|
+
if (!paymentRequired) throw error;
|
|
2197
|
+
}
|
|
2198
|
+
opts.onPaymentRequested(paymentRequired, opts.name);
|
|
2199
|
+
const paymentPayload = await opts.x402PaymentClient.createPaymentPayload(paymentRequired);
|
|
2200
|
+
const paidResult = await opts.remoteClient.callTool({
|
|
2201
|
+
name: opts.name,
|
|
2202
|
+
arguments: opts.args,
|
|
2203
|
+
_meta: { [X402_PAYMENT_META_KEY]: paymentPayload }
|
|
2204
|
+
});
|
|
2205
|
+
opts.onPaymentSettled({
|
|
2206
|
+
toolName: opts.name,
|
|
2207
|
+
paymentPayload,
|
|
2208
|
+
settleResponse: paidResult._meta?.[X402_PAYMENT_RESPONSE_META_KEY]
|
|
2209
|
+
});
|
|
2210
|
+
return paidResult;
|
|
2211
|
+
}
|
|
1826
2212
|
const mcpCommand = buildCommand({
|
|
1827
2213
|
docs: {
|
|
1828
2214
|
brief: "Start MCP stdio proxy with automatic payment",
|
|
@@ -1876,7 +2262,7 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
1876
2262
|
});
|
|
1877
2263
|
if (wallet.source === "none") {
|
|
1878
2264
|
dim("No wallet found. Auto-generating...");
|
|
1879
|
-
const { runSetup } = await import("../setup-
|
|
2265
|
+
const { runSetup } = await import("../setup-wMOAgrsN.js");
|
|
1880
2266
|
await runSetup({ nonInteractive: true });
|
|
1881
2267
|
const fresh = resolveWallet({
|
|
1882
2268
|
evmKey: flags.evmKey,
|
|
@@ -1893,12 +2279,12 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
1893
2279
|
if (wallet.solanaAddress) dim(` Solana: ${wallet.solanaAddress}`);
|
|
1894
2280
|
const config = loadConfig();
|
|
1895
2281
|
const resolvedProtocol = flags.protocol ?? config?.preferredProtocol ?? "x402";
|
|
1896
|
-
const { Client } = await import("
|
|
1897
|
-
const { SSEClientTransport } = await import("
|
|
1898
|
-
const { StreamableHTTPClientTransport } = await import("
|
|
1899
|
-
const { Server } = await import("
|
|
1900
|
-
const { StdioServerTransport } = await import("
|
|
1901
|
-
const { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ToolListChangedNotificationSchema, ResourceListChangedNotificationSchema
|
|
2282
|
+
const { Client } = await import("../client-Blw2V7LF.js");
|
|
2283
|
+
const { SSEClientTransport } = await import("../sse-B4LLqBQm.js");
|
|
2284
|
+
const { StreamableHTTPClientTransport } = await import("../streamableHttp-BHkJypcI.js");
|
|
2285
|
+
const { Server } = await import("../server-CSq0IuUq.js");
|
|
2286
|
+
const { StdioServerTransport } = await import("../stdio-BADqxZdZ.js");
|
|
2287
|
+
const { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ToolListChangedNotificationSchema, ResourceListChangedNotificationSchema } = await import("../types-DatK5vR5.js");
|
|
1902
2288
|
async function connectTransport(target) {
|
|
1903
2289
|
try {
|
|
1904
2290
|
const transport = new StreamableHTTPClientTransport(new URL(remoteUrl));
|
|
@@ -1920,7 +2306,7 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
1920
2306
|
async function startX402Proxy() {
|
|
1921
2307
|
let preferredNetwork = config?.defaultNetwork;
|
|
1922
2308
|
if (!preferredNetwork && wallet.evmAddress && wallet.solanaAddress) {
|
|
1923
|
-
const { fetchAllBalances } = await import("../wallet-
|
|
2309
|
+
const { fetchAllBalances } = await import("../wallet-DKVlrR1S.js");
|
|
1924
2310
|
const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
|
|
1925
2311
|
const evmUsdc = balances.evm ? Number(balances.evm.usdc) : 0;
|
|
1926
2312
|
const solUsdc = balances.sol ? Number(balances.sol.usdc) : 0;
|
|
@@ -1933,23 +2319,16 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
1933
2319
|
spendLimitDaily: config?.spendLimitDaily,
|
|
1934
2320
|
spendLimitPerTx: config?.spendLimitPerTx
|
|
1935
2321
|
});
|
|
1936
|
-
const { x402MCPClient } = await import("@x402/mcp");
|
|
1937
2322
|
function warnPayment(accepts, toolName) {
|
|
1938
2323
|
const accept = accepts?.[0];
|
|
1939
2324
|
if (accept) warn(` Payment: ${accept.amount ? formatAmount(Number(accept.amount) / 1e6, "USDC") : "? USDC"} on ${displayNetwork(accept.network)} for tool "${toolName}"`);
|
|
1940
2325
|
}
|
|
1941
2326
|
const remoteClient = new Client({
|
|
1942
2327
|
name: "x402-proxy",
|
|
1943
|
-
version: "0.10.
|
|
2328
|
+
version: "0.10.9"
|
|
1944
2329
|
});
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
onPaymentRequested: (ctx) => {
|
|
1948
|
-
warnPayment(ctx.paymentRequired.accepts, ctx.toolName);
|
|
1949
|
-
return true;
|
|
1950
|
-
}
|
|
1951
|
-
});
|
|
1952
|
-
x402Mcp.onAfterPayment(async (ctx) => {
|
|
2330
|
+
await connectTransport(remoteClient);
|
|
2331
|
+
function recordX402Payment(ctx) {
|
|
1953
2332
|
const accepted = ctx.paymentPayload.accepted;
|
|
1954
2333
|
const tx = ctx.settleResponse?.transaction;
|
|
1955
2334
|
const record = {
|
|
@@ -1965,73 +2344,50 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
1965
2344
|
label: `mcp:${ctx.toolName}`
|
|
1966
2345
|
};
|
|
1967
2346
|
appendHistory(getHistoryPath(), record);
|
|
1968
|
-
}
|
|
1969
|
-
await
|
|
1970
|
-
let { tools } = await x402Mcp.listTools();
|
|
2347
|
+
}
|
|
2348
|
+
let { tools } = await remoteClient.listTools();
|
|
1971
2349
|
dim(` ${tools.length} tools available`);
|
|
1972
2350
|
let remoteResources = [];
|
|
1973
2351
|
try {
|
|
1974
|
-
remoteResources = (await
|
|
2352
|
+
remoteResources = (await remoteClient.listResources()).resources;
|
|
1975
2353
|
if (remoteResources.length > 0) dim(` ${remoteResources.length} resources available`);
|
|
1976
2354
|
} catch {
|
|
1977
2355
|
dim(" Resources not available from remote");
|
|
1978
2356
|
}
|
|
1979
2357
|
const localServer = new Server({
|
|
1980
2358
|
name: "x402-proxy",
|
|
1981
|
-
version: "0.10.
|
|
2359
|
+
version: "0.10.9"
|
|
1982
2360
|
}, { capabilities: {
|
|
1983
2361
|
tools: tools.length > 0 ? {} : void 0,
|
|
1984
2362
|
resources: remoteResources.length > 0 ? {} : void 0
|
|
1985
2363
|
} });
|
|
1986
|
-
localServer.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: tools.map((t) => (
|
|
1987
|
-
name: t.name,
|
|
1988
|
-
description: t.description,
|
|
1989
|
-
inputSchema: t.inputSchema,
|
|
1990
|
-
annotations: t.annotations
|
|
1991
|
-
})) }));
|
|
2364
|
+
localServer.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: tools.map((t) => cloneTool(t)) }));
|
|
1992
2365
|
localServer.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
1993
2366
|
const { name, arguments: args } = request.params;
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
const accepts = x402PaymentRequired.accepts;
|
|
2005
|
-
warnPayment(accepts, name);
|
|
2006
|
-
const paymentPayload = await x402PaymentClient.createPaymentPayload(x402PaymentRequired);
|
|
2007
|
-
const result = await x402Mcp.callToolWithPayment(name, args ?? {}, paymentPayload);
|
|
2008
|
-
return {
|
|
2009
|
-
content: result.content,
|
|
2010
|
-
isError: result.isError
|
|
2011
|
-
};
|
|
2012
|
-
}
|
|
2013
|
-
}
|
|
2014
|
-
throw err;
|
|
2015
|
-
}
|
|
2367
|
+
return normalizeCallToolResult(await callX402ToolWithAutoPayment({
|
|
2368
|
+
remoteClient,
|
|
2369
|
+
name,
|
|
2370
|
+
args: args ?? {},
|
|
2371
|
+
x402PaymentClient,
|
|
2372
|
+
onPaymentRequested: (paymentRequired, toolName) => {
|
|
2373
|
+
warnPayment(paymentRequired.accepts, toolName);
|
|
2374
|
+
},
|
|
2375
|
+
onPaymentSettled: recordX402Payment
|
|
2376
|
+
}));
|
|
2016
2377
|
});
|
|
2017
2378
|
if (remoteResources.length > 0) {
|
|
2018
|
-
localServer.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: remoteResources.map((r) => (
|
|
2019
|
-
name: r.name,
|
|
2020
|
-
uri: r.uri,
|
|
2021
|
-
description: r.description,
|
|
2022
|
-
mimeType: r.mimeType
|
|
2023
|
-
})) }));
|
|
2379
|
+
localServer.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: remoteResources.map((r) => cloneResource(r)) }));
|
|
2024
2380
|
localServer.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
2025
|
-
return { contents: (await
|
|
2381
|
+
return { contents: (await remoteClient.readResource({ uri: request.params.uri })).contents.map((c) => ({ ...c })) };
|
|
2026
2382
|
});
|
|
2027
2383
|
}
|
|
2028
2384
|
remoteClient.setNotificationHandler(ToolListChangedNotificationSchema, async () => {
|
|
2029
|
-
tools = (await
|
|
2385
|
+
tools = (await remoteClient.listTools()).tools;
|
|
2030
2386
|
dim(` Tools updated: ${tools.length} available`);
|
|
2031
2387
|
await localServer.notification({ method: "notifications/tools/list_changed" });
|
|
2032
2388
|
});
|
|
2033
2389
|
if (remoteResources.length > 0) remoteClient.setNotificationHandler(ResourceListChangedNotificationSchema, async () => {
|
|
2034
|
-
remoteResources = (await
|
|
2390
|
+
remoteResources = (await remoteClient.listResources()).resources;
|
|
2035
2391
|
dim(` Resources updated: ${remoteResources.length} available`);
|
|
2036
2392
|
await localServer.notification({ method: "notifications/resources/list_changed" });
|
|
2037
2393
|
});
|
|
@@ -2042,7 +2398,7 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
2042
2398
|
const cleanup = async () => {
|
|
2043
2399
|
if (closing) return;
|
|
2044
2400
|
closing = true;
|
|
2045
|
-
await
|
|
2401
|
+
await remoteClient.close();
|
|
2046
2402
|
process.exit(0);
|
|
2047
2403
|
};
|
|
2048
2404
|
process.stdin.on("end", cleanup);
|
|
@@ -2054,9 +2410,9 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
2054
2410
|
error("MPP requires an EVM wallet. Configure one with: npx x402-proxy setup");
|
|
2055
2411
|
process.exit(1);
|
|
2056
2412
|
}
|
|
2057
|
-
const { tempo } = await import("
|
|
2058
|
-
const { McpClient } = await import("
|
|
2059
|
-
const { privateKeyToAccount } = await import("
|
|
2413
|
+
const { tempo } = await import("../client-CVDTUY0l.js");
|
|
2414
|
+
const { McpClient } = await import("../client-C37gWJOZ.js");
|
|
2415
|
+
const { privateKeyToAccount } = await import("../accounts-DzvAlQRn.js");
|
|
2060
2416
|
const account = privateKeyToAccount(wallet.evmKey);
|
|
2061
2417
|
const maxDeposit = config?.mppSessionBudget ?? "1";
|
|
2062
2418
|
let lastChallengeAmount;
|
|
@@ -2073,7 +2429,7 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
2073
2429
|
}));
|
|
2074
2430
|
const remoteClient = new Client({
|
|
2075
2431
|
name: "x402-proxy",
|
|
2076
|
-
version: "0.10.
|
|
2432
|
+
version: "0.10.9"
|
|
2077
2433
|
});
|
|
2078
2434
|
await connectTransport(remoteClient);
|
|
2079
2435
|
const mppClient = McpClient.wrap(remoteClient, { methods: wrappedMethods });
|
|
@@ -2088,17 +2444,12 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
2088
2444
|
}
|
|
2089
2445
|
const localServer = new Server({
|
|
2090
2446
|
name: "x402-proxy",
|
|
2091
|
-
version: "0.10.
|
|
2447
|
+
version: "0.10.9"
|
|
2092
2448
|
}, { capabilities: {
|
|
2093
2449
|
tools: tools.length > 0 ? {} : void 0,
|
|
2094
2450
|
resources: remoteResources.length > 0 ? {} : void 0
|
|
2095
2451
|
} });
|
|
2096
|
-
localServer.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: tools.map((t) => (
|
|
2097
|
-
name: t.name,
|
|
2098
|
-
description: t.description,
|
|
2099
|
-
inputSchema: t.inputSchema,
|
|
2100
|
-
annotations: t.annotations
|
|
2101
|
-
})) }));
|
|
2452
|
+
localServer.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: tools.map((t) => cloneTool(t)) }));
|
|
2102
2453
|
localServer.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
2103
2454
|
const { name, arguments: args } = request.params;
|
|
2104
2455
|
const result = await mppClient.callTool({
|
|
@@ -2122,18 +2473,10 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
2122
2473
|
warn(` MPP payment for tool "${name}" (Tempo)${amountStr ? ` \u00b7 ${amountStr}` : ""}`);
|
|
2123
2474
|
lastChallengeAmount = void 0;
|
|
2124
2475
|
}
|
|
2125
|
-
return
|
|
2126
|
-
content: result.content,
|
|
2127
|
-
isError: result.isError
|
|
2128
|
-
};
|
|
2476
|
+
return normalizeCallToolResult(result);
|
|
2129
2477
|
});
|
|
2130
2478
|
if (remoteResources.length > 0) {
|
|
2131
|
-
localServer.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: remoteResources.map((r) => (
|
|
2132
|
-
name: r.name,
|
|
2133
|
-
uri: r.uri,
|
|
2134
|
-
description: r.description,
|
|
2135
|
-
mimeType: r.mimeType
|
|
2136
|
-
})) }));
|
|
2479
|
+
localServer.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: remoteResources.map((r) => cloneResource(r)) }));
|
|
2137
2480
|
localServer.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
2138
2481
|
return { contents: (await remoteClient.readResource({ uri: request.params.uri })).contents.map((c) => ({ ...c })) };
|
|
2139
2482
|
});
|
|
@@ -2165,7 +2508,7 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
2165
2508
|
}
|
|
2166
2509
|
});
|
|
2167
2510
|
//#endregion
|
|
2168
|
-
//#region src/commands/mcp-add.ts
|
|
2511
|
+
//#region packages/x402-proxy/src/commands/mcp-add.ts
|
|
2169
2512
|
function resolvePlatformPath(raw) {
|
|
2170
2513
|
let p = raw;
|
|
2171
2514
|
if (p.startsWith("~/")) p = join(homedir(), p.slice(2));
|
|
@@ -2215,45 +2558,45 @@ const mcpAddCommand = buildCommand({
|
|
|
2215
2558
|
}
|
|
2216
2559
|
},
|
|
2217
2560
|
async func(flags, name, url) {
|
|
2218
|
-
|
|
2561
|
+
Wt(import_picocolors.default.cyan("Add MCP server"));
|
|
2219
2562
|
try {
|
|
2220
2563
|
new URL(url);
|
|
2221
2564
|
} catch {
|
|
2222
|
-
|
|
2223
|
-
|
|
2565
|
+
R.error(`Invalid URL: ${url}`);
|
|
2566
|
+
Nt("Aborted.");
|
|
2224
2567
|
process.exit(1);
|
|
2225
2568
|
}
|
|
2226
2569
|
const serverName = name;
|
|
2227
2570
|
if (!isConfigured()) {
|
|
2228
|
-
|
|
2571
|
+
R.warn("No wallet configured. Let's set one up first.\n");
|
|
2229
2572
|
await runSetup();
|
|
2230
2573
|
console.log();
|
|
2231
|
-
|
|
2574
|
+
R.step(import_picocolors.default.cyan("Continuing MCP setup..."));
|
|
2232
2575
|
}
|
|
2233
|
-
const { generators, getAppIds, generateConfig, deepMerge } = await import("
|
|
2576
|
+
const { generators, getAppIds, generateConfig, deepMerge } = await import("../dist-DxJCYyL5.js");
|
|
2234
2577
|
let clientId;
|
|
2235
2578
|
if (flags.client) {
|
|
2236
2579
|
const appIds = getAppIds();
|
|
2237
2580
|
if (!appIds.includes(flags.client)) {
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2581
|
+
R.error(`Unknown client: ${flags.client}`);
|
|
2582
|
+
R.info(`Supported: ${appIds.join(", ")}`);
|
|
2583
|
+
Nt("Aborted.");
|
|
2241
2584
|
process.exit(1);
|
|
2242
2585
|
}
|
|
2243
2586
|
clientId = flags.client;
|
|
2244
2587
|
} else {
|
|
2245
2588
|
const appIds = getAppIds();
|
|
2246
2589
|
const detected = appIds.filter((id) => generators[id].detectInstalled());
|
|
2247
|
-
const selected = await
|
|
2590
|
+
const selected = await Jt({
|
|
2248
2591
|
message: "Where would you like to install the MCP server?",
|
|
2249
2592
|
options: appIds.map((id) => ({
|
|
2250
2593
|
value: id,
|
|
2251
|
-
label: `${generators[id].app.name}${detected.includes(id) ?
|
|
2594
|
+
label: `${generators[id].app.name}${detected.includes(id) ? import_picocolors.default.dim(" (detected)") : ""}`
|
|
2252
2595
|
})),
|
|
2253
2596
|
initialValue: detected.includes("claude-code") ? "claude-code" : detected[0] ?? "claude-code"
|
|
2254
2597
|
});
|
|
2255
|
-
if (
|
|
2256
|
-
|
|
2598
|
+
if (Ct(selected)) {
|
|
2599
|
+
Nt("Cancelled.");
|
|
2257
2600
|
process.exit(0);
|
|
2258
2601
|
}
|
|
2259
2602
|
clientId = selected;
|
|
@@ -2263,8 +2606,8 @@ const mcpAddCommand = buildCommand({
|
|
|
2263
2606
|
const platform = process.platform;
|
|
2264
2607
|
const rawPath = globalPaths?.[platform];
|
|
2265
2608
|
if (!rawPath) {
|
|
2266
|
-
|
|
2267
|
-
|
|
2609
|
+
R.error(`No global config path for ${generator.app.name} on ${platform}`);
|
|
2610
|
+
Nt("Aborted.");
|
|
2268
2611
|
process.exit(1);
|
|
2269
2612
|
}
|
|
2270
2613
|
const configPath = resolvePlatformPath(rawPath);
|
|
@@ -2283,7 +2626,7 @@ const mcpAddCommand = buildCommand({
|
|
|
2283
2626
|
});
|
|
2284
2627
|
let existing;
|
|
2285
2628
|
if (configFormat === "yaml") {
|
|
2286
|
-
const { default: YAML } = await import("
|
|
2629
|
+
const { default: YAML } = await import("../dist-DM5_F3r5.js").then((m) => /* @__PURE__ */ __toESM(m.default, 1));
|
|
2287
2630
|
if (existsSync(configPath)) {
|
|
2288
2631
|
const raw = readFileSync(configPath, "utf-8").trim();
|
|
2289
2632
|
existing = raw ? YAML.parse(raw) ?? {} : {};
|
|
@@ -2292,27 +2635,27 @@ const mcpAddCommand = buildCommand({
|
|
|
2292
2635
|
const rootKey = Object.keys(generated)[0] ?? "mcpServers";
|
|
2293
2636
|
const existingServers = existing[rootKey] ?? {};
|
|
2294
2637
|
if (existingServers[serverName]) {
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
const overwrite = await
|
|
2298
|
-
if (
|
|
2299
|
-
|
|
2638
|
+
R.warn(`Server ${import_picocolors.default.bold(serverName)} already exists in config`);
|
|
2639
|
+
R.message(import_picocolors.default.dim(JSON.stringify({ [serverName]: existingServers[serverName] }, null, 2)));
|
|
2640
|
+
const overwrite = await Rt({ message: "Overwrite?" });
|
|
2641
|
+
if (Ct(overwrite) || !overwrite) {
|
|
2642
|
+
Nt("Cancelled.");
|
|
2300
2643
|
process.exit(0);
|
|
2301
2644
|
}
|
|
2302
2645
|
}
|
|
2303
|
-
|
|
2646
|
+
R.info(`Config will be added to ${import_picocolors.default.dim(configPath)}`);
|
|
2304
2647
|
const previewLines = generator.serialize(generated).split("\n");
|
|
2305
2648
|
const formatted = previewLines.map((line, i) => {
|
|
2306
2649
|
if (i === 0 || i === previewLines.length - 1) return line;
|
|
2307
2650
|
const trimmed = line.trimStart();
|
|
2308
2651
|
if (trimmed.startsWith(`"${rootKey}"`) || trimmed.startsWith(`${rootKey}:`)) return line;
|
|
2309
|
-
return `${
|
|
2652
|
+
return `${import_picocolors.default.green("+")} ${line}`;
|
|
2310
2653
|
}).join("\n");
|
|
2311
|
-
|
|
2654
|
+
R.message(formatted);
|
|
2312
2655
|
if (!flags.yes) {
|
|
2313
|
-
const proceed = await
|
|
2314
|
-
if (
|
|
2315
|
-
|
|
2656
|
+
const proceed = await Rt({ message: "Would you like to proceed?" });
|
|
2657
|
+
if (Ct(proceed) || !proceed) {
|
|
2658
|
+
Nt("Cancelled.");
|
|
2316
2659
|
process.exit(0);
|
|
2317
2660
|
}
|
|
2318
2661
|
}
|
|
@@ -2321,7 +2664,7 @@ const mcpAddCommand = buildCommand({
|
|
|
2321
2664
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
2322
2665
|
const serialized = generator.serialize(merged);
|
|
2323
2666
|
writeFileSync(configPath, serialized.endsWith("\n") ? serialized : `${serialized}\n`);
|
|
2324
|
-
|
|
2667
|
+
R.success(`Added ${import_picocolors.default.bold(serverName)} MCP to ${import_picocolors.default.bold(generator.app.name)}`);
|
|
2325
2668
|
const wallet = resolveWallet();
|
|
2326
2669
|
if (wallet.source !== "none") {
|
|
2327
2670
|
const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
|
|
@@ -2329,26 +2672,26 @@ const mcpAddCommand = buildCommand({
|
|
|
2329
2672
|
const solUsdc = balances.sol ? Number(balances.sol.usdc) : 0;
|
|
2330
2673
|
const tempoUsdc = balances.tempo ? Number(balances.tempo.usdc) : 0;
|
|
2331
2674
|
if (evmUsdc === 0 && solUsdc === 0 && tempoUsdc === 0) {
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
if (wallet.evmAddress)
|
|
2335
|
-
if (wallet.solanaAddress)
|
|
2675
|
+
R.warn("Balance: 0 USDC");
|
|
2676
|
+
R.info("To use paid MCP tools, send USDC to your wallet:");
|
|
2677
|
+
if (wallet.evmAddress) R.message(` Base: ${import_picocolors.default.cyan(wallet.evmAddress)}`);
|
|
2678
|
+
if (wallet.solanaAddress) R.message(` Solana: ${import_picocolors.default.cyan(wallet.solanaAddress)}`);
|
|
2336
2679
|
} else {
|
|
2337
2680
|
const parts = [];
|
|
2338
2681
|
if (balances.evm) parts.push(`Base: ${balances.evm.usdc} USDC`);
|
|
2339
2682
|
if (balances.sol) parts.push(`Solana: ${balances.sol.usdc} USDC`);
|
|
2340
2683
|
if (balances.tempo) parts.push(`Tempo: ${balances.tempo.usdc} USDC`);
|
|
2341
|
-
|
|
2684
|
+
R.success(`Balance: ${parts.join(" | ")}`);
|
|
2342
2685
|
}
|
|
2343
2686
|
}
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2687
|
+
R.step("Try your first request:");
|
|
2688
|
+
R.message(` ${import_picocolors.default.cyan(`$ npx x402-proxy -X POST -d '{"ref":"CoinbaseDev"}' https://surf.cascade.fyi/api/v1/twitter/user`)}`);
|
|
2689
|
+
R.message(` ${import_picocolors.default.dim("Run")} ${import_picocolors.default.cyan("npx x402-proxy")} ${import_picocolors.default.dim("to see your wallet and balance")}`);
|
|
2690
|
+
Gt(import_picocolors.default.green(`MCP server ${import_picocolors.default.bold(serverName)} is ready to use!`));
|
|
2348
2691
|
}
|
|
2349
2692
|
});
|
|
2350
2693
|
//#endregion
|
|
2351
|
-
//#region src/commands/wallet-export.ts
|
|
2694
|
+
//#region packages/x402-proxy/src/commands/wallet-export.ts
|
|
2352
2695
|
const walletExportCommand = buildCommand({
|
|
2353
2696
|
docs: { brief: "Export private key or mnemonic to stdout (pipe-safe)" },
|
|
2354
2697
|
parameters: {
|
|
@@ -2373,8 +2716,8 @@ const walletExportCommand = buildCommand({
|
|
|
2373
2716
|
process.exit(1);
|
|
2374
2717
|
}
|
|
2375
2718
|
if (process.stdout.isTTY) {
|
|
2376
|
-
const confirmed = await
|
|
2377
|
-
if (
|
|
2719
|
+
const confirmed = await Rt({ message: "This will print your mnemonic to the terminal. Continue?" });
|
|
2720
|
+
if (Ct(confirmed) || !confirmed) process.exit(0);
|
|
2378
2721
|
} else warn("Warning: mnemonic will be printed to stdout.");
|
|
2379
2722
|
process.stdout.write(mnemonic);
|
|
2380
2723
|
return;
|
|
@@ -2393,15 +2736,15 @@ const walletExportCommand = buildCommand({
|
|
|
2393
2736
|
process.exit(1);
|
|
2394
2737
|
}
|
|
2395
2738
|
if (process.stdout.isTTY) {
|
|
2396
|
-
const confirmed = await
|
|
2397
|
-
if (
|
|
2739
|
+
const confirmed = await Rt({ message: "This will print your private key to the terminal. Continue?" });
|
|
2740
|
+
if (Ct(confirmed) || !confirmed) process.exit(0);
|
|
2398
2741
|
} else warn("Warning: private key will be printed to stdout.");
|
|
2399
2742
|
if (chain === "evm") process.stdout.write(wallet.evmKey);
|
|
2400
2743
|
else process.stdout.write(base58.encode(wallet.solanaKey.slice(0, 32)));
|
|
2401
2744
|
}
|
|
2402
2745
|
});
|
|
2403
2746
|
//#endregion
|
|
2404
|
-
//#region src/app.ts
|
|
2747
|
+
//#region packages/x402-proxy/src/app.ts
|
|
2405
2748
|
const walletRoutes = buildRouteMap({
|
|
2406
2749
|
routes: {
|
|
2407
2750
|
info: walletInfoCommand,
|
|
@@ -2429,7 +2772,7 @@ const walletRoutes = buildRouteMap({
|
|
|
2429
2772
|
func(flags) {
|
|
2430
2773
|
const records = readHistory(getHistoryPath());
|
|
2431
2774
|
if (records.length === 0) {
|
|
2432
|
-
console.log(
|
|
2775
|
+
console.log(import_picocolors.default.dim("No payment history yet."));
|
|
2433
2776
|
return;
|
|
2434
2777
|
}
|
|
2435
2778
|
if (flags.json) {
|
|
@@ -2447,7 +2790,7 @@ const walletRoutes = buildRouteMap({
|
|
|
2447
2790
|
console.log(line);
|
|
2448
2791
|
}
|
|
2449
2792
|
console.log();
|
|
2450
|
-
console.log(
|
|
2793
|
+
console.log(import_picocolors.default.dim(` Today: ${formatAmount(spend.today, "USDC")} | Total: ${formatAmount(spend.total, "USDC")} | ${spend.count} transactions`));
|
|
2451
2794
|
console.log();
|
|
2452
2795
|
}
|
|
2453
2796
|
}),
|
|
@@ -2487,16 +2830,16 @@ const app = buildApplication(buildRouteMap({
|
|
|
2487
2830
|
docs: { brief: "curl for x402 paid APIs" }
|
|
2488
2831
|
}), {
|
|
2489
2832
|
name: "x402-proxy",
|
|
2490
|
-
versionInfo: { currentVersion: "0.10.
|
|
2833
|
+
versionInfo: { currentVersion: "0.10.9" },
|
|
2491
2834
|
scanner: { caseStyle: "allow-kebab-for-camel" }
|
|
2492
2835
|
});
|
|
2493
2836
|
//#endregion
|
|
2494
|
-
//#region src/context.ts
|
|
2837
|
+
//#region packages/x402-proxy/src/context.ts
|
|
2495
2838
|
function buildContext(process) {
|
|
2496
2839
|
return { process };
|
|
2497
2840
|
}
|
|
2498
2841
|
//#endregion
|
|
2499
|
-
//#region src/bin/cli.ts
|
|
2842
|
+
//#region packages/x402-proxy/src/bin/cli.ts
|
|
2500
2843
|
const rawArgs = process.argv.slice(2);
|
|
2501
2844
|
const args = [];
|
|
2502
2845
|
for (let i = 0; i < rawArgs.length; i++) {
|