wolverine-ai 5.4.3 → 5.5.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.4.3",
3
+ "version": "5.5.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": {
@@ -190,15 +190,15 @@ const SEED_DOCS = [
190
190
  metadata: { topic: "notifications" },
191
191
  },
192
192
  {
193
- text: "x402 paid APIs — turn any route into a paid API with one flag. Register the plugin: fastify.register(require('wolverine-ai/src/middleware/x402-fastify')). Add { config: { x402: { price: '$0.10' } } } to any route. Routes WITHOUT x402 config are unaffected. Two modes: (1) Fixed price: { x402: { price: '$0.10' } }. (2) Variable price: { x402: { variable: true, min: '$1', max: '$10000', priceField: 'dollars' } }. Uses @coinbase/x402 facilitator (built-in, no auth config needed) + x402/verify for payment verification and on-chain settlement. Protocol is x402 v1 network must be 'base' (NOT 'eip155:8453'). Payment header: X-PAYMENT or payment-signature (base64 JSON). Verify happens in preHandler hook, settle happens in onSend hook (after handler succeeds). Vault wallet auto-detected as payTo. Requires: Node 22+, CDP_API_KEY_ID + CDP_API_KEY_SECRET in .env.local (get free at cdp.coinbase.com), npm packages @coinbase/x402 + x402 + viem. CRITICAL SETUP NOTES: (1) network MUST be 'base' not 'eip155:8453' — the v1 SDK uses human-readable names. (2) Do NOT set a facilitator URL in settings.json — the @coinbase/x402 package has it built-in. (3) paymentRequirements.resource must be a full URL (https://host/path) not 'POST /path'. (4) paymentRequirements.asset must be checksummed via viem's getAddress(). (5) paymentRequirements.extra must be { name: 'USD Coin', version: '2' } for USDC EIP-712 domain. (6) maxAmountRequired must match the user's payment value for variable pricing. (7) Frontend payment payload format: { x402Version: 1, scheme: 'exact', network: 'base', payload: { authorization: { from, to, value (decimal string), validAfter (string), validBefore (string), nonce (0x hex) }, signature } }. Dashboard tracks x402 payments, revenue by route, USDC/ETH balances.",
193
+ text: "x402 paid APIs — turn any route into a paid API with one flag. Register the plugin: fastify.register(require('wolverine-ai/src/middleware/x402-fastify')). Add { config: { x402: { price: '$0.10' } } } to any route. Routes WITHOUT x402 config are unaffected. Two modes: (1) Fixed price: { x402: { price: '$0.10' } }. (2) Variable price: { x402: { variable: true, min: '$1', max: '$10000', priceField: 'dollars' } }. Uses x402 v2 protocol: @x402/core SDK (x402ResourceServer + ExactEvmScheme) with @coinbase/x402 facilitator for CDP auth. The trick: pass @coinbase/x402's facilitator config to @x402/core's HTTPFacilitatorClient this gives v2 protocol with working CDP JWT auth. Network is CAIP-2 format 'eip155:8453' (Base mainnet). Payment header: X-PAYMENT or payment-signature (base64 JSON). Verify AND settle both happen in preHandler handler only runs after USDC moves on-chain. Vault wallet auto-detected as payTo. Requires: Node 22+, CDP_API_KEY_ID + CDP_API_KEY_SECRET in .env.local (free at cdp.coinbase.com), npm packages @coinbase/x402 + @x402/core + @x402/evm + viem. Auto-installed on startup when vault exists. CRITICAL: (1) Do NOT set facilitator URL in settings.json. (2) paymentRequirements uses 'amount' field (v2) not 'maxAmountRequired' (v1). (3) paymentRequirements.extra = { name: 'USD Coin', version: '2' } for EIP-712. (4) Frontend payload: { x402Version: 2, scheme: 'exact', network: 'eip155:8453', payload: { authorization: { from, to, value (decimal string), validAfter (string), validBefore (string), nonce (0x hex) }, signature } }. (5) Values must be decimal strings not hex. Dashboard tracks x402 payments, revenue by route, USDC/ETH balances.",
194
194
  metadata: { topic: "x402-paid-apis" },
195
195
  },
196
196
  {
197
- text: "x402 examples — making a paid API is as simple as adding a route. FREE route: fastify.get('/api/data', async (req) => { return getData(); }). PAID route: fastify.get('/api/premium', { config: { x402: { price: '$0.01' } } }, async (req) => { return { data: 'premium', paid: req.x402?.amount }; }). CREDIT PURCHASE: fastify.post('/buy', { config: { x402: { variable: true, min: '$1', max: '$10000', priceField: 'dollars' } } }, async (req) => { const credits = parseFloat(req.x402.amount.replace('$','')) * 100; return { credits }; }). Frontend integration: sign EIP-3009 TransferWithAuthorization with eth_signTypedData_v4, build payload as { x402Version: 1, scheme: 'exact', network: 'base', payload: { authorization: {...}, signature } }, base64 encode, send as X-PAYMENT header. Values MUST be decimal strings ('1000000' for $1) NOT hex ('0xf4240'). validAfter = String(now - 600), validBefore = String(now + 300). The facilitator verifies signature then settles on-chain (calls transferWithAuthorization on USDC contract). Handler only runs after verification passes. Settlement happens after handler returns success (onSend hook). req.x402 = { paid: true, amount: '$5.00', from: '0x...', txHash, settled: true }.",
197
+ text: "x402 examples — making a paid API is as simple as adding a route. FREE route: fastify.get('/api/data', async (req) => { return getData(); }). PAID route: fastify.get('/api/premium', { config: { x402: { price: '$0.01' } } }, async (req) => { return { data: 'premium', paid: req.x402.amount, txHash: req.x402.txHash }; }). CREDIT PURCHASE: fastify.post('/buy', { config: { x402: { variable: true, min: '$1', max: '$10000', priceField: 'dollars' } } }, async (req) => { const credits = parseFloat(req.x402.amount.replace('$','')) * 100; return { credits, txHash: req.x402.txHash }; }). Frontend integration: sign EIP-3009 TransferWithAuthorization with eth_signTypedData_v4, build v2 payload as { x402Version: 2, scheme: 'exact', network: 'eip155:8453', payload: { authorization: {...}, signature } }, base64 encode, send as X-PAYMENT header. Values MUST be decimal strings ('1000000' for $1) NOT hex. validAfter = String(now - 600), validBefore = String(now + 300). Both verify + settle happen in preHandler BEFORE handler runs. Handler only executes after USDC moves on-chain. req.x402 = { paid: true, amount: '$5.00', from: '0x...', txHash: '0x...', settled: true }.",
198
198
  metadata: { topic: "x402-examples" },
199
199
  },
200
200
  {
201
- text: "x402 troubleshooting — common issues and fixes. (1) 'Invalid network': using 'eip155:8453' instead of 'base' — the @coinbase/x402 v1 SDK uses human-readable network names. (2) 'Unauthorized' from facilitator: either wrong CDP_API_KEY_ID/SECRET, or a facilitator URL override in settings.json pointing to api.cdp.coinbase.com (remove it, let the built-in handle auth). (3) '400 Bad Request': payload format mismatch — ensure x402Version:1, scheme:'exact', network:'base'. Don't use exact.evm.decodePayment() on the server, parse the base64 JSON directly. (4) 'invalid_exact_evm_payload_signature': the EIP-712 signature is wrong — check that the frontend signs with the correct domain (name:'USD Coin', version:'2', chainId:8453, verifyingContract:USDC address). (5) USDC not moving: the old middleware only verified signatures without calling the facilitator settle — must use @coinbase/x402 facilitator which handles verify+settle. (6) Frontend value showing as hex (0xf4240): must use decimal strings. (7) SDK not loading: @coinbase/x402 + x402 are ESM packages, need Node 22+. On Node 18 they fail with ERR_REQUIRE_ESM. (8) Packages disappearing after --update: add @coinbase/x402, x402, viem to package.json dependencies so npm install preserves them.",
201
+ text: "x402 troubleshooting — common issues and fixes. (1) 'Unauthorized' from CDP facilitator: the @coinbase/x402 package handles JWT auth internally using CDP_API_KEY_ID + CDP_API_KEY_SECRET from env vars do NOT set a facilitator URL in settings.json. (2) '400 Bad Request': payload format mismatch — v2 uses x402Version:2, network:'eip155:8453', amount (not maxAmountRequired). Parse base64 JSON directly, don't use decodePayment(). (3) 'invalid_exact_evm_payload_signature': EIP-712 signature wrong — check domain (name:'USD Coin', version:'2', chainId:8453, verifyingContract:USDC). (4) USDC not moving: old middleware only verified without settling — must use x402ResourceServer.verifyPayment() + settlePayment() which goes through the CDP facilitator. (5) Frontend value as hex (0xf4240): must use decimal strings ('1000000'). (6) ESM errors: @coinbase/x402 needs Node 22+. (7) Packages disappearing after --update: they're in package.json dependencies + auto-installed on startup when vault exists. (8) Credits granted before settlement: verify+settle must both be in preHandler, not onSend. (9) v2 auth trick: pass @coinbase/x402's facilitator config to @x402/core HTTPFacilitatorClient this gives v2 protocol with CDP auth that actually works.",
202
202
  metadata: { topic: "x402-troubleshooting" },
203
203
  },
204
204
  {
@@ -77,7 +77,7 @@ function ensureX402Deps(cwd) {
77
77
  console.log(chalk.blue(" 📦 Installing x402 payment dependencies..."));
78
78
  try {
79
79
  const { execSync } = require("child_process");
80
- execSync("npm install @coinbase/x402 x402 viem --no-save --ignore-engines 2>/dev/null", {
80
+ execSync("npm install @coinbase/x402 @x402/core @x402/evm x402 viem --no-save --ignore-engines 2>/dev/null", {
81
81
  cwd,
82
82
  stdio: "pipe",
83
83
  timeout: 60000,
@@ -4,15 +4,16 @@ const path = require("path");
4
4
  /**
5
5
  * x402 Fastify Plugin — add crypto payments to any route with one flag.
6
6
  *
7
- * Uses @coinbase/x402 facilitator + x402/verify for payment verification
8
- * and on-chain settlement. Matches the working pattern from blockaid-scanner.
7
+ * Uses x402 v2 protocol (@x402/core + @x402/evm) with @coinbase/x402
8
+ * facilitator for CDP authentication. Verify + settle both happen in
9
+ * preHandler — the route handler only runs after USDC moves on-chain.
9
10
  *
10
- * Two modes:
11
- * Fixed price: { x402: { price: "$0.01" } }
12
- * Variable price: { x402: { variable: true, min: "$1", max: "$10000" } }
11
+ * Setup:
12
+ * 1. wolverine --init-vault
13
+ * 2. Set CDP_API_KEY_ID + CDP_API_KEY_SECRET in .env.local
14
+ * 3. Add { config: { x402: { price: "$0.01" } } } to any route
13
15
  */
14
16
 
15
- // Node 18 needs globalThis.crypto
16
17
  if (!globalThis.crypto) {
17
18
  globalThis.crypto = require("crypto").webcrypto;
18
19
  }
@@ -21,8 +22,8 @@ const USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
21
22
  const USDC_EIP712 = { name: "USD Coin", version: "2" };
22
23
 
23
24
  let _payTo = null;
24
- let _network = "base"; // v1 format, not CAIP-2
25
- let _facilitatorClient = null;
25
+ let _network = "eip155:8453";
26
+ let _x402Server = null;
26
27
 
27
28
  async function x402Plugin(fastify, opts) {
28
29
  _payTo = opts.payTo || null;
@@ -43,14 +44,18 @@ async function x402Plugin(fastify, opts) {
43
44
  } catch {}
44
45
  }
45
46
 
46
- // Initialize facilitator from @coinbase/x402 (ESM packages, need dynamic import)
47
+ // Initialize v2 x402 SDK with @coinbase/x402 facilitator auth
47
48
  try {
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)`);
49
+ const { facilitator } = require("@coinbase/x402");
50
+ const { x402ResourceServer, HTTPFacilitatorClient } = require("@x402/core/server");
51
+ const { ExactEvmScheme } = require("@x402/evm/exact/server");
52
+
53
+ const client = new HTTPFacilitatorClient(facilitator);
54
+ _x402Server = new x402ResourceServer(client);
55
+ _x402Server.register("eip155:*", new ExactEvmScheme());
56
+ console.log(` 💰 x402: v2 SDK loaded (ExactEvmScheme + CDP facilitator)`);
52
57
  } catch (err) {
53
- console.log(` ⚠️ x402: facilitator init failed (${err.message}) — install @coinbase/x402 x402`);
58
+ console.log(` ⚠️ x402: SDK init failed (${err.message})`);
54
59
  }
55
60
 
56
61
  if (_payTo) {
@@ -61,14 +66,8 @@ async function x402Plugin(fastify, opts) {
61
66
  fastify.addHook("preHandler", async (request, reply) => {
62
67
  const routeConfig = request.routeOptions?.config?.x402 || request.routeConfig?.x402 || request.context?.config?.x402;
63
68
  if (!routeConfig) return;
64
- if (!_payTo) {
65
- reply.code(500).send({ error: "x402 not configuredno wallet address" });
66
- return;
67
- }
68
- if (!_facilitatorClient) {
69
- reply.code(500).send({ error: "x402 facilitator not loaded — install @coinbase/x402 x402" });
70
- return;
71
- }
69
+ if (!_payTo) { reply.code(500).send({ error: "x402 not configured — run wolverine --init-vault" }); return; }
70
+ if (!_x402Server) { reply.code(500).send({ error: "x402 SDK not loadedinstall @coinbase/x402 @x402/core @x402/evm" }); return; }
72
71
 
73
72
  // Determine dollar amount
74
73
  let dollarAmount;
@@ -91,36 +90,29 @@ async function x402Plugin(fastify, opts) {
91
90
  if (!dollarAmount) { reply.code(500).send({ error: "x402 route missing price config" }); return; }
92
91
  }
93
92
 
94
- const usdcAtomicAmount = String(Math.round(dollarAmount * 1e6));
93
+ const usdcAmount = String(Math.round(dollarAmount * 1e6));
95
94
  const price = "$" + dollarAmount.toFixed(2);
96
-
97
- // Check for payment header (X-PAYMENT for v1 compat, payment-signature for v2)
98
95
  const paymentHeader = request.headers["x-payment"] || request.headers["payment-signature"];
99
96
 
100
- // Build payment requirements (v1 format matching @coinbase/x402)
97
+ // Build v2 payment requirements
101
98
  const { getAddress } = await import("viem");
102
- // Build full resource URL (required by facilitator)
103
99
  const proto = request.headers["x-forwarded-proto"] || "https";
104
100
  const host = request.headers["x-forwarded-host"] || request.headers.host || "localhost";
105
- const resourceUrl = `${proto}://${host}${request.url}`;
106
101
 
107
102
  const paymentRequirements = {
108
103
  scheme: "exact",
109
104
  network: _network,
110
- maxAmountRequired: usdcAtomicAmount,
111
- resource: resourceUrl,
112
- description: routeConfig.description || `Payment of $${dollarAmount.toFixed(2)} USDC`,
113
- mimeType: "application/json",
105
+ amount: usdcAmount,
106
+ asset: getAddress(USDC_ADDRESS),
114
107
  payTo: getAddress(_payTo),
115
108
  maxTimeoutSeconds: 60,
116
- asset: getAddress(USDC_ADDRESS),
117
109
  extra: USDC_EIP712,
118
110
  };
119
111
 
120
112
  // No payment — return 402
121
113
  if (!paymentHeader) {
122
114
  reply.code(402).send({
123
- x402Version: 1,
115
+ x402Version: 2,
124
116
  error: "Payment Required",
125
117
  accepts: [paymentRequirements],
126
118
  price,
@@ -132,55 +124,52 @@ async function x402Plugin(fastify, opts) {
132
124
  return;
133
125
  }
134
126
 
135
- // Decode payment — parse raw payload directly (matching working project pattern)
136
- let decodedPayment;
127
+ // Decode payment
128
+ let paymentPayload;
137
129
  try {
138
130
  const raw = JSON.parse(Buffer.from(paymentHeader, "base64").toString("utf-8"));
131
+ if (!raw.payload?.authorization || !raw.payload?.signature) throw new Error("Missing authorization or signature");
139
132
 
140
- // Validate required fields
141
- if (!raw.payload?.authorization || !raw.payload?.signature) {
142
- throw new Error("Missing authorization or signature");
143
- }
144
-
145
- decodedPayment = {
146
- x402Version: raw.x402Version || 1,
133
+ paymentPayload = {
134
+ x402Version: raw.x402Version || 2,
147
135
  scheme: raw.scheme || "exact",
148
136
  network: raw.network || _network,
149
137
  payload: raw.payload,
150
138
  };
139
+ if (raw.accepted) paymentPayload.accepted = raw.accepted;
140
+ if (raw.resource) paymentPayload.resource = raw.resource;
151
141
  } catch (err) {
152
- reply.code(402).send({ error: "Invalid payment format: " + err.message, accepts: [paymentRequirements] });
142
+ reply.code(402).send({ error: "Invalid payment: " + err.message, accepts: [paymentRequirements] });
153
143
  return;
154
144
  }
155
145
 
156
- // For variable pricing, use user's actual payment value as maxAmountRequired
157
- const userValue = decodedPayment.payload.authorization.value;
158
- const actualRequirements = { ...paymentRequirements, maxAmountRequired: userValue };
146
+ // For variable pricing, match maxAmountRequired to user's payment
147
+ const userValue = paymentPayload.payload.authorization.value;
148
+ const actualRequirements = { ...paymentRequirements, amount: userValue };
159
149
 
160
150
  // Verify via facilitator
161
151
  try {
162
- console.log(` 💰 x402 verify: from=${decodedPayment.payload?.authorization?.from} value=${decodedPayment.payload?.authorization?.value} network=${decodedPayment.network}`);
163
- const verifyResult = await _facilitatorClient.verify(decodedPayment, actualRequirements);
152
+ console.log(` 💰 x402 verify: from=${paymentPayload.payload?.authorization?.from} value=${userValue} network=${paymentPayload.network}`);
153
+ const verifyResult = await _x402Server.verifyPayment(paymentPayload, actualRequirements);
164
154
  if (!verifyResult.isValid) {
165
155
  console.log(` ⚠️ x402 verify failed: ${verifyResult.invalidReason} ${verifyResult.invalidMessage || ""}`);
166
- reply.code(402).send({ error: verifyResult.invalidReason || "Payment verification failed", message: verifyResult.invalidMessage, accepts: [paymentRequirements], payer: verifyResult.payer });
156
+ reply.code(402).send({ error: verifyResult.invalidReason, message: verifyResult.invalidMessage, payer: verifyResult.payer });
167
157
  return;
168
158
  }
169
159
  } catch (err) {
170
- // Extract detailed error from the x402 SDK
171
160
  const detail = err.invalidReason || err.invalidMessage || err.cause?.message || "";
172
161
  console.log(` ⚠️ x402 verify error: ${err.message} | ${detail}`);
173
- reply.code(402).send({ error: err.invalidReason || err.message, message: err.invalidMessage || detail, accepts: [paymentRequirements] });
162
+ reply.code(402).send({ error: err.invalidReason || err.message, message: err.invalidMessage || detail });
174
163
  return;
175
164
  }
176
165
 
177
- // Settle via facilitator BEFORE handler runs — USDC must move before granting access
178
- const payer = decodedPayment.payload.authorization.from;
166
+ // Settle via facilitator — USDC moves on-chain BEFORE handler runs
167
+ const payer = paymentPayload.payload.authorization.from;
179
168
  try {
180
- const settleResult = await _facilitatorClient.settle(decodedPayment, actualRequirements);
169
+ const settleResult = await _x402Server.settlePayment(paymentPayload, actualRequirements);
181
170
  if (!settleResult.success) {
182
171
  console.log(` ⚠️ x402 settle failed: ${settleResult.errorReason || "unknown"}`);
183
- reply.code(402).send({ error: "Payment settlement failed", reason: settleResult.errorReason });
172
+ reply.code(402).send({ error: "Settlement failed", reason: settleResult.errorReason });
184
173
  return;
185
174
  }
186
175
  const txHash = settleResult.transaction || null;
@@ -190,17 +179,15 @@ async function x402Plugin(fastify, opts) {
190
179
  _logPayment({ route: request.url, method: request.method, amount: price, from: payer, txHash, verified: true, settled: true, timestamp: Date.now() });
191
180
  } catch (err) {
192
181
  console.log(` ⚠️ x402 settle error: ${err.message}`);
193
- reply.code(402).send({ error: "Payment settlement failed: " + err.message });
182
+ reply.code(402).send({ error: "Settlement failed: " + err.message });
194
183
  return;
195
184
  }
196
185
  });
197
186
 
198
- // No onSend settle needed — settlement happens in preHandler before handler runs
199
-
200
187
  // Public info endpoint
201
188
  fastify.get("/x402/info", async () => ({
202
- payTo: _payTo, network: _network, protocol: "x402", x402Version: 1,
203
- facilitatorLoaded: !!_facilitatorClient,
189
+ payTo: _payTo, network: _network, protocol: "x402", x402Version: 2,
190
+ sdkLoaded: !!_x402Server,
204
191
  docs: "https://docs.cdp.coinbase.com/x402/welcome",
205
192
  }));
206
193
  }