wolverine-ai 5.2.12 → 5.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wolverine-ai",
3
- "version": "5.2.12",
3
+ "version": "5.3.1",
4
4
  "description": "Self-healing Node.js server framework powered by AI. Catches crashes, diagnoses errors, generates fixes, verifies, and restarts — automatically.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -52,13 +52,15 @@
52
52
  ],
53
53
  "dependencies": {
54
54
  "@anthropic-ai/sdk": "^0.82.0",
55
+ "@coinbase/x402": "^2.1.0",
55
56
  "@fastify/compress": "^8.0.0",
56
57
  "@fastify/cors": "^10.0.0",
57
58
  "chalk": "^4.1.2",
58
59
  "diff": "^7.0.0",
59
60
  "dotenv": "^16.4.7",
60
61
  "fastify": "^5.8.4",
61
- "openai": "^4.73.0"
62
+ "openai": "^4.73.0",
63
+ "x402": "^1.1.0"
62
64
  },
63
65
  "optionalDependencies": {
64
66
  "@coinbase/cdp-sdk": "^1.46.1",
@@ -4,38 +4,35 @@ const path = require("path");
4
4
  /**
5
5
  * x402 Fastify Plugin — add crypto payments to any route with one flag.
6
6
  *
7
- * Uses the official @x402/core SDK for payment verification and settlement
8
- * via the CDP facilitator. Compatible with x402 Bazaar for API discovery.
7
+ * Uses @coinbase/x402 facilitator + x402/verify for payment verification
8
+ * and on-chain settlement. Matches the working pattern from blockaid-scanner.
9
9
  *
10
10
  * Two modes:
11
11
  * Fixed price: { x402: { price: "$0.01" } }
12
12
  * Variable price: { x402: { variable: true, min: "$1", max: "$10000" } }
13
13
  */
14
14
 
15
- // Node 18 needs globalThis.crypto for @x402/evm
15
+ // Node 18 needs globalThis.crypto
16
16
  if (!globalThis.crypto) {
17
17
  globalThis.crypto = require("crypto").webcrypto;
18
18
  }
19
19
 
20
- const USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
20
+ const USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
21
+ const USDC_EIP712 = { name: "USD Coin", version: "2" };
21
22
 
22
23
  let _payTo = null;
23
- let _network = "eip155:8453";
24
- let _facilitatorUrl = "https://api.cdp.coinbase.com/platform/v2/x402";
25
- let _x402Server = null; // x402ResourceServer instance
24
+ let _network = "base"; // v1 format, not CAIP-2
25
+ let _facilitatorClient = null;
26
26
 
27
27
  async function x402Plugin(fastify, opts) {
28
- _network = opts.network || _network;
29
- _facilitatorUrl = opts.facilitator || _facilitatorUrl;
30
28
  _payTo = opts.payTo || null;
31
29
 
32
30
  // Load from settings.json
33
31
  try {
34
32
  const settingsPath = path.join(process.cwd(), "server", "config", "settings.json");
35
33
  const settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
36
- if (settings.x402?.network) _network = settings.x402.network;
37
- if (settings.x402?.facilitator) _facilitatorUrl = settings.x402.facilitator;
38
34
  if (settings.x402?.payTo) _payTo = settings.x402.payTo;
35
+ if (settings.x402?.network) _network = settings.x402.network;
39
36
  } catch {}
40
37
 
41
38
  // Auto-detect payTo from vault
@@ -46,44 +43,18 @@ async function x402Plugin(fastify, opts) {
46
43
  } catch {}
47
44
  }
48
45
 
49
- if (_payTo) {
50
- console.log(` 💰 x402: payments to ${_payTo.slice(0, 6)}...${_payTo.slice(-4)} on ${_network}`);
51
- console.log(` 💰 x402: facilitator ${_facilitatorUrl}`);
52
- }
53
-
54
- // Initialize x402 SDK server with CDP facilitator
46
+ // Initialize facilitator from @coinbase/x402 (ESM packages, need dynamic import)
55
47
  try {
56
- const { x402ResourceServer, HTTPFacilitatorClient } = require("@x402/core/server");
57
- const { ExactEvmScheme } = require("@x402/evm/exact/server");
58
-
59
- const facilitatorConfig = { url: _facilitatorUrl };
60
-
61
- // Add CDP auth if keys are available
62
- if (process.env.CDP_API_KEY_ID && process.env.CDP_API_KEY_SECRET) {
63
- facilitatorConfig.createAuthHeaders = async () => {
64
- const { getAuthHeaders } = await import("@coinbase/cdp-sdk/auth");
65
- const endpoints = ["verify", "settle", "supported"];
66
- const result = {};
67
- for (const ep of endpoints) {
68
- const headers = await getAuthHeaders({
69
- apiKeyId: process.env.CDP_API_KEY_ID,
70
- apiKeySecret: process.env.CDP_API_KEY_SECRET,
71
- requestMethod: "POST",
72
- requestHost: `https://${new URL(_facilitatorUrl).host}`,
73
- requestPath: `${new URL(_facilitatorUrl).pathname}/${ep}`,
74
- });
75
- result[ep] = headers;
76
- }
77
- return result;
78
- };
79
- }
80
-
81
- const client = new HTTPFacilitatorClient(facilitatorConfig);
82
- _x402Server = new x402ResourceServer(client);
83
- _x402Server.register("eip155:*", new ExactEvmScheme());
84
- console.log(` 💰 x402: SDK initialized (ExactEvmScheme)`);
48
+ const { facilitator } = await import("@coinbase/x402");
49
+ const { useFacilitator } = await import("x402/verify");
50
+ _facilitatorClient = useFacilitator(facilitator);
51
+ console.log(` 💰 x402: facilitator loaded (@coinbase/x402)`);
85
52
  } catch (err) {
86
- console.log(` ⚠️ x402: SDK init failed (${err.message}) — using direct verification`);
53
+ console.log(` ⚠️ x402: facilitator init failed (${err.message}) — install @coinbase/x402 x402`);
54
+ }
55
+
56
+ if (_payTo) {
57
+ console.log(` 💰 x402: payments to ${_payTo.slice(0, 6)}...${_payTo.slice(-4)} on ${_network}`);
87
58
  }
88
59
 
89
60
  // ── Route-level x402 hook ──
@@ -94,17 +65,18 @@ async function x402Plugin(fastify, opts) {
94
65
  reply.code(500).send({ error: "x402 not configured — no wallet address" });
95
66
  return;
96
67
  }
68
+ if (!_facilitatorClient) {
69
+ reply.code(500).send({ error: "x402 facilitator not loaded — install @coinbase/x402 x402" });
70
+ return;
71
+ }
97
72
 
98
- // Determine the price
73
+ // Determine dollar amount
99
74
  let dollarAmount;
100
75
  if (routeConfig.variable) {
101
76
  const field = routeConfig.priceField || "dollars";
102
77
  const raw = request.body?.[field] || request.query?.[field];
103
78
  if (!raw) {
104
- reply.code(400).send({
105
- error: `${field} required`,
106
- x402: { variable: true, min: routeConfig.min || "$1", max: routeConfig.max || "$10000" },
107
- });
79
+ reply.code(400).send({ error: `${field} required`, x402: { variable: true, min: routeConfig.min || "$1", max: routeConfig.max || "$10000" } });
108
80
  return;
109
81
  }
110
82
  dollarAmount = parseFloat(String(raw).replace("$", ""));
@@ -119,113 +91,128 @@ async function x402Plugin(fastify, opts) {
119
91
  if (!dollarAmount) { reply.code(500).send({ error: "x402 route missing price config" }); return; }
120
92
  }
121
93
 
122
- const usdcAmount = String(Math.round(dollarAmount * 1e6));
94
+ const usdcAtomicAmount = String(Math.round(dollarAmount * 1e6));
123
95
  const price = "$" + dollarAmount.toFixed(2);
124
- const paymentHeader = request.headers["payment-signature"] || request.headers["x-payment"];
125
96
 
126
- // No payment return 402 with payment requirements
97
+ // Check for payment header (X-PAYMENT for v1 compat, payment-signature for v2)
98
+ const paymentHeader = request.headers["x-payment"] || request.headers["payment-signature"];
99
+
100
+ // Build payment requirements (v1 format matching @coinbase/x402)
101
+ const { getAddress } = await import("viem");
102
+ const paymentRequirements = {
103
+ scheme: "exact",
104
+ network: _network,
105
+ maxAmountRequired: usdcAtomicAmount,
106
+ resource: `${request.method} ${request.url}`,
107
+ description: routeConfig.description || `Payment for ${request.method} ${request.url}`,
108
+ mimeType: "application/json",
109
+ payTo: getAddress(_payTo),
110
+ maxTimeoutSeconds: 300,
111
+ asset: getAddress(USDC_ADDRESS),
112
+ extra: USDC_EIP712,
113
+ };
114
+
115
+ // No payment — return 402
127
116
  if (!paymentHeader) {
128
- const paymentRequirements = {
129
- scheme: "exact",
130
- network: _network,
131
- amount: usdcAmount,
132
- asset: USDC_BASE,
133
- payTo: _payTo,
134
- maxTimeoutSeconds: 300,
135
- extra: { name: "USD Coin", version: "2" },
136
- };
137
- const paymentRequired = {
138
- x402Version: 2,
117
+ reply.code(402).send({
118
+ x402Version: 1,
139
119
  error: "Payment Required",
140
- resource: {
141
- url: `${request.method} ${request.url}`,
142
- description: routeConfig.description || `Payment for ${request.method} ${request.url}`,
143
- mimeType: "application/json",
144
- },
145
120
  accepts: [paymentRequirements],
146
- };
147
-
148
- const encoded = Buffer.from(JSON.stringify(paymentRequired)).toString("base64");
149
- reply
150
- .code(402)
151
- .header("Payment-Required", encoded)
152
- .send({
153
- error: "Payment Required",
154
- price,
155
- network: _network,
156
- payTo: _payTo,
157
- protocol: "x402",
158
- x402Version: 2,
159
- ...(routeConfig.variable ? { variable: true, min: routeConfig.min, max: routeConfig.max, priceField: routeConfig.priceField || "dollars" } : {}),
160
- });
121
+ price,
122
+ network: _network,
123
+ payTo: _payTo,
124
+ protocol: "x402",
125
+ ...(routeConfig.variable ? { variable: true, min: routeConfig.min, max: routeConfig.max, priceField: routeConfig.priceField || "dollars" } : {}),
126
+ });
161
127
  return;
162
128
  }
163
129
 
164
- // Payment present — decode
165
- let paymentPayload;
130
+ // Decode payment
131
+ let decodedPayment;
166
132
  try {
167
- paymentPayload = JSON.parse(Buffer.from(paymentHeader, "base64").toString());
168
- } catch {
169
- reply.code(400).send({ error: "Invalid payment encoding" });
133
+ const { exact } = await import("x402/schemes");
134
+ const libraryDecoded = exact.evm.decodePayment(paymentHeader);
135
+
136
+ // Parse raw payload for metadata
137
+ const raw = JSON.parse(Buffer.from(paymentHeader, "base64").toString("utf-8"));
138
+
139
+ decodedPayment = {
140
+ x402Version: raw.x402Version || 1,
141
+ scheme: raw.scheme || "exact",
142
+ network: raw.network || _network,
143
+ payload: libraryDecoded.payload,
144
+ };
145
+ } catch (err) {
146
+ reply.code(402).send({ error: "Invalid payment format: " + err.message, accepts: [paymentRequirements] });
170
147
  return;
171
148
  }
172
149
 
173
- if (!paymentPayload.x402Version) paymentPayload.x402Version = 2;
174
-
175
- const paymentRequirements = {
176
- scheme: "exact",
177
- network: _network,
178
- amount: usdcAmount,
179
- asset: USDC_BASE,
180
- payTo: _payTo,
181
- maxTimeoutSeconds: 300,
182
- extra: { name: "USD Coin", version: "2" },
183
- };
150
+ // For variable pricing, use user's actual payment value as maxAmountRequired
151
+ const userValue = decodedPayment.payload.authorization.value;
152
+ const actualRequirements = { ...paymentRequirements, maxAmountRequired: userValue };
184
153
 
185
- if (!paymentPayload.accepted) paymentPayload.accepted = paymentRequirements;
186
-
187
- // Use x402 SDK if available
188
- if (_x402Server) {
189
- try {
190
- // Verify via facilitator
191
- const verifyResult = await _x402Server.verifyPayment(paymentPayload, paymentRequirements);
192
- if (!verifyResult.isValid) {
193
- reply.code(402).send({ error: "Payment verification failed", reason: verifyResult.invalidReason, price, payTo: _payTo });
194
- return;
195
- }
196
-
197
- // Settle via facilitator (executes on-chain transfer)
198
- const settleResult = await _x402Server.settlePayment(paymentPayload, paymentRequirements);
199
- if (!settleResult.success) {
200
- reply.code(402).send({ error: "Payment settlement failed", reason: settleResult.errorReason, price, payTo: _payTo });
201
- return;
202
- }
203
-
204
- const txHash = settleResult.transaction || null;
205
- const payer = settleResult.payer || verifyResult.payer || paymentPayload.payload?.authorization?.from || null;
206
-
207
- request.x402 = { paid: true, amount: price, receipt: settleResult, txHash, from: payer };
208
- _logPayment({ route: request.url, method: request.method, amount: price, from: payer, txHash, verified: true, settled: "facilitator", timestamp: Date.now() });
209
- return;
210
- } catch (err) {
211
- console.log(` ⚠️ x402 SDK error: ${err.message}`);
212
- reply.code(402).send({ error: "Payment processing failed", reason: err.message, price, payTo: _payTo });
154
+ // Verify via facilitator
155
+ try {
156
+ const verifyResult = await _facilitatorClient.verify(decodedPayment, actualRequirements);
157
+ if (!verifyResult.isValid) {
158
+ console.log(` ⚠️ x402 verify failed: ${verifyResult.invalidReason}`);
159
+ reply.code(402).send({ error: verifyResult.invalidReason || "Payment verification failed", accepts: [paymentRequirements], payer: verifyResult.payer });
213
160
  return;
214
161
  }
162
+ } catch (err) {
163
+ console.log(` ⚠️ x402 verify error: ${err.message}`);
164
+ reply.code(402).send({ error: "Payment verification failed: " + err.message, accepts: [paymentRequirements] });
165
+ return;
215
166
  }
216
167
 
217
- // Fallback: direct verification (no on-chain settlement)
218
- reply.code(500).send({ error: "x402 SDK not available — install @x402/core @x402/evm" });
168
+ // Payment verified attach info to request
169
+ const payer = decodedPayment.payload.authorization.from;
170
+ request.x402 = { paid: true, amount: price, from: payer, value: userValue, verified: true };
171
+
172
+ // Log payment
173
+ _logPayment({ route: request.url, method: request.method, amount: price, from: payer, verified: true, timestamp: Date.now() });
174
+ });
175
+
176
+ // Settlement hook — settle AFTER successful handler response
177
+ fastify.addHook("onSend", async (request, reply, payload) => {
178
+ if (!request.x402?.paid || !_facilitatorClient) return payload;
179
+ if (reply.statusCode >= 400) return payload;
180
+
181
+ try {
182
+ const paymentHeader = request.headers["x-payment"] || request.headers["payment-signature"];
183
+ const { exact } = require("x402/schemes");
184
+ const raw = JSON.parse(Buffer.from(paymentHeader, "base64").toString("utf-8"));
185
+ const libraryDecoded = exact.evm.decodePayment(paymentHeader);
186
+ const decodedPayment = { x402Version: raw.x402Version || 1, scheme: raw.scheme || "exact", network: raw.network || _network, payload: libraryDecoded.payload };
187
+
188
+ const userValue = decodedPayment.payload.authorization.value;
189
+ const { getAddress } = await import("viem");
190
+ const requirements = {
191
+ scheme: "exact", network: _network, maxAmountRequired: userValue,
192
+ resource: `${request.method} ${request.url}`, description: "", mimeType: "application/json",
193
+ payTo: getAddress(_payTo), maxTimeoutSeconds: 300, asset: getAddress(USDC_ADDRESS), extra: USDC_EIP712,
194
+ };
195
+
196
+ const settleResult = await _facilitatorClient.settle(decodedPayment, requirements);
197
+ if (settleResult.success) {
198
+ request.x402.txHash = settleResult.transaction;
199
+ request.x402.settled = true;
200
+ console.log(` 💰 x402 settled: ${settleResult.transaction || "confirmed"} (${request.x402.amount} from ${request.x402.from?.slice(0, 10)})`);
201
+ // Update payment log
202
+ _logPayment({ route: request.url, method: request.method, amount: request.x402.amount, from: request.x402.from, txHash: settleResult.transaction, verified: true, settled: true, timestamp: Date.now() });
203
+ } else {
204
+ console.log(` ⚠️ x402 settle failed: ${settleResult.errorReason || "unknown"}`);
205
+ }
206
+ } catch (err) {
207
+ console.log(` ⚠️ x402 settle error: ${err.message}`);
208
+ }
209
+ return payload;
219
210
  });
220
211
 
221
- // ── Public pricing endpoint ──
212
+ // Public info endpoint
222
213
  fastify.get("/x402/info", async () => ({
223
- payTo: _payTo,
224
- network: _network,
225
- facilitator: _facilitatorUrl,
226
- protocol: "x402",
227
- x402Version: 2,
228
- sdkLoaded: !!_x402Server,
214
+ payTo: _payTo, network: _network, protocol: "x402", x402Version: 1,
215
+ facilitatorLoaded: !!_facilitatorClient,
229
216
  docs: "https://docs.cdp.coinbase.com/x402/welcome",
230
217
  }));
231
218
  }