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