x402-proxy 0.10.6 → 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 +30 -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 +608 -244
- 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,20 +1390,38 @@ 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";
|
|
1395
|
+
const modelList = [
|
|
1396
|
+
"stepfun/step-3.5-flash",
|
|
1397
|
+
"minimax/minimax-m2.5",
|
|
1398
|
+
"minimax/minimax-m2.7",
|
|
1399
|
+
"z-ai/glm-5",
|
|
1400
|
+
"z-ai/glm-5-turbo",
|
|
1401
|
+
"moonshotai/kimi-k2.5"
|
|
1402
|
+
].map((id) => ` ${id}`).join("\n");
|
|
1086
1403
|
function normalizeClaudeArgs(args) {
|
|
1087
1404
|
return args[0] === "--" ? args.slice(1) : args;
|
|
1088
1405
|
}
|
|
1089
1406
|
const claudeCommand = buildCommand({
|
|
1090
1407
|
docs: {
|
|
1091
1408
|
brief: "Run Claude Code through a paid local proxy",
|
|
1092
|
-
fullDescription: `
|
|
1409
|
+
fullDescription: `Starts a local x402-proxy server and launches Claude Code with
|
|
1410
|
+
ANTHROPIC_BASE_URL pointed at it. All inference requests go through
|
|
1411
|
+
the proxy, which handles payments automatically via MPP.
|
|
1093
1412
|
|
|
1094
|
-
|
|
1095
|
-
$ x402-proxy claude
|
|
1096
|
-
$ x402-proxy claude --model z-ai/glm-5
|
|
1097
|
-
$ x402-proxy claude -- --print "explain this
|
|
1413
|
+
Usage:
|
|
1414
|
+
$ x402-proxy claude Start with default model
|
|
1415
|
+
$ x402-proxy claude --model z-ai/glm-5 Use a specific model
|
|
1416
|
+
$ x402-proxy claude -- --print "explain this" Pass args to Claude Code
|
|
1417
|
+
$ x402-proxy claude -- -p "summarize *.ts" Print mode (non-interactive)
|
|
1418
|
+
|
|
1419
|
+
Available models (via surf.cascade.fyi):
|
|
1420
|
+
${modelList}
|
|
1421
|
+
|
|
1422
|
+
The --model value is passed as ANTHROPIC_MODEL and ANTHROPIC_CUSTOM_MODEL_OPTION
|
|
1423
|
+
to Claude Code. Any model supported by the upstream endpoint will work, even if
|
|
1424
|
+
not listed above.`
|
|
1098
1425
|
},
|
|
1099
1426
|
parameters: {
|
|
1100
1427
|
flags: {
|
|
@@ -1199,7 +1526,8 @@ Examples:
|
|
|
1199
1526
|
}
|
|
1200
1527
|
});
|
|
1201
1528
|
//#endregion
|
|
1202
|
-
//#region src/commands/config.ts
|
|
1529
|
+
//#region packages/x402-proxy/src/commands/config.ts
|
|
1530
|
+
var import_picocolors = /* @__PURE__ */ __toESM(require_picocolors(), 1);
|
|
1203
1531
|
const VALID_KEYS = {
|
|
1204
1532
|
defaultNetwork: {
|
|
1205
1533
|
description: "Preferred network (base, solana, tempo)",
|
|
@@ -1259,23 +1587,23 @@ const configShowCommand = buildCommand({
|
|
|
1259
1587
|
async func() {
|
|
1260
1588
|
const config = loadConfig();
|
|
1261
1589
|
console.log();
|
|
1262
|
-
console.log(
|
|
1590
|
+
console.log(import_picocolors.default.bold("Configuration"));
|
|
1263
1591
|
dim(` ${getConfigDirShort()}/config.yaml`);
|
|
1264
1592
|
console.log();
|
|
1265
1593
|
if (!config || Object.keys(config).length === 0) {
|
|
1266
1594
|
dim(" No configuration set. Using defaults.");
|
|
1267
1595
|
console.log();
|
|
1268
1596
|
dim(" Available keys:");
|
|
1269
|
-
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}`);
|
|
1270
1598
|
console.log();
|
|
1271
|
-
dim(` Set with: ${
|
|
1599
|
+
dim(` Set with: ${import_picocolors.default.cyan("npx x402-proxy config set <key> <value>")}`);
|
|
1272
1600
|
console.log();
|
|
1273
1601
|
return;
|
|
1274
1602
|
}
|
|
1275
1603
|
for (const key of Object.keys(VALID_KEYS)) {
|
|
1276
1604
|
const value = config[key];
|
|
1277
|
-
if (value !== void 0) console.log(` ${
|
|
1278
|
-
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)")}`);
|
|
1279
1607
|
}
|
|
1280
1608
|
console.log();
|
|
1281
1609
|
}
|
|
@@ -1308,7 +1636,7 @@ To unset a value, use: npx x402-proxy config unset <key>`
|
|
|
1308
1636
|
error(`Unknown config key: ${key}`);
|
|
1309
1637
|
console.error();
|
|
1310
1638
|
dim(" Available keys:");
|
|
1311
|
-
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}`);
|
|
1312
1640
|
process.exit(1);
|
|
1313
1641
|
}
|
|
1314
1642
|
const meta = VALID_KEYS[key];
|
|
@@ -1322,7 +1650,7 @@ To unset a value, use: npx x402-proxy config unset <key>`
|
|
|
1322
1650
|
const config = loadConfig() ?? {};
|
|
1323
1651
|
config[key] = parsed;
|
|
1324
1652
|
saveConfig(config);
|
|
1325
|
-
console.log(` ${
|
|
1653
|
+
console.log(` ${import_picocolors.default.cyan(key)} = ${import_picocolors.default.green(String(parsed))}`);
|
|
1326
1654
|
}
|
|
1327
1655
|
});
|
|
1328
1656
|
const configUnsetCommand = buildCommand({
|
|
@@ -1349,7 +1677,7 @@ const configUnsetCommand = buildCommand({
|
|
|
1349
1677
|
}
|
|
1350
1678
|
});
|
|
1351
1679
|
//#endregion
|
|
1352
|
-
//#region src/commands/fetch.ts
|
|
1680
|
+
//#region packages/x402-proxy/src/commands/fetch.ts
|
|
1353
1681
|
function isStreamingResponse(res) {
|
|
1354
1682
|
return (res.headers.get("content-type") ?? "").includes("text/event-stream");
|
|
1355
1683
|
}
|
|
@@ -1447,36 +1775,39 @@ Examples:
|
|
|
1447
1775
|
};
|
|
1448
1776
|
if (!url) {
|
|
1449
1777
|
if (isConfigured()) {
|
|
1450
|
-
const { displayStatus } = await import("../status-
|
|
1778
|
+
const { displayStatus } = await import("../status-Bu23RjW6.js");
|
|
1451
1779
|
await displayStatus();
|
|
1452
1780
|
console.log();
|
|
1453
|
-
console.log(
|
|
1454
|
-
console.log(` ${
|
|
1455
|
-
console.log(` ${
|
|
1456
|
-
console.log(` ${
|
|
1457
|
-
console.log(` ${
|
|
1458
|
-
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`);
|
|
1459
1789
|
console.log();
|
|
1460
|
-
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`));
|
|
1461
1791
|
console.log();
|
|
1462
|
-
console.log(
|
|
1792
|
+
console.log(import_picocolors.default.dim(" https://github.com/cascade-protocol/x402-proxy"));
|
|
1463
1793
|
console.log();
|
|
1464
1794
|
} else {
|
|
1465
1795
|
console.log();
|
|
1466
|
-
console.log(
|
|
1467
|
-
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"));
|
|
1468
1798
|
console.log();
|
|
1469
|
-
console.log(
|
|
1470
|
-
console.log(` ${
|
|
1471
|
-
console.log(` ${
|
|
1472
|
-
console.log(` ${
|
|
1473
|
-
console.log(` ${
|
|
1474
|
-
console.log(` ${
|
|
1475
|
-
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`);
|
|
1476
1807
|
console.log();
|
|
1477
|
-
console.log(
|
|
1808
|
+
console.log(import_picocolors.default.dim(" try: ") + import_picocolors.default.cyan("$ npx x402-proxy setup"));
|
|
1478
1809
|
console.log();
|
|
1479
|
-
console.log(
|
|
1810
|
+
console.log(import_picocolors.default.dim(" https://github.com/cascade-protocol/x402-proxy"));
|
|
1480
1811
|
console.log();
|
|
1481
1812
|
}
|
|
1482
1813
|
return;
|
|
@@ -1495,11 +1826,11 @@ Examples:
|
|
|
1495
1826
|
if (wallet.source === "none") {
|
|
1496
1827
|
if (!isTTY()) {
|
|
1497
1828
|
error("No wallet configured.");
|
|
1498
|
-
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`));
|
|
1499
1830
|
process.exit(1);
|
|
1500
1831
|
}
|
|
1501
1832
|
dim(" No wallet found. Let's set one up first.\n");
|
|
1502
|
-
const { runSetup } = await import("../setup-
|
|
1833
|
+
const { runSetup } = await import("../setup-wMOAgrsN.js");
|
|
1503
1834
|
await runSetup();
|
|
1504
1835
|
console.log();
|
|
1505
1836
|
wallet = resolveWallet();
|
|
@@ -1512,7 +1843,7 @@ Examples:
|
|
|
1512
1843
|
verbose(`protocol: ${resolvedProtocol ?? "auto-detect"}, maxDeposit: ${maxDeposit}`);
|
|
1513
1844
|
let preferredNetwork = config?.defaultNetwork;
|
|
1514
1845
|
if (!preferredNetwork && wallet.evmAddress && wallet.solanaAddress) {
|
|
1515
|
-
const { fetchAllBalances } = await import("../wallet-
|
|
1846
|
+
const { fetchAllBalances } = await import("../wallet-DKVlrR1S.js");
|
|
1516
1847
|
const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
|
|
1517
1848
|
const evmUsdc = balances.evm ? Number(balances.evm.usdc) : 0;
|
|
1518
1849
|
const solUsdc = balances.sol ? Number(balances.sol.usdc) : 0;
|
|
@@ -1679,7 +2010,7 @@ Examples:
|
|
|
1679
2010
|
const hasSolana = accepts.some((a) => a.network.startsWith("solana:"));
|
|
1680
2011
|
const hasMpp = detected.mpp;
|
|
1681
2012
|
const hasOther = accepts.some((a) => !a.network.startsWith("eip155:") && !a.network.startsWith("solana:"));
|
|
1682
|
-
const { fetchAllBalances } = await import("../wallet-
|
|
2013
|
+
const { fetchAllBalances } = await import("../wallet-DKVlrR1S.js");
|
|
1683
2014
|
const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
|
|
1684
2015
|
const evmUsdc = hasEvm && balances.evm ? Number(balances.evm.usdc) : 0;
|
|
1685
2016
|
const solUsdc = hasSolana && balances.sol ? Number(balances.sol.usdc) : 0;
|
|
@@ -1698,9 +2029,9 @@ Examples:
|
|
|
1698
2029
|
if (payment) dim(" Payment was signed and sent but rejected by the server.");
|
|
1699
2030
|
else dim(" Payment was not attempted despite sufficient balance.");
|
|
1700
2031
|
if (serverReason) dim(` Reason: ${serverReason}`);
|
|
1701
|
-
if (hasEvm && wallet.evmAddress && evmUsdc > 0) console.error(` Base: ${
|
|
1702
|
-
if (hasMpp && wallet.evmAddress && tempoUsdc > 0) console.error(` Tempo: ${
|
|
1703
|
-
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")})`)}`);
|
|
1704
2035
|
console.error();
|
|
1705
2036
|
dim(" This may be a temporary server-side issue. Try again in a moment.");
|
|
1706
2037
|
console.error();
|
|
@@ -1710,16 +2041,16 @@ Examples:
|
|
|
1710
2041
|
console.error();
|
|
1711
2042
|
dim(" Fund your wallet with USDC:");
|
|
1712
2043
|
if (hasEvm && wallet.evmAddress) {
|
|
1713
|
-
const balHint = evmUsdc > 0 ?
|
|
1714
|
-
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}`);
|
|
1715
2046
|
}
|
|
1716
2047
|
if (hasMpp && wallet.evmAddress) {
|
|
1717
|
-
const balHint = tempoUsdc > 0 ?
|
|
1718
|
-
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}`);
|
|
1719
2050
|
}
|
|
1720
2051
|
if (hasSolana && wallet.solanaAddress) {
|
|
1721
|
-
const balHint = solUsdc > 0 ?
|
|
1722
|
-
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}`);
|
|
1723
2054
|
}
|
|
1724
2055
|
if (hasEvm && !wallet.evmAddress) dim(" Base: endpoint accepts EVM but no EVM wallet configured");
|
|
1725
2056
|
if (hasMpp && !wallet.evmAddress) dim(" Tempo: endpoint accepts MPP but no EVM wallet configured");
|
|
@@ -1731,7 +2062,7 @@ Examples:
|
|
|
1731
2062
|
}
|
|
1732
2063
|
console.error();
|
|
1733
2064
|
dim(" Then re-run:");
|
|
1734
|
-
console.error(` ${
|
|
2065
|
+
console.error(` ${import_picocolors.default.cyan(`$ npx x402-proxy ${url}`)}`);
|
|
1735
2066
|
console.error();
|
|
1736
2067
|
}
|
|
1737
2068
|
return;
|
|
@@ -1743,7 +2074,7 @@ Examples:
|
|
|
1743
2074
|
}
|
|
1744
2075
|
if (isTTY()) {
|
|
1745
2076
|
const statusText = ` ${response.status} ${response.statusText} (${elapsedMs}ms)`;
|
|
1746
|
-
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;
|
|
1747
2078
|
process.stderr.write(`${colorFn(statusText)}\n`);
|
|
1748
2079
|
}
|
|
1749
2080
|
if (x402Payment) {
|
|
@@ -1801,7 +2132,83 @@ Examples:
|
|
|
1801
2132
|
}
|
|
1802
2133
|
});
|
|
1803
2134
|
//#endregion
|
|
1804
|
-
//#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
|
+
}
|
|
1805
2212
|
const mcpCommand = buildCommand({
|
|
1806
2213
|
docs: {
|
|
1807
2214
|
brief: "Start MCP stdio proxy with automatic payment",
|
|
@@ -1855,7 +2262,7 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
1855
2262
|
});
|
|
1856
2263
|
if (wallet.source === "none") {
|
|
1857
2264
|
dim("No wallet found. Auto-generating...");
|
|
1858
|
-
const { runSetup } = await import("../setup-
|
|
2265
|
+
const { runSetup } = await import("../setup-wMOAgrsN.js");
|
|
1859
2266
|
await runSetup({ nonInteractive: true });
|
|
1860
2267
|
const fresh = resolveWallet({
|
|
1861
2268
|
evmKey: flags.evmKey,
|
|
@@ -1872,12 +2279,12 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
1872
2279
|
if (wallet.solanaAddress) dim(` Solana: ${wallet.solanaAddress}`);
|
|
1873
2280
|
const config = loadConfig();
|
|
1874
2281
|
const resolvedProtocol = flags.protocol ?? config?.preferredProtocol ?? "x402";
|
|
1875
|
-
const { Client } = await import("
|
|
1876
|
-
const { SSEClientTransport } = await import("
|
|
1877
|
-
const { StreamableHTTPClientTransport } = await import("
|
|
1878
|
-
const { Server } = await import("
|
|
1879
|
-
const { StdioServerTransport } = await import("
|
|
1880
|
-
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");
|
|
1881
2288
|
async function connectTransport(target) {
|
|
1882
2289
|
try {
|
|
1883
2290
|
const transport = new StreamableHTTPClientTransport(new URL(remoteUrl));
|
|
@@ -1899,7 +2306,7 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
1899
2306
|
async function startX402Proxy() {
|
|
1900
2307
|
let preferredNetwork = config?.defaultNetwork;
|
|
1901
2308
|
if (!preferredNetwork && wallet.evmAddress && wallet.solanaAddress) {
|
|
1902
|
-
const { fetchAllBalances } = await import("../wallet-
|
|
2309
|
+
const { fetchAllBalances } = await import("../wallet-DKVlrR1S.js");
|
|
1903
2310
|
const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
|
|
1904
2311
|
const evmUsdc = balances.evm ? Number(balances.evm.usdc) : 0;
|
|
1905
2312
|
const solUsdc = balances.sol ? Number(balances.sol.usdc) : 0;
|
|
@@ -1912,23 +2319,16 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
1912
2319
|
spendLimitDaily: config?.spendLimitDaily,
|
|
1913
2320
|
spendLimitPerTx: config?.spendLimitPerTx
|
|
1914
2321
|
});
|
|
1915
|
-
const { x402MCPClient } = await import("@x402/mcp");
|
|
1916
2322
|
function warnPayment(accepts, toolName) {
|
|
1917
2323
|
const accept = accepts?.[0];
|
|
1918
2324
|
if (accept) warn(` Payment: ${accept.amount ? formatAmount(Number(accept.amount) / 1e6, "USDC") : "? USDC"} on ${displayNetwork(accept.network)} for tool "${toolName}"`);
|
|
1919
2325
|
}
|
|
1920
2326
|
const remoteClient = new Client({
|
|
1921
2327
|
name: "x402-proxy",
|
|
1922
|
-
version: "0.10.
|
|
2328
|
+
version: "0.10.9"
|
|
1923
2329
|
});
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
onPaymentRequested: (ctx) => {
|
|
1927
|
-
warnPayment(ctx.paymentRequired.accepts, ctx.toolName);
|
|
1928
|
-
return true;
|
|
1929
|
-
}
|
|
1930
|
-
});
|
|
1931
|
-
x402Mcp.onAfterPayment(async (ctx) => {
|
|
2330
|
+
await connectTransport(remoteClient);
|
|
2331
|
+
function recordX402Payment(ctx) {
|
|
1932
2332
|
const accepted = ctx.paymentPayload.accepted;
|
|
1933
2333
|
const tx = ctx.settleResponse?.transaction;
|
|
1934
2334
|
const record = {
|
|
@@ -1944,73 +2344,50 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
1944
2344
|
label: `mcp:${ctx.toolName}`
|
|
1945
2345
|
};
|
|
1946
2346
|
appendHistory(getHistoryPath(), record);
|
|
1947
|
-
}
|
|
1948
|
-
await
|
|
1949
|
-
let { tools } = await x402Mcp.listTools();
|
|
2347
|
+
}
|
|
2348
|
+
let { tools } = await remoteClient.listTools();
|
|
1950
2349
|
dim(` ${tools.length} tools available`);
|
|
1951
2350
|
let remoteResources = [];
|
|
1952
2351
|
try {
|
|
1953
|
-
remoteResources = (await
|
|
2352
|
+
remoteResources = (await remoteClient.listResources()).resources;
|
|
1954
2353
|
if (remoteResources.length > 0) dim(` ${remoteResources.length} resources available`);
|
|
1955
2354
|
} catch {
|
|
1956
2355
|
dim(" Resources not available from remote");
|
|
1957
2356
|
}
|
|
1958
2357
|
const localServer = new Server({
|
|
1959
2358
|
name: "x402-proxy",
|
|
1960
|
-
version: "0.10.
|
|
2359
|
+
version: "0.10.9"
|
|
1961
2360
|
}, { capabilities: {
|
|
1962
2361
|
tools: tools.length > 0 ? {} : void 0,
|
|
1963
2362
|
resources: remoteResources.length > 0 ? {} : void 0
|
|
1964
2363
|
} });
|
|
1965
|
-
localServer.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: tools.map((t) => (
|
|
1966
|
-
name: t.name,
|
|
1967
|
-
description: t.description,
|
|
1968
|
-
inputSchema: t.inputSchema,
|
|
1969
|
-
annotations: t.annotations
|
|
1970
|
-
})) }));
|
|
2364
|
+
localServer.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: tools.map((t) => cloneTool(t)) }));
|
|
1971
2365
|
localServer.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
1972
2366
|
const { name, arguments: args } = request.params;
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
const accepts = x402PaymentRequired.accepts;
|
|
1984
|
-
warnPayment(accepts, name);
|
|
1985
|
-
const paymentPayload = await x402PaymentClient.createPaymentPayload(x402PaymentRequired);
|
|
1986
|
-
const result = await x402Mcp.callToolWithPayment(name, args ?? {}, paymentPayload);
|
|
1987
|
-
return {
|
|
1988
|
-
content: result.content,
|
|
1989
|
-
isError: result.isError
|
|
1990
|
-
};
|
|
1991
|
-
}
|
|
1992
|
-
}
|
|
1993
|
-
throw err;
|
|
1994
|
-
}
|
|
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
|
+
}));
|
|
1995
2377
|
});
|
|
1996
2378
|
if (remoteResources.length > 0) {
|
|
1997
|
-
localServer.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: remoteResources.map((r) => (
|
|
1998
|
-
name: r.name,
|
|
1999
|
-
uri: r.uri,
|
|
2000
|
-
description: r.description,
|
|
2001
|
-
mimeType: r.mimeType
|
|
2002
|
-
})) }));
|
|
2379
|
+
localServer.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: remoteResources.map((r) => cloneResource(r)) }));
|
|
2003
2380
|
localServer.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
2004
|
-
return { contents: (await
|
|
2381
|
+
return { contents: (await remoteClient.readResource({ uri: request.params.uri })).contents.map((c) => ({ ...c })) };
|
|
2005
2382
|
});
|
|
2006
2383
|
}
|
|
2007
2384
|
remoteClient.setNotificationHandler(ToolListChangedNotificationSchema, async () => {
|
|
2008
|
-
tools = (await
|
|
2385
|
+
tools = (await remoteClient.listTools()).tools;
|
|
2009
2386
|
dim(` Tools updated: ${tools.length} available`);
|
|
2010
2387
|
await localServer.notification({ method: "notifications/tools/list_changed" });
|
|
2011
2388
|
});
|
|
2012
2389
|
if (remoteResources.length > 0) remoteClient.setNotificationHandler(ResourceListChangedNotificationSchema, async () => {
|
|
2013
|
-
remoteResources = (await
|
|
2390
|
+
remoteResources = (await remoteClient.listResources()).resources;
|
|
2014
2391
|
dim(` Resources updated: ${remoteResources.length} available`);
|
|
2015
2392
|
await localServer.notification({ method: "notifications/resources/list_changed" });
|
|
2016
2393
|
});
|
|
@@ -2021,7 +2398,7 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
2021
2398
|
const cleanup = async () => {
|
|
2022
2399
|
if (closing) return;
|
|
2023
2400
|
closing = true;
|
|
2024
|
-
await
|
|
2401
|
+
await remoteClient.close();
|
|
2025
2402
|
process.exit(0);
|
|
2026
2403
|
};
|
|
2027
2404
|
process.stdin.on("end", cleanup);
|
|
@@ -2033,9 +2410,9 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
2033
2410
|
error("MPP requires an EVM wallet. Configure one with: npx x402-proxy setup");
|
|
2034
2411
|
process.exit(1);
|
|
2035
2412
|
}
|
|
2036
|
-
const { tempo } = await import("
|
|
2037
|
-
const { McpClient } = await import("
|
|
2038
|
-
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");
|
|
2039
2416
|
const account = privateKeyToAccount(wallet.evmKey);
|
|
2040
2417
|
const maxDeposit = config?.mppSessionBudget ?? "1";
|
|
2041
2418
|
let lastChallengeAmount;
|
|
@@ -2052,7 +2429,7 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
2052
2429
|
}));
|
|
2053
2430
|
const remoteClient = new Client({
|
|
2054
2431
|
name: "x402-proxy",
|
|
2055
|
-
version: "0.10.
|
|
2432
|
+
version: "0.10.9"
|
|
2056
2433
|
});
|
|
2057
2434
|
await connectTransport(remoteClient);
|
|
2058
2435
|
const mppClient = McpClient.wrap(remoteClient, { methods: wrappedMethods });
|
|
@@ -2067,17 +2444,12 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
2067
2444
|
}
|
|
2068
2445
|
const localServer = new Server({
|
|
2069
2446
|
name: "x402-proxy",
|
|
2070
|
-
version: "0.10.
|
|
2447
|
+
version: "0.10.9"
|
|
2071
2448
|
}, { capabilities: {
|
|
2072
2449
|
tools: tools.length > 0 ? {} : void 0,
|
|
2073
2450
|
resources: remoteResources.length > 0 ? {} : void 0
|
|
2074
2451
|
} });
|
|
2075
|
-
localServer.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: tools.map((t) => (
|
|
2076
|
-
name: t.name,
|
|
2077
|
-
description: t.description,
|
|
2078
|
-
inputSchema: t.inputSchema,
|
|
2079
|
-
annotations: t.annotations
|
|
2080
|
-
})) }));
|
|
2452
|
+
localServer.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: tools.map((t) => cloneTool(t)) }));
|
|
2081
2453
|
localServer.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
2082
2454
|
const { name, arguments: args } = request.params;
|
|
2083
2455
|
const result = await mppClient.callTool({
|
|
@@ -2101,18 +2473,10 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
2101
2473
|
warn(` MPP payment for tool "${name}" (Tempo)${amountStr ? ` \u00b7 ${amountStr}` : ""}`);
|
|
2102
2474
|
lastChallengeAmount = void 0;
|
|
2103
2475
|
}
|
|
2104
|
-
return
|
|
2105
|
-
content: result.content,
|
|
2106
|
-
isError: result.isError
|
|
2107
|
-
};
|
|
2476
|
+
return normalizeCallToolResult(result);
|
|
2108
2477
|
});
|
|
2109
2478
|
if (remoteResources.length > 0) {
|
|
2110
|
-
localServer.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: remoteResources.map((r) => (
|
|
2111
|
-
name: r.name,
|
|
2112
|
-
uri: r.uri,
|
|
2113
|
-
description: r.description,
|
|
2114
|
-
mimeType: r.mimeType
|
|
2115
|
-
})) }));
|
|
2479
|
+
localServer.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: remoteResources.map((r) => cloneResource(r)) }));
|
|
2116
2480
|
localServer.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
2117
2481
|
return { contents: (await remoteClient.readResource({ uri: request.params.uri })).contents.map((c) => ({ ...c })) };
|
|
2118
2482
|
});
|
|
@@ -2144,7 +2508,7 @@ Wallet is auto-generated on first run. No env vars needed.`
|
|
|
2144
2508
|
}
|
|
2145
2509
|
});
|
|
2146
2510
|
//#endregion
|
|
2147
|
-
//#region src/commands/mcp-add.ts
|
|
2511
|
+
//#region packages/x402-proxy/src/commands/mcp-add.ts
|
|
2148
2512
|
function resolvePlatformPath(raw) {
|
|
2149
2513
|
let p = raw;
|
|
2150
2514
|
if (p.startsWith("~/")) p = join(homedir(), p.slice(2));
|
|
@@ -2194,45 +2558,45 @@ const mcpAddCommand = buildCommand({
|
|
|
2194
2558
|
}
|
|
2195
2559
|
},
|
|
2196
2560
|
async func(flags, name, url) {
|
|
2197
|
-
|
|
2561
|
+
Wt(import_picocolors.default.cyan("Add MCP server"));
|
|
2198
2562
|
try {
|
|
2199
2563
|
new URL(url);
|
|
2200
2564
|
} catch {
|
|
2201
|
-
|
|
2202
|
-
|
|
2565
|
+
R.error(`Invalid URL: ${url}`);
|
|
2566
|
+
Nt("Aborted.");
|
|
2203
2567
|
process.exit(1);
|
|
2204
2568
|
}
|
|
2205
2569
|
const serverName = name;
|
|
2206
2570
|
if (!isConfigured()) {
|
|
2207
|
-
|
|
2571
|
+
R.warn("No wallet configured. Let's set one up first.\n");
|
|
2208
2572
|
await runSetup();
|
|
2209
2573
|
console.log();
|
|
2210
|
-
|
|
2574
|
+
R.step(import_picocolors.default.cyan("Continuing MCP setup..."));
|
|
2211
2575
|
}
|
|
2212
|
-
const { generators, getAppIds, generateConfig, deepMerge } = await import("
|
|
2576
|
+
const { generators, getAppIds, generateConfig, deepMerge } = await import("../dist-DxJCYyL5.js");
|
|
2213
2577
|
let clientId;
|
|
2214
2578
|
if (flags.client) {
|
|
2215
2579
|
const appIds = getAppIds();
|
|
2216
2580
|
if (!appIds.includes(flags.client)) {
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2581
|
+
R.error(`Unknown client: ${flags.client}`);
|
|
2582
|
+
R.info(`Supported: ${appIds.join(", ")}`);
|
|
2583
|
+
Nt("Aborted.");
|
|
2220
2584
|
process.exit(1);
|
|
2221
2585
|
}
|
|
2222
2586
|
clientId = flags.client;
|
|
2223
2587
|
} else {
|
|
2224
2588
|
const appIds = getAppIds();
|
|
2225
2589
|
const detected = appIds.filter((id) => generators[id].detectInstalled());
|
|
2226
|
-
const selected = await
|
|
2590
|
+
const selected = await Jt({
|
|
2227
2591
|
message: "Where would you like to install the MCP server?",
|
|
2228
2592
|
options: appIds.map((id) => ({
|
|
2229
2593
|
value: id,
|
|
2230
|
-
label: `${generators[id].app.name}${detected.includes(id) ?
|
|
2594
|
+
label: `${generators[id].app.name}${detected.includes(id) ? import_picocolors.default.dim(" (detected)") : ""}`
|
|
2231
2595
|
})),
|
|
2232
2596
|
initialValue: detected.includes("claude-code") ? "claude-code" : detected[0] ?? "claude-code"
|
|
2233
2597
|
});
|
|
2234
|
-
if (
|
|
2235
|
-
|
|
2598
|
+
if (Ct(selected)) {
|
|
2599
|
+
Nt("Cancelled.");
|
|
2236
2600
|
process.exit(0);
|
|
2237
2601
|
}
|
|
2238
2602
|
clientId = selected;
|
|
@@ -2242,8 +2606,8 @@ const mcpAddCommand = buildCommand({
|
|
|
2242
2606
|
const platform = process.platform;
|
|
2243
2607
|
const rawPath = globalPaths?.[platform];
|
|
2244
2608
|
if (!rawPath) {
|
|
2245
|
-
|
|
2246
|
-
|
|
2609
|
+
R.error(`No global config path for ${generator.app.name} on ${platform}`);
|
|
2610
|
+
Nt("Aborted.");
|
|
2247
2611
|
process.exit(1);
|
|
2248
2612
|
}
|
|
2249
2613
|
const configPath = resolvePlatformPath(rawPath);
|
|
@@ -2262,7 +2626,7 @@ const mcpAddCommand = buildCommand({
|
|
|
2262
2626
|
});
|
|
2263
2627
|
let existing;
|
|
2264
2628
|
if (configFormat === "yaml") {
|
|
2265
|
-
const { default: YAML } = await import("
|
|
2629
|
+
const { default: YAML } = await import("../dist-DM5_F3r5.js").then((m) => /* @__PURE__ */ __toESM(m.default, 1));
|
|
2266
2630
|
if (existsSync(configPath)) {
|
|
2267
2631
|
const raw = readFileSync(configPath, "utf-8").trim();
|
|
2268
2632
|
existing = raw ? YAML.parse(raw) ?? {} : {};
|
|
@@ -2271,27 +2635,27 @@ const mcpAddCommand = buildCommand({
|
|
|
2271
2635
|
const rootKey = Object.keys(generated)[0] ?? "mcpServers";
|
|
2272
2636
|
const existingServers = existing[rootKey] ?? {};
|
|
2273
2637
|
if (existingServers[serverName]) {
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
const overwrite = await
|
|
2277
|
-
if (
|
|
2278
|
-
|
|
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.");
|
|
2279
2643
|
process.exit(0);
|
|
2280
2644
|
}
|
|
2281
2645
|
}
|
|
2282
|
-
|
|
2646
|
+
R.info(`Config will be added to ${import_picocolors.default.dim(configPath)}`);
|
|
2283
2647
|
const previewLines = generator.serialize(generated).split("\n");
|
|
2284
2648
|
const formatted = previewLines.map((line, i) => {
|
|
2285
2649
|
if (i === 0 || i === previewLines.length - 1) return line;
|
|
2286
2650
|
const trimmed = line.trimStart();
|
|
2287
2651
|
if (trimmed.startsWith(`"${rootKey}"`) || trimmed.startsWith(`${rootKey}:`)) return line;
|
|
2288
|
-
return `${
|
|
2652
|
+
return `${import_picocolors.default.green("+")} ${line}`;
|
|
2289
2653
|
}).join("\n");
|
|
2290
|
-
|
|
2654
|
+
R.message(formatted);
|
|
2291
2655
|
if (!flags.yes) {
|
|
2292
|
-
const proceed = await
|
|
2293
|
-
if (
|
|
2294
|
-
|
|
2656
|
+
const proceed = await Rt({ message: "Would you like to proceed?" });
|
|
2657
|
+
if (Ct(proceed) || !proceed) {
|
|
2658
|
+
Nt("Cancelled.");
|
|
2295
2659
|
process.exit(0);
|
|
2296
2660
|
}
|
|
2297
2661
|
}
|
|
@@ -2300,7 +2664,7 @@ const mcpAddCommand = buildCommand({
|
|
|
2300
2664
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
2301
2665
|
const serialized = generator.serialize(merged);
|
|
2302
2666
|
writeFileSync(configPath, serialized.endsWith("\n") ? serialized : `${serialized}\n`);
|
|
2303
|
-
|
|
2667
|
+
R.success(`Added ${import_picocolors.default.bold(serverName)} MCP to ${import_picocolors.default.bold(generator.app.name)}`);
|
|
2304
2668
|
const wallet = resolveWallet();
|
|
2305
2669
|
if (wallet.source !== "none") {
|
|
2306
2670
|
const balances = await fetchAllBalances(wallet.evmAddress, wallet.solanaAddress);
|
|
@@ -2308,26 +2672,26 @@ const mcpAddCommand = buildCommand({
|
|
|
2308
2672
|
const solUsdc = balances.sol ? Number(balances.sol.usdc) : 0;
|
|
2309
2673
|
const tempoUsdc = balances.tempo ? Number(balances.tempo.usdc) : 0;
|
|
2310
2674
|
if (evmUsdc === 0 && solUsdc === 0 && tempoUsdc === 0) {
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
if (wallet.evmAddress)
|
|
2314
|
-
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)}`);
|
|
2315
2679
|
} else {
|
|
2316
2680
|
const parts = [];
|
|
2317
2681
|
if (balances.evm) parts.push(`Base: ${balances.evm.usdc} USDC`);
|
|
2318
2682
|
if (balances.sol) parts.push(`Solana: ${balances.sol.usdc} USDC`);
|
|
2319
2683
|
if (balances.tempo) parts.push(`Tempo: ${balances.tempo.usdc} USDC`);
|
|
2320
|
-
|
|
2684
|
+
R.success(`Balance: ${parts.join(" | ")}`);
|
|
2321
2685
|
}
|
|
2322
2686
|
}
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
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!`));
|
|
2327
2691
|
}
|
|
2328
2692
|
});
|
|
2329
2693
|
//#endregion
|
|
2330
|
-
//#region src/commands/wallet-export.ts
|
|
2694
|
+
//#region packages/x402-proxy/src/commands/wallet-export.ts
|
|
2331
2695
|
const walletExportCommand = buildCommand({
|
|
2332
2696
|
docs: { brief: "Export private key or mnemonic to stdout (pipe-safe)" },
|
|
2333
2697
|
parameters: {
|
|
@@ -2352,8 +2716,8 @@ const walletExportCommand = buildCommand({
|
|
|
2352
2716
|
process.exit(1);
|
|
2353
2717
|
}
|
|
2354
2718
|
if (process.stdout.isTTY) {
|
|
2355
|
-
const confirmed = await
|
|
2356
|
-
if (
|
|
2719
|
+
const confirmed = await Rt({ message: "This will print your mnemonic to the terminal. Continue?" });
|
|
2720
|
+
if (Ct(confirmed) || !confirmed) process.exit(0);
|
|
2357
2721
|
} else warn("Warning: mnemonic will be printed to stdout.");
|
|
2358
2722
|
process.stdout.write(mnemonic);
|
|
2359
2723
|
return;
|
|
@@ -2372,15 +2736,15 @@ const walletExportCommand = buildCommand({
|
|
|
2372
2736
|
process.exit(1);
|
|
2373
2737
|
}
|
|
2374
2738
|
if (process.stdout.isTTY) {
|
|
2375
|
-
const confirmed = await
|
|
2376
|
-
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);
|
|
2377
2741
|
} else warn("Warning: private key will be printed to stdout.");
|
|
2378
2742
|
if (chain === "evm") process.stdout.write(wallet.evmKey);
|
|
2379
2743
|
else process.stdout.write(base58.encode(wallet.solanaKey.slice(0, 32)));
|
|
2380
2744
|
}
|
|
2381
2745
|
});
|
|
2382
2746
|
//#endregion
|
|
2383
|
-
//#region src/app.ts
|
|
2747
|
+
//#region packages/x402-proxy/src/app.ts
|
|
2384
2748
|
const walletRoutes = buildRouteMap({
|
|
2385
2749
|
routes: {
|
|
2386
2750
|
info: walletInfoCommand,
|
|
@@ -2408,7 +2772,7 @@ const walletRoutes = buildRouteMap({
|
|
|
2408
2772
|
func(flags) {
|
|
2409
2773
|
const records = readHistory(getHistoryPath());
|
|
2410
2774
|
if (records.length === 0) {
|
|
2411
|
-
console.log(
|
|
2775
|
+
console.log(import_picocolors.default.dim("No payment history yet."));
|
|
2412
2776
|
return;
|
|
2413
2777
|
}
|
|
2414
2778
|
if (flags.json) {
|
|
@@ -2426,7 +2790,7 @@ const walletRoutes = buildRouteMap({
|
|
|
2426
2790
|
console.log(line);
|
|
2427
2791
|
}
|
|
2428
2792
|
console.log();
|
|
2429
|
-
console.log(
|
|
2793
|
+
console.log(import_picocolors.default.dim(` Today: ${formatAmount(spend.today, "USDC")} | Total: ${formatAmount(spend.total, "USDC")} | ${spend.count} transactions`));
|
|
2430
2794
|
console.log();
|
|
2431
2795
|
}
|
|
2432
2796
|
}),
|
|
@@ -2466,16 +2830,16 @@ const app = buildApplication(buildRouteMap({
|
|
|
2466
2830
|
docs: { brief: "curl for x402 paid APIs" }
|
|
2467
2831
|
}), {
|
|
2468
2832
|
name: "x402-proxy",
|
|
2469
|
-
versionInfo: { currentVersion: "0.10.
|
|
2833
|
+
versionInfo: { currentVersion: "0.10.9" },
|
|
2470
2834
|
scanner: { caseStyle: "allow-kebab-for-camel" }
|
|
2471
2835
|
});
|
|
2472
2836
|
//#endregion
|
|
2473
|
-
//#region src/context.ts
|
|
2837
|
+
//#region packages/x402-proxy/src/context.ts
|
|
2474
2838
|
function buildContext(process) {
|
|
2475
2839
|
return { process };
|
|
2476
2840
|
}
|
|
2477
2841
|
//#endregion
|
|
2478
|
-
//#region src/bin/cli.ts
|
|
2842
|
+
//#region packages/x402-proxy/src/bin/cli.ts
|
|
2479
2843
|
const rawArgs = process.argv.slice(2);
|
|
2480
2844
|
const args = [];
|
|
2481
2845
|
for (let i = 0; i < rawArgs.length; i++) {
|