wolverine-ai 5.2.7 → 5.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wolverine-ai",
3
- "version": "5.2.7",
3
+ "version": "5.2.9",
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": {
@@ -4,19 +4,25 @@ const path = require("path");
4
4
  /**
5
5
  * x402 Fastify Plugin — add crypto payments to any route with one flag.
6
6
  *
7
- * Implements the x402 v2 protocol with CDP facilitator for on-chain settlement.
8
- * Compatible with @x402/fetch, @x402/evm client SDKs and x402 Bazaar.
7
+ * Uses the official @x402/core SDK for payment verification and settlement
8
+ * via the CDP facilitator. Compatible with x402 Bazaar for API discovery.
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
16
+ if (!globalThis.crypto) {
17
+ globalThis.crypto = require("crypto").webcrypto;
18
+ }
19
+
15
20
  const USDC_BASE = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
16
21
 
17
22
  let _payTo = null;
18
23
  let _network = "eip155:8453";
19
24
  let _facilitatorUrl = "https://api.cdp.coinbase.com/platform/v2/x402";
25
+ let _x402Server = null; // x402ResourceServer instance
20
26
 
21
27
  async function x402Plugin(fastify, opts) {
22
28
  _network = opts.network || _network;
@@ -40,18 +46,46 @@ async function x402Plugin(fastify, opts) {
40
46
  } catch {}
41
47
  }
42
48
 
43
- // Auto-select facilitator based on network
44
- if (!opts.facilitator) {
45
- if (!process.env.CDP_API_KEY_ID) {
46
- console.log(` ⚠️ x402: CDP_API_KEY_ID not set — facilitator auth may fail`);
47
- }
48
- }
49
-
50
49
  if (_payTo) {
51
50
  console.log(` 💰 x402: payments to ${_payTo.slice(0, 6)}...${_payTo.slice(-4)} on ${_network}`);
52
51
  console.log(` 💰 x402: facilitator ${_facilitatorUrl}`);
53
52
  }
54
53
 
54
+ // Initialize x402 SDK server with CDP facilitator
55
+ 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)`);
85
+ } catch (err) {
86
+ console.log(` ⚠️ x402: SDK init failed (${err.message}) — using direct verification`);
87
+ }
88
+
55
89
  // ── Route-level x402 hook ──
56
90
  fastify.addHook("preHandler", async (request, reply) => {
57
91
  const routeConfig = request.routeOptions?.config?.x402 || request.routeConfig?.x402 || request.context?.config?.x402;
@@ -85,13 +119,11 @@ async function x402Plugin(fastify, opts) {
85
119
  if (!dollarAmount) { reply.code(500).send({ error: "x402 route missing price config" }); return; }
86
120
  }
87
121
 
88
- // USDC amount in atomic units (6 decimals)
89
122
  const usdcAmount = String(Math.round(dollarAmount * 1e6));
90
123
  const price = "$" + dollarAmount.toFixed(2);
91
-
92
124
  const paymentHeader = request.headers["payment-signature"] || request.headers["x-payment"];
93
125
 
94
- // No payment — return 402 with x402 v2 payment requirements
126
+ // No payment — return 402 with payment requirements
95
127
  if (!paymentHeader) {
96
128
  const paymentRequirements = {
97
129
  scheme: "exact",
@@ -100,7 +132,7 @@ async function x402Plugin(fastify, opts) {
100
132
  asset: USDC_BASE,
101
133
  payTo: _payTo,
102
134
  maxTimeoutSeconds: 300,
103
- extra: { name: "USD Coin", version: "2" }, // EIP-712 domain params for USDC
135
+ extra: { name: "USD Coin", version: "2" },
104
136
  };
105
137
  const paymentRequired = {
106
138
  x402Version: 2,
@@ -129,7 +161,17 @@ async function x402Plugin(fastify, opts) {
129
161
  return;
130
162
  }
131
163
 
132
- // Payment present — decode, verify via facilitator, then settle
164
+ // Payment present — decode
165
+ let paymentPayload;
166
+ try {
167
+ paymentPayload = JSON.parse(Buffer.from(paymentHeader, "base64").toString());
168
+ } catch {
169
+ reply.code(400).send({ error: "Invalid payment encoding" });
170
+ return;
171
+ }
172
+
173
+ if (!paymentPayload.x402Version) paymentPayload.x402Version = 2;
174
+
133
175
  const paymentRequirements = {
134
176
  scheme: "exact",
135
177
  network: _network,
@@ -140,57 +182,40 @@ async function x402Plugin(fastify, opts) {
140
182
  extra: { name: "USD Coin", version: "2" },
141
183
  };
142
184
 
143
- // Decode the payment payload
144
- let paymentPayload;
145
- try {
146
- const decoded = Buffer.from(paymentHeader, "base64").toString();
147
- paymentPayload = JSON.parse(decoded);
148
- } catch {
149
- reply.code(400).send({ error: "Invalid payment encoding" });
150
- return;
151
- }
152
-
153
- // Ensure x402Version is set
154
- if (!paymentPayload.x402Version) {
155
- paymentPayload.x402Version = 2;
156
- }
157
-
158
- // Ensure the accepted requirements are included (x402 v2 spec)
159
- if (!paymentPayload.accepted) {
160
- paymentPayload.accepted = paymentRequirements;
161
- }
185
+ if (!paymentPayload.accepted) paymentPayload.accepted = paymentRequirements;
162
186
 
163
- // Step 1: Verify via facilitator
164
- const verifyResult = await _facilitatorCall("/verify", paymentPayload, paymentRequirements);
165
- if (!verifyResult.ok) {
166
- reply.code(402).send({
167
- error: "Payment verification failed",
168
- reason: verifyResult.reason,
169
- price,
170
- payTo: _payTo,
171
- });
172
- return;
173
- }
174
-
175
- // Step 2: Settle via facilitator (executes on-chain transfer)
176
- const settleResult = await _facilitatorCall("/settle", paymentPayload, paymentRequirements);
177
- if (!settleResult.ok) {
178
- reply.code(402).send({
179
- error: "Payment settlement failed",
180
- reason: settleResult.reason,
181
- price,
182
- payTo: _payTo,
183
- });
184
- return;
187
+ // Use x402 SDK if available
188
+ if (_x402Server) {
189
+ try {
190
+ // Verify
191
+ const verifyResult = await _x402Server.verify(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
198
+ const settleResult = await _x402Server.settle(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 });
213
+ return;
214
+ }
185
215
  }
186
216
 
187
- // Payment verified AND settled on-chain
188
- const txHash = settleResult.data?.transaction || settleResult.data?.txHash || null;
189
- const payer = settleResult.data?.payer || verifyResult.data?.payer || paymentPayload.payload?.authorization?.from || null;
190
-
191
- reply.header("Payment-Response", JSON.stringify(settleResult.data || {}));
192
- request.x402 = { paid: true, amount: price, receipt: settleResult.data, txHash, from: payer };
193
- _logPayment({ route: request.url, method: request.method, amount: price, from: payer, txHash, verified: true, settled: "facilitator", timestamp: Date.now() });
217
+ // Fallback: direct verification (no on-chain settlement)
218
+ reply.code(500).send({ error: "x402 SDK not available install @x402/core @x402/evm" });
194
219
  });
195
220
 
196
221
  // ── Public pricing endpoint ──
@@ -200,89 +225,11 @@ async function x402Plugin(fastify, opts) {
200
225
  facilitator: _facilitatorUrl,
201
226
  protocol: "x402",
202
227
  x402Version: 2,
228
+ sdkLoaded: !!_x402Server,
203
229
  docs: "https://docs.cdp.coinbase.com/x402/welcome",
204
230
  }));
205
231
  }
206
232
 
207
- /**
208
- * Call the x402 facilitator — matches the exact format from @x402/core HTTPFacilitatorClient.
209
- * Uses fetch() for automatic redirect following (x402.org → www.x402.org).
210
- *
211
- * POST {facilitatorUrl}/verify or /settle
212
- * Body: { x402Version, paymentPayload, paymentRequirements }
213
- */
214
- async function _facilitatorCall(endpoint, paymentPayload, paymentRequirements) {
215
- try {
216
- const url = _facilitatorUrl + endpoint;
217
- const body = JSON.stringify({
218
- x402Version: paymentPayload.x402Version || 2,
219
- paymentPayload,
220
- paymentRequirements,
221
- });
222
-
223
- const controller = new AbortController();
224
- const timeout = setTimeout(() => controller.abort(), 30000);
225
-
226
- // Build headers — add CDP JWT auth if using CDP facilitator
227
- const headers = { "Content-Type": "application/json" };
228
- if (url.includes("api.cdp.coinbase.com") && process.env.CDP_API_KEY_ID) {
229
- try {
230
- // Use dynamic import for @coinbase/cdp-sdk (ESM-only jose dependency)
231
- const { generateJwt } = await import("@coinbase/cdp-sdk/auth");
232
- const parsedUrl = new URL(url);
233
- const jwt = await generateJwt({
234
- apiKeyId: process.env.CDP_API_KEY_ID,
235
- apiKeySecret: process.env.CDP_API_KEY_SECRET,
236
- requestMethod: "POST",
237
- requestHost: `https://${parsedUrl.host}`,
238
- requestPath: parsedUrl.pathname,
239
- });
240
- headers["Authorization"] = `Bearer ${jwt}`;
241
- } catch (authErr) {
242
- console.log(` ⚠️ x402 CDP auth failed: ${authErr.message}`);
243
- }
244
- }
245
-
246
- const response = await fetch(url, {
247
- method: "POST",
248
- headers,
249
- body,
250
- redirect: "follow",
251
- signal: controller.signal,
252
- });
253
-
254
- clearTimeout(timeout);
255
- const text = await response.text();
256
- let parsed;
257
- try { parsed = JSON.parse(text); } catch {
258
- console.log(` ⚠️ x402 facilitator ${endpoint} ${response.status}: unparseable response`);
259
- return { ok: false, reason: `facilitator_parse_error_${response.status}` };
260
- }
261
-
262
- if (!response.ok) {
263
- const reason = parsed.invalidReason || parsed.errorReason || parsed.error || `facilitator_${response.status}`;
264
- console.log(` ⚠️ x402 facilitator ${endpoint} ${response.status}: ${reason}`);
265
- return { ok: false, reason, data: parsed };
266
- }
267
-
268
- // Verify: check isValid. Settle: check success.
269
- if (endpoint === "/verify" && parsed.isValid === false) {
270
- console.log(` ⚠️ x402 verify rejected: ${parsed.invalidReason || "unknown"}`);
271
- return { ok: false, reason: parsed.invalidReason || "verification_rejected", data: parsed };
272
- }
273
- if (endpoint === "/settle" && parsed.success === false) {
274
- console.log(` ⚠️ x402 settle rejected: ${parsed.errorReason || "unknown"}`);
275
- return { ok: false, reason: parsed.errorReason || "settlement_rejected", data: parsed };
276
- }
277
-
278
- return { ok: true, data: parsed };
279
- } catch (err) {
280
- const reason = err.name === "AbortError" ? "facilitator_timeout" : "facilitator_unavailable: " + err.message;
281
- console.log(` ⚠️ x402 facilitator ${endpoint}: ${reason}`);
282
- return { ok: false, reason };
283
- }
284
- }
285
-
286
233
  function _logPayment(entry) {
287
234
  try {
288
235
  const logPath = path.join(process.cwd(), ".wolverine", "x402-payments.json");