x402-proxy 0.5.2 → 0.7.0

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.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import fs from "node:fs";
3
- import os from "node:os";
4
3
  import path from "node:path";
4
+ import os from "node:os";
5
5
  import { parse, stringify } from "yaml";
6
6
  import { ed25519 } from "@noble/curves/ed25519.js";
7
7
  import { base58 } from "@scure/base";
@@ -158,4 +158,4 @@ function checksumAddress(addr) {
158
158
  }
159
159
 
160
160
  //#endregion
161
- export { getConfigDirShort as a, isConfigured as c, saveConfig as d, saveWalletFile as f, ensureConfigDir as i, loadConfig as l, deriveSolanaKeypair as n, getHistoryPath as o, generateMnemonic$1 as r, getWalletPath as s, deriveEvmKeypair as t, loadWalletFile as u };
161
+ export { getHistoryPath as a, loadConfig as c, saveWalletFile as d, getConfigDirShort as i, loadWalletFile as l, deriveSolanaKeypair as n, getWalletPath as o, generateMnemonic$1 as r, isConfigured as s, deriveEvmKeypair as t, saveConfig as u };
package/dist/index.d.ts CHANGED
@@ -5,11 +5,27 @@ import { KeyPairSigner } from "@solana/kit";
5
5
 
6
6
  //#region src/handler.d.ts
7
7
  type PaymentInfo = {
8
+ protocol: "x402";
8
9
  network: string | undefined;
9
10
  payTo: string | undefined; /** Raw amount in base units as returned by x402 (e.g. "50000" for 0.05 USDC) */
10
11
  amount: string | undefined;
11
12
  asset: string | undefined;
12
13
  };
14
+ type MppPaymentInfo = {
15
+ protocol: "mpp";
16
+ network: string;
17
+ amount?: string;
18
+ intent?: string;
19
+ channelId?: string;
20
+ receipt?: {
21
+ method: string;
22
+ reference: string;
23
+ status: string;
24
+ timestamp: string;
25
+ acceptedCumulative?: string;
26
+ txHash?: string;
27
+ };
28
+ };
13
29
  type X402ProxyOptions = {
14
30
  client: x402Client$1;
15
31
  };
@@ -17,20 +33,39 @@ type X402ProxyHandler = {
17
33
  /** Wrapped fetch with x402 payment handling */x402Fetch: (input: string | URL | Request, init?: RequestInit) => Promise<Response>; /** Shift the latest payment info from the queue (call after x402Fetch) */
18
34
  shiftPayment: () => PaymentInfo | undefined;
19
35
  };
36
+ type MppProxyHandler = {
37
+ /** Payment-aware fetch that handles 402 + WWW-Authenticate automatically */fetch: (input: string | URL, init?: RequestInit) => Promise<Response>; /** SSE streaming with mid-stream voucher cycling */
38
+ sse: (input: string | URL, init?: RequestInit) => Promise<AsyncIterable<string>>; /** Shift the latest payment info from the queue */
39
+ shiftPayment: () => MppPaymentInfo | undefined; /** Settle any active session channel */
40
+ close: () => Promise<void>;
41
+ };
42
+ type DetectedProtocols = {
43
+ x402: boolean;
44
+ mpp: boolean;
45
+ };
46
+ /**
47
+ * Detect which payment protocols a 402 response advertises.
48
+ * - x402: PAYMENT-REQUIRED or X-PAYMENT-REQUIRED header
49
+ * - MPP: WWW-Authenticate header with Payment scheme
50
+ */
51
+ declare function detectProtocols(response: Response): DetectedProtocols;
20
52
  /**
21
53
  * Extract the on-chain transaction signature from an x402 payment response header.
22
54
  */
23
55
  declare function extractTxSignature(response: Response): string | undefined;
24
56
  /**
25
57
  * Create an x402 proxy handler that wraps fetch with automatic payment.
26
- *
27
- * Chain-agnostic: accepts a pre-configured x402Client with any registered
28
- * schemes (SVM, EVM, etc). The handler captures payment info via the
29
- * onAfterPaymentCreation hook. Callers use `x402Fetch` for requests that
30
- * may require x402 payment, and `shiftPayment` to retrieve captured
31
- * payment info after each call.
32
58
  */
33
59
  declare function createX402ProxyHandler(opts: X402ProxyOptions): X402ProxyHandler;
60
+ declare const TEMPO_NETWORK = "eip155:4217";
61
+ /**
62
+ * Create an MPP proxy handler using mppx client.
63
+ * Dynamically imports mppx/client to keep startup fast.
64
+ */
65
+ declare function createMppProxyHandler(opts: {
66
+ evmKey: string;
67
+ maxDeposit?: string;
68
+ }): Promise<MppProxyHandler>;
34
69
  //#endregion
35
70
  //#region src/history.d.ts
36
71
  declare const HISTORY_MAX_LINES = 1000;
@@ -38,7 +73,7 @@ declare const HISTORY_KEEP_LINES = 500;
38
73
  type TxRecord = {
39
74
  t: number;
40
75
  ok: boolean;
41
- kind: "x402_inference" | "x402_payment" | "transfer" | "buy" | "sell" | "mint" | "swap";
76
+ kind: "x402_inference" | "x402_payment" | "mpp_payment" | "transfer" | "buy" | "sell" | "mint" | "swap";
42
77
  net: string;
43
78
  from: string;
44
79
  to?: string;
@@ -82,4 +117,4 @@ declare function loadSvmWallet(keypairPath: string): Promise<KeyPairSigner>;
82
117
  */
83
118
  declare function loadEvmWallet(keyPath: string): ClientEvmSigner$1;
84
119
  //#endregion
85
- export { type ClientEvmSigner, ExactEvmScheme, ExactSvmScheme, HISTORY_KEEP_LINES, HISTORY_MAX_LINES, type PaymentInfo, type TxRecord, type X402ProxyHandler, type X402ProxyOptions, appendHistory, calcSpend, createX402ProxyHandler, explorerUrl, extractTxSignature, formatTxLine, loadEvmWallet, loadSvmWallet, readHistory, toClientEvmSigner, x402Client };
120
+ export { type ClientEvmSigner, type DetectedProtocols, ExactEvmScheme, ExactSvmScheme, HISTORY_KEEP_LINES, HISTORY_MAX_LINES, type MppPaymentInfo, type MppProxyHandler, type PaymentInfo, TEMPO_NETWORK, type TxRecord, type X402ProxyHandler, type X402ProxyOptions, appendHistory, calcSpend, createMppProxyHandler, createX402ProxyHandler, detectProtocols, explorerUrl, extractTxSignature, formatTxLine, loadEvmWallet, loadSvmWallet, readHistory, toClientEvmSigner, x402Client };
package/dist/index.js CHANGED
@@ -1,31 +1,42 @@
1
1
  import { ExactEvmScheme, toClientEvmSigner, toClientEvmSigner as toClientEvmSigner$1 } from "@x402/evm";
2
2
  import { decodePaymentResponseHeader, wrapFetchWithPayment, x402Client } from "@x402/fetch";
3
3
  import { ExactSvmScheme } from "@x402/svm/exact/client";
4
- import { appendFileSync, existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
4
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
5
+ import { dirname } from "node:path";
5
6
  import { createKeyPairSignerFromBytes } from "@solana/kit";
6
7
  import { privateKeyToAccount } from "viem/accounts";
7
8
 
8
9
  //#region src/handler.ts
9
10
  /**
11
+ * Detect which payment protocols a 402 response advertises.
12
+ * - x402: PAYMENT-REQUIRED or X-PAYMENT-REQUIRED header
13
+ * - MPP: WWW-Authenticate header with Payment scheme
14
+ */
15
+ function detectProtocols(response) {
16
+ const pr = response.headers.get("PAYMENT-REQUIRED") ?? response.headers.get("X-PAYMENT-REQUIRED");
17
+ const wwwAuth = response.headers.get("WWW-Authenticate");
18
+ return {
19
+ x402: !!pr,
20
+ mpp: !!(wwwAuth && /^Payment\b/i.test(wwwAuth.trim()))
21
+ };
22
+ }
23
+ /**
10
24
  * Extract the on-chain transaction signature from an x402 payment response header.
11
25
  */
12
26
  function extractTxSignature(response) {
13
- const header = response.headers.get("PAYMENT-RESPONSE") ?? response.headers.get("X-PAYMENT-RESPONSE");
14
- if (!header) return void 0;
15
- try {
16
- return decodePaymentResponseHeader(header).transaction ?? void 0;
27
+ const x402Header = response.headers.get("PAYMENT-RESPONSE") ?? response.headers.get("X-PAYMENT-RESPONSE");
28
+ if (x402Header) try {
29
+ return decodePaymentResponseHeader(x402Header).transaction ?? void 0;
30
+ } catch {}
31
+ const mppHeader = response.headers.get("Payment-Receipt");
32
+ if (mppHeader) try {
33
+ return JSON.parse(Buffer.from(mppHeader, "base64url").toString()).reference ?? void 0;
17
34
  } catch {
18
35
  return;
19
36
  }
20
37
  }
21
38
  /**
22
39
  * Create an x402 proxy handler that wraps fetch with automatic payment.
23
- *
24
- * Chain-agnostic: accepts a pre-configured x402Client with any registered
25
- * schemes (SVM, EVM, etc). The handler captures payment info via the
26
- * onAfterPaymentCreation hook. Callers use `x402Fetch` for requests that
27
- * may require x402 payment, and `shiftPayment` to retrieve captured
28
- * payment info after each call.
29
40
  */
30
41
  function createX402ProxyHandler(opts) {
31
42
  const { client } = opts;
@@ -33,6 +44,7 @@ function createX402ProxyHandler(opts) {
33
44
  client.onAfterPaymentCreation(async (hookCtx) => {
34
45
  const raw = hookCtx.selectedRequirements.amount;
35
46
  paymentQueue.push({
47
+ protocol: "x402",
36
48
  network: hookCtx.selectedRequirements.network,
37
49
  payTo: hookCtx.selectedRequirements.payTo,
38
50
  amount: raw,
@@ -44,6 +56,84 @@ function createX402ProxyHandler(opts) {
44
56
  shiftPayment: () => paymentQueue.shift()
45
57
  };
46
58
  }
59
+ const TEMPO_NETWORK = "eip155:4217";
60
+ /**
61
+ * Create an MPP proxy handler using mppx client.
62
+ * Dynamically imports mppx/client to keep startup fast.
63
+ */
64
+ async function createMppProxyHandler(opts) {
65
+ const { Mppx, tempo } = await import("mppx/client");
66
+ const { privateKeyToAccount } = await import("viem/accounts");
67
+ const account = privateKeyToAccount(opts.evmKey);
68
+ const maxDeposit = opts.maxDeposit ?? "1";
69
+ const paymentQueue = [];
70
+ const mppx = Mppx.create({
71
+ methods: [tempo({
72
+ account,
73
+ maxDeposit
74
+ })],
75
+ polyfill: false
76
+ });
77
+ let session;
78
+ return {
79
+ async fetch(input, init) {
80
+ const response = await mppx.fetch(typeof input === "string" ? input : input.toString(), init);
81
+ const receiptHeader = response.headers.get("Payment-Receipt");
82
+ if (receiptHeader) try {
83
+ const receipt = JSON.parse(Buffer.from(receiptHeader, "base64url").toString());
84
+ paymentQueue.push({
85
+ protocol: "mpp",
86
+ network: TEMPO_NETWORK,
87
+ receipt
88
+ });
89
+ } catch {
90
+ paymentQueue.push({
91
+ protocol: "mpp",
92
+ network: TEMPO_NETWORK
93
+ });
94
+ }
95
+ return response;
96
+ },
97
+ async sse(input, init) {
98
+ session ??= tempo.session({
99
+ account,
100
+ maxDeposit
101
+ });
102
+ const url = typeof input === "string" ? input : input.toString();
103
+ const iterable = await session.sse(url, init);
104
+ paymentQueue.push({
105
+ protocol: "mpp",
106
+ network: TEMPO_NETWORK,
107
+ intent: "session"
108
+ });
109
+ return iterable;
110
+ },
111
+ shiftPayment: () => paymentQueue.shift(),
112
+ async close() {
113
+ if (session?.opened) {
114
+ const receipt = await session.close();
115
+ if (receipt) {
116
+ const spentUsdc = receipt.spent ? (Number(receipt.spent) / 1e6).toString() : void 0;
117
+ paymentQueue.push({
118
+ protocol: "mpp",
119
+ network: TEMPO_NETWORK,
120
+ intent: "session",
121
+ amount: spentUsdc,
122
+ channelId: session.channelId ?? void 0,
123
+ receipt: {
124
+ method: receipt.method,
125
+ reference: receipt.reference,
126
+ status: receipt.status,
127
+ timestamp: receipt.timestamp,
128
+ acceptedCumulative: receipt.acceptedCumulative,
129
+ txHash: receipt.txHash
130
+ }
131
+ });
132
+ }
133
+ }
134
+ }
135
+ };
136
+ }
47
137
 
48
138
  //#endregion
49
139
  //#region src/history.ts
@@ -51,6 +141,7 @@ const HISTORY_MAX_LINES = 1e3;
51
141
  const HISTORY_KEEP_LINES = 500;
52
142
  function appendHistory(historyPath, record) {
53
143
  try {
144
+ mkdirSync(dirname(historyPath), { recursive: true });
54
145
  appendFileSync(historyPath, `${JSON.stringify(record)}\n`);
55
146
  if (existsSync(historyPath)) {
56
147
  if (statSync(historyPath).size > HISTORY_MAX_LINES * 200) {
@@ -111,6 +202,7 @@ function formatAmount(amount, token) {
111
202
  const KIND_LABELS = {
112
203
  x402_inference: "inference",
113
204
  x402_payment: "payment",
205
+ mpp_payment: "mpp payment",
114
206
  transfer: "transfer",
115
207
  buy: "buy",
116
208
  sell: "sell",
@@ -119,7 +211,9 @@ const KIND_LABELS = {
119
211
  };
120
212
  function explorerUrl(net, tx) {
121
213
  if (net.startsWith("eip155:")) {
122
- if (net.split(":")[1] === "8453") return `https://basescan.org/tx/${tx}`;
214
+ const chainId = net.split(":")[1];
215
+ if (chainId === "4217") return `https://explore.mainnet.tempo.xyz/tx/${tx}`;
216
+ if (chainId === "8453") return `https://basescan.org/tx/${tx}`;
123
217
  return `https://basescan.org/tx/${tx}`;
124
218
  }
125
219
  return `https://solscan.io/tx/${tx}`;
@@ -133,6 +227,7 @@ function shortModel(model) {
133
227
  }
134
228
  function shortNetwork(net) {
135
229
  if (net === "eip155:8453") return "base";
230
+ if (net === "eip155:4217") return "tempo";
136
231
  if (net.startsWith("eip155:")) return `evm:${net.split(":")[1]}`;
137
232
  if (net.startsWith("solana:")) return "sol";
138
233
  return net;
@@ -178,4 +273,4 @@ function loadEvmWallet(keyPath) {
178
273
  }
179
274
 
180
275
  //#endregion
181
- export { ExactEvmScheme, ExactSvmScheme, HISTORY_KEEP_LINES, HISTORY_MAX_LINES, appendHistory, calcSpend, createX402ProxyHandler, explorerUrl, extractTxSignature, formatTxLine, loadEvmWallet, loadSvmWallet, readHistory, toClientEvmSigner, x402Client };
276
+ export { ExactEvmScheme, ExactSvmScheme, HISTORY_KEEP_LINES, HISTORY_MAX_LINES, TEMPO_NETWORK, appendHistory, calcSpend, createMppProxyHandler, createX402ProxyHandler, detectProtocols, explorerUrl, extractTxSignature, formatTxLine, loadEvmWallet, loadSvmWallet, readHistory, toClientEvmSigner, x402Client };
@@ -0,0 +1,55 @@
1
+ import { x402Client } from "@x402/fetch";
2
+ //#region src/openclaw/tools.d.ts
3
+ type ModelEntry = {
4
+ provider: string;
5
+ id: string;
6
+ name: string;
7
+ maxTokens: number;
8
+ reasoning: boolean;
9
+ input: string[];
10
+ cost: {
11
+ input: number;
12
+ output: number;
13
+ cacheRead: number;
14
+ cacheWrite: number;
15
+ };
16
+ contextWindow: number;
17
+ };
18
+ //#endregion
19
+ //#region src/openclaw/plugin.d.ts
20
+ type OpenClawPluginApi = {
21
+ pluginConfig?: Record<string, unknown>;
22
+ logger: {
23
+ info: (msg: string) => void;
24
+ error: (msg: string) => void;
25
+ };
26
+ registerProvider: (provider: {
27
+ id: string;
28
+ label: string;
29
+ auth: unknown[];
30
+ models: {
31
+ baseUrl: string;
32
+ api: string;
33
+ authHeader: boolean;
34
+ models: Array<Omit<ModelEntry, "provider"> & {
35
+ input: Array<"text" | "image">;
36
+ }>;
37
+ };
38
+ }) => void;
39
+ registerTool: (tool: unknown) => void;
40
+ registerCommand: (command: unknown) => void;
41
+ registerService: (service: {
42
+ id: string;
43
+ start: () => Promise<void>;
44
+ stop: () => Promise<void>;
45
+ }) => void;
46
+ registerHttpRoute: (params: {
47
+ path: string;
48
+ match: string;
49
+ auth: string;
50
+ handler: (req: unknown, res: unknown) => Promise<void>;
51
+ }) => void;
52
+ };
53
+ declare function register(api: OpenClawPluginApi): void;
54
+ //#endregion
55
+ export { register };