wolverine-ai 5.0.0 → 5.0.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/bin/wolverine.js CHANGED
@@ -38,7 +38,7 @@ ${chalk.bold("Options:")}
38
38
  --workers <n> Force specific worker count
39
39
  --info Show system info and exit
40
40
  --init Scan server/ and build context map (routes, DB, config, deps)
41
- --x402-price Set x402 price: wolverine --x402-price "POST /api" "$0.01"
41
+ --x402-info Show x402 payment configuration
42
42
 
43
43
  ${chalk.bold("Configuration:")}
44
44
  server/config/settings.json Models, telemetry, limits, health checks
@@ -67,20 +67,27 @@ if (args.includes("--info")) {
67
67
  }
68
68
 
69
69
  // --init: scan server/ and build context map
70
- // --x402-price: set route pricing live
71
- if (args.includes("--x402-price")) {
72
- const idx = args.indexOf("--x402-price");
73
- const route = args[idx + 1];
74
- const price = args[idx + 2];
75
- if (!route || !price) {
76
- console.log(chalk.red(' Usage: wolverine --x402-price "POST /v1/chat/completions" "$0.001"'));
77
- process.exit(1);
78
- }
79
- const { setPrice } = require("../src/middleware/x402-fastify");
80
- const result = setPrice(route, price);
81
- console.log(chalk.green(` ✅ x402 price updated: ${result.route} → ${result.price}`));
82
- console.log(chalk.gray(" Change is live no restart needed."));
83
- process.exit(0);
70
+ // --x402-info: show payment configuration
71
+ if (args.includes("--x402-info")) {
72
+ (async () => {
73
+ let payTo = "not configured";
74
+ try {
75
+ const { getWalletAddress } = require("../src/vault/wallet-ops");
76
+ payTo = await getWalletAddress();
77
+ } catch {}
78
+ console.log(chalk.blue("\n 💰 x402 Payment Configuration\n"));
79
+ console.log(chalk.gray(` Wallet: ${payTo}`));
80
+ console.log(chalk.gray(` Network: Base (eip155:8453)`));
81
+ console.log(chalk.gray(` Token: USDC`));
82
+ console.log(chalk.gray(` Protocol: x402 (HTTP 402 Payment Required)`));
83
+ console.log(chalk.gray(` Facilitator: x402.org`));
84
+ console.log(chalk.gray(`\n Add to any Fastify route:`));
85
+ console.log(chalk.cyan(` { config: { x402: { price: "$0.10" } } }`));
86
+ console.log(chalk.gray(`\n Variable pricing (credit purchases):`));
87
+ console.log(chalk.cyan(` { config: { x402: { variable: true, min: "$1", max: "$10000", priceField: "dollars" } } }\n`));
88
+ process.exit(0);
89
+ })();
90
+ return;
84
91
  }
85
92
 
86
93
  if (args.includes("--init")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wolverine-ai",
3
- "version": "5.0.0",
3
+ "version": "5.0.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,7 +190,7 @@ const SEED_DOCS = [
190
190
  metadata: { topic: "notifications" },
191
191
  },
192
192
  {
193
- text: "Vault + x402 payments: encrypted key storage in .wolverine/vault/. AES-256-GCM encryption. master.key encrypts eth.vault (Ethereum private key). Generated on first run. Private key NEVER as JS string — Buffer only, wiped after use. wallet-ops: getWalletAddress(), signTransaction(), signMessage(). x402 protocol: HTTP 402 Payment Required for API monetization. Fastify middleware (src/middleware/x402-fastify.js) auto-gates routes with USDC payments on Base network. Config in settings.json x402.routes: { 'POST /api': { price: '$0.01' } }. Live price updates: PUT /x402/price or wolverine --x402-price 'POST /api' '$0.01'. Vault wallet is the payTo address. Facilitator (x402.org or Coinbase CDP) verifies and settles payments. Buyer SDKs (@x402/fetch, @x402/axios) auto-handle the 402→sign→retry flow. Vault skill actions: x402_pricing, x402_set_price, x402_remove_price.",
193
+ text: "Vault + x402 payments: encrypted key storage in .wolverine/vault/. AES-256-GCM. Private key NEVER as JS string — Buffer only, wiped after use. x402 protocol: HTTP 402 Payment Required for USDC payments on Base. Fastify plugin (src/middleware/x402-fastify.js) adds crypto to any route via config flag. Fixed price: { config: { x402: { price: '$0.10' } } }. Variable price (credit purchases): { config: { x402: { variable: true, min: '$1', max: '$10000', priceField: 'dollars' } } } — reads amount from request body. Flow: client requests 402 with Payment-Required header → client SDK signs USDC auth → retries with Payment-Signature → server verifies via facilitator → 200 + receipt. req.x402.paid/amount/txHash available in handler after payment. Vault wallet is auto-detected as payTo. GET /x402/info shows payment config. Buyer SDKs: @x402/fetch, @x402/axios auto-handle 402→sign→retry.",
194
194
  metadata: { topic: "vault-x402" },
195
195
  },
196
196
  {
@@ -1,91 +1,60 @@
1
- const path = require("path");
2
1
  const fs = require("fs");
2
+ const path = require("path");
3
3
 
4
4
  /**
5
- * x402 Payment Middleware for Fastify monetize any API route with USDC on Base.
5
+ * x402 Fastify Plugin add crypto payments to any route with one flag.
6
6
  *
7
- * Implements the x402 protocol (HTTP 402 Payment Required) for Fastify.
8
- * No official @x402/fastify exists, so we build directly on @x402/core.
7
+ * Makes it dead simple to accept USDC payments on Base network.
8
+ * The developer marks a route with x402 config, and the middleware
9
+ * handles the 402 → payment → verification → callback flow.
9
10
  *
10
- * Usage in server/index.js:
11
- * fastify.register(require('wolverine-ai/src/middleware/x402-fastify'), {
12
- * payTo: '0xYourAddress', // or auto from vault
13
- * network: 'eip155:8453', // Base mainnet
14
- * });
11
+ * Two modes:
12
+ * Fixed price: { x402: { price: "$0.01" } }
13
+ * Variable price: { x402: { variable: true, min: "$1", max: "$10000" } }
15
14
  *
16
- * Route pricing in settings.json:
17
- * "x402": {
18
- * "enabled": true,
19
- * "routes": {
20
- * "POST /v1/chat/completions": { "price": "$0.001" },
21
- * "GET /api/premium": { "price": "$0.01" }
15
+ * Example fixed price route:
16
+ * fastify.get("/premium-data", { config: { x402: { price: "$0.10" } } }, handler)
17
+ *
18
+ * Example — variable price (credit purchase):
19
+ * fastify.post("/buy-credits", {
20
+ * config: {
21
+ * x402: {
22
+ * variable: true,
23
+ * min: "$1",
24
+ * max: "$10000",
25
+ * priceField: "dollars", // reads amount from request body
26
+ * }
22
27
  * }
23
- * }
28
+ * }, async (req, reply) => {
29
+ * // req.x402.paid === true, req.x402.amount === "$5.00"
30
+ * addCredits(req.x402.amount);
31
+ * return { credits: newBalance };
32
+ * })
24
33
  *
25
- * Live price updates:
26
- * PUT /x402/price { route: "POST /v1/chat/completions", price: "$0.002" }
27
- * wolverine --x402-price "POST /v1/chat/completions" "$0.002"
34
+ * The payment receipt is in req.x402 after verification:
35
+ * { paid: true, amount: "$5.00", receipt: {...}, txHash: "0x..." }
28
36
  */
29
37
 
30
- // In-memory route pricing — survives hot updates without restart
31
- let _routePricing = {};
32
38
  let _payTo = null;
33
- let _network = "eip155:8453"; // Base mainnet default
39
+ let _network = "eip155:8453";
34
40
  let _facilitatorUrl = "https://x402.org/facilitator";
35
- let _initialized = false;
36
-
37
- /**
38
- * Get current route pricing (for external access).
39
- */
40
- function getPricing() { return { ..._routePricing }; }
41
-
42
- /**
43
- * Update a single route's price live — no restart needed.
44
- */
45
- function setPrice(routeKey, price) {
46
- if (!price.startsWith("$")) price = "$" + price;
47
- _routePricing[routeKey] = { price };
48
- // Persist to settings.json
49
- _persistPricing();
50
- return { route: routeKey, price };
51
- }
52
41
 
53
- /**
54
- * Remove pricing from a route (make it free).
55
- */
56
- function removePrice(routeKey) {
57
- delete _routePricing[routeKey];
58
- _persistPricing();
59
- return { route: routeKey, price: "free" };
60
- }
42
+ async function x402Plugin(fastify, opts) {
43
+ // Config
44
+ _network = opts.network || _network;
45
+ _facilitatorUrl = opts.facilitator || _facilitatorUrl;
46
+ _payTo = opts.payTo || null;
61
47
 
62
- function _persistPricing() {
48
+ // Load from settings.json
63
49
  try {
64
50
  const settingsPath = path.join(process.cwd(), "server", "config", "settings.json");
65
- if (fs.existsSync(settingsPath)) {
66
- const settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
67
- if (!settings.x402) settings.x402 = {};
68
- settings.x402.routes = {};
69
- for (const [route, cfg] of Object.entries(_routePricing)) {
70
- settings.x402.routes[route] = { price: cfg.price };
71
- }
72
- const tmp = settingsPath + ".tmp";
73
- fs.writeFileSync(tmp, JSON.stringify(settings, null, 2), "utf-8");
74
- fs.renameSync(tmp, settingsPath);
75
- }
51
+ const settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
52
+ if (settings.x402?.network) _network = settings.x402.network;
53
+ if (settings.x402?.facilitator) _facilitatorUrl = settings.x402.facilitator;
54
+ if (settings.x402?.payTo) _payTo = settings.x402.payTo;
76
55
  } catch {}
77
- }
78
-
79
- /**
80
- * Fastify plugin — registers x402 payment middleware.
81
- */
82
- async function x402Plugin(fastify, opts) {
83
- // Load config
84
- _payTo = opts.payTo || null;
85
- _network = opts.network || "eip155:8453";
86
- _facilitatorUrl = opts.facilitator || "https://x402.org/facilitator";
87
56
 
88
- // Auto-detect payTo from vault if not provided
57
+ // Auto-detect payTo from vault
89
58
  if (!_payTo) {
90
59
  try {
91
60
  const { getWalletAddress } = require("../vault/wallet-ops");
@@ -93,188 +62,130 @@ async function x402Plugin(fastify, opts) {
93
62
  } catch {}
94
63
  }
95
64
 
96
- // Load route pricing from settings.json
97
- try {
98
- const settingsPath = path.join(process.cwd(), "server", "config", "settings.json");
99
- if (fs.existsSync(settingsPath)) {
100
- const settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
101
- if (settings.x402?.routes) {
102
- for (const [route, cfg] of Object.entries(settings.x402.routes)) {
103
- _routePricing[route] = { price: cfg.price };
104
- }
105
- }
106
- if (settings.x402?.network) _network = settings.x402.network;
107
- if (settings.x402?.facilitator) _facilitatorUrl = settings.x402.facilitator;
108
- }
109
- } catch {}
110
-
111
- if (!_payTo) {
112
- console.log(" ⚠️ x402: no payTo address (set in settings.json or init vault)");
113
- return;
65
+ if (_payTo) {
66
+ console.log(` 💰 x402: payments to ${_payTo.slice(0, 6)}...${_payTo.slice(-4)} on ${_network}`);
114
67
  }
115
68
 
116
- _initialized = true;
117
- const routeCount = Object.keys(_routePricing).length;
118
- if (routeCount > 0) {
119
- console.log(` 💰 x402: ${routeCount} paid route(s), receiving at ${_payTo.slice(0, 6)}...${_payTo.slice(-4)}`);
120
- }
121
-
122
- // ── Main payment gate — onRequest hook ──
69
+ // ── Route-level x402 hook ──
123
70
  fastify.addHook("onRequest", async (request, reply) => {
124
- if (!_initialized || Object.keys(_routePricing).length === 0) return;
71
+ // Check if this route has x402 config
72
+ const routeConfig = request.routeOptions?.config?.x402 || request.context?.config?.x402;
73
+ if (!routeConfig) return; // Not an x402 route
74
+ if (!_payTo) {
75
+ reply.code(500).send({ error: "x402 not configured — no wallet address" });
76
+ return;
77
+ }
125
78
 
126
- const routeKey = `${request.method} ${request.url.split("?")[0]}`;
127
- const routeConfig = _routePricing[routeKey];
128
- if (!routeConfig) return; // Free route
79
+ // Determine the price
80
+ let price;
81
+ if (routeConfig.variable) {
82
+ // Variable pricing — read amount from request body or query
83
+ const field = routeConfig.priceField || "dollars";
84
+ const raw = request.body?.[field] || request.query?.[field];
85
+ if (!raw) {
86
+ reply.code(400).send({
87
+ error: `${field} required`,
88
+ x402: { variable: true, min: routeConfig.min || "$1", max: routeConfig.max || "$10000" },
89
+ });
90
+ return;
91
+ }
92
+ const amount = parseFloat(String(raw).replace("$", ""));
93
+ const min = parseFloat((routeConfig.min || "$1").replace("$", ""));
94
+ const max = parseFloat((routeConfig.max || "$10000").replace("$", ""));
95
+ if (isNaN(amount) || amount < min || amount > max) {
96
+ reply.code(400).send({ error: `Amount must be $${min}-$${max}` });
97
+ return;
98
+ }
99
+ price = "$" + amount.toFixed(2);
100
+ } else {
101
+ price = routeConfig.price;
102
+ if (!price) { reply.code(500).send({ error: "x402 route missing price config" }); return; }
103
+ }
129
104
 
130
105
  const paymentSig = request.headers["payment-signature"];
131
106
 
132
107
  // No payment — return 402 with payment instructions
133
108
  if (!paymentSig) {
134
- const paymentRequired = {
109
+ const instructions = {
135
110
  accepts: [{
136
111
  scheme: "exact",
137
- price: routeConfig.price,
112
+ price,
138
113
  network: _network,
139
114
  payTo: _payTo,
140
115
  }],
141
- description: `Payment required for ${routeKey}`,
116
+ description: routeConfig.description || `Payment for ${request.method} ${request.url}`,
142
117
  mimeType: "application/json",
143
118
  };
144
-
145
119
  reply
146
120
  .code(402)
147
- .header("Payment-Required", JSON.stringify(paymentRequired))
148
- .header("X-402-Version", "1.0")
121
+ .header("Payment-Required", JSON.stringify(instructions))
149
122
  .send({
150
123
  error: "Payment Required",
151
- price: routeConfig.price,
124
+ price,
152
125
  network: _network,
153
126
  payTo: _payTo,
154
127
  protocol: "x402",
128
+ ...(routeConfig.variable ? { variable: true, min: routeConfig.min, max: routeConfig.max, priceField: routeConfig.priceField || "dollars" } : {}),
155
129
  });
156
130
  return;
157
131
  }
158
132
 
159
- // Payment present — verify via facilitator
160
- try {
161
- const verified = await _verifyPayment(paymentSig, routeConfig);
162
- if (verified.valid) {
163
- // Payment good add receipt header and continue
164
- reply.header("Payment-Response", JSON.stringify(verified.receipt || {}));
165
- request.x402 = { paid: true, amount: routeConfig.price, txHash: verified.txHash };
166
- return; // continue to route handler
167
- }
168
- } catch {}
169
-
170
- // Verification failed
171
- reply.code(402).send({
172
- error: "Payment verification failed",
173
- price: routeConfig.price,
174
- network: _network,
175
- payTo: _payTo,
176
- });
177
- });
178
-
179
- // ── Live price management API ──
180
- fastify.put("/x402/price", async (request, reply) => {
181
- // Admin only
182
- const token = request.headers.authorization?.replace("Bearer ", "");
183
- let settings = {};
184
- try { settings = JSON.parse(fs.readFileSync(path.join(process.cwd(), "server", "config", "settings.json"), "utf-8")); } catch {}
185
- if (token !== settings.platform?.apiKey) {
186
- return reply.code(403).send({ error: "Admin only" });
187
- }
188
-
189
- const { route, price } = request.body || {};
190
- if (!route || !price) return reply.code(400).send({ error: "route and price required" });
191
- const result = setPrice(route, price);
192
- return { updated: true, ...result };
193
- });
194
-
195
- fastify.delete("/x402/price", async (request, reply) => {
196
- const token = request.headers.authorization?.replace("Bearer ", "");
197
- let settings = {};
198
- try { settings = JSON.parse(fs.readFileSync(path.join(process.cwd(), "server", "config", "settings.json"), "utf-8")); } catch {}
199
- if (token !== settings.platform?.apiKey) {
200
- return reply.code(403).send({ error: "Admin only" });
133
+ // Payment present — verify
134
+ const verified = await _verifyPayment(paymentSig, price);
135
+ if (verified.valid) {
136
+ reply.header("Payment-Response", JSON.stringify(verified.receipt || {}));
137
+ request.x402 = { paid: true, amount: price, receipt: verified.receipt, txHash: verified.txHash };
138
+ return; // continue to route handler
201
139
  }
202
140
 
203
- const { route } = request.body || {};
204
- if (!route) return reply.code(400).send({ error: "route required" });
205
- return removePrice(route);
141
+ reply.code(402).send({ error: "Payment verification failed", price, payTo: _payTo });
206
142
  });
207
143
 
208
- fastify.get("/x402/pricing", async () => {
209
- return {
210
- payTo: _payTo,
211
- network: _network,
212
- routes: _routePricing,
213
- };
214
- });
144
+ // ── Public pricing endpoint ──
145
+ fastify.get("/x402/info", async () => ({
146
+ payTo: _payTo,
147
+ network: _network,
148
+ facilitator: _facilitatorUrl,
149
+ protocol: "x402",
150
+ docs: "https://docs.cdp.coinbase.com/x402/welcome",
151
+ }));
215
152
  }
216
153
 
217
- /**
218
- * Verify a payment signature via the facilitator.
219
- */
220
- async function _verifyPayment(paymentSig, routeConfig) {
154
+ async function _verifyPayment(paymentSig, price) {
221
155
  try {
222
- // Try @x402/core facilitator client if available
156
+ // Try @x402/core if available
223
157
  const { HTTPFacilitatorClient } = require("@x402/core/server");
224
- const { ExactEvmScheme } = require("@x402/evm/exact/server");
225
-
226
158
  const facilitator = new HTTPFacilitatorClient({ url: _facilitatorUrl });
227
159
  const result = await facilitator.verify({
228
160
  paymentSignature: paymentSig,
229
- routeConfig: {
230
- accepts: [{
231
- scheme: "exact",
232
- price: routeConfig.price,
233
- network: _network,
234
- payTo: _payTo,
235
- }],
236
- },
161
+ routeConfig: { accepts: [{ scheme: "exact", price, network: _network, payTo: _payTo }] },
237
162
  });
238
163
  return { valid: result.valid, receipt: result.receipt, txHash: result.txHash };
239
164
  } catch {
240
- // Fallback: manual HTTP verification to facilitator
165
+ // Fallback: raw HTTP to facilitator
241
166
  try {
242
167
  const https = require("https");
243
168
  const http = require("http");
244
169
  const url = new (require("url").URL)(_facilitatorUrl + "/verify");
245
170
  const body = JSON.stringify({
246
171
  paymentSignature: paymentSig,
247
- routeConfig: {
248
- accepts: [{
249
- scheme: "exact",
250
- price: routeConfig.price,
251
- network: _network,
252
- payTo: _payTo,
253
- }],
254
- },
172
+ routeConfig: { accepts: [{ scheme: "exact", price, network: _network, payTo: _payTo }] },
255
173
  });
256
-
257
174
  return new Promise((resolve) => {
258
175
  const client = url.protocol === "https:" ? https : http;
259
176
  const req = client.request({
260
- hostname: url.hostname,
261
- port: url.port,
262
- path: url.pathname,
263
- method: "POST",
177
+ hostname: url.hostname, port: url.port, path: url.pathname, method: "POST",
264
178
  headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(body) },
265
179
  timeout: 10000,
266
180
  }, (res) => {
267
181
  let data = "";
268
182
  res.on("data", (c) => data += c);
269
183
  res.on("end", () => {
270
- try {
271
- const parsed = JSON.parse(data);
272
- resolve({ valid: parsed.valid || parsed.success, receipt: parsed, txHash: parsed.txHash });
273
- } catch { resolve({ valid: false }); }
184
+ try { const p = JSON.parse(data); resolve({ valid: p.valid || p.success, receipt: p, txHash: p.txHash }); }
185
+ catch { resolve({ valid: false }); }
274
186
  });
275
187
  });
276
188
  req.on("error", () => resolve({ valid: false }));
277
- req.on("timeout", () => { req.destroy(); resolve({ valid: false }); });
278
189
  req.write(body);
279
190
  req.end();
280
191
  });
@@ -282,9 +193,5 @@ async function _verifyPayment(paymentSig, routeConfig) {
282
193
  }
283
194
  }
284
195
 
285
- x402Plugin[Symbol.for("skip-override")] = true; // Fastify plugin compat
286
-
196
+ x402Plugin[Symbol.for("skip-override")] = true;
287
197
  module.exports = x402Plugin;
288
- module.exports.getPricing = getPricing;
289
- module.exports.setPrice = setPrice;
290
- module.exports.removePrice = removePrice;
@@ -61,31 +61,8 @@ async function execute(action, params = {}) {
61
61
  return { signedTransaction: signed };
62
62
  }
63
63
 
64
- // x402 payment actions
65
- case "x402_pricing":
66
- try {
67
- const { getPricing } = require("../middleware/x402-fastify");
68
- return getPricing();
69
- } catch { return { error: "x402 middleware not loaded" }; }
70
-
71
- case "x402_set_price": {
72
- if (!params.route || !params.price) return { error: "route and price required" };
73
- try {
74
- const { setPrice } = require("../middleware/x402-fastify");
75
- return setPrice(params.route, params.price);
76
- } catch { return { error: "x402 middleware not loaded" }; }
77
- }
78
-
79
- case "x402_remove_price": {
80
- if (!params.route) return { error: "route required" };
81
- try {
82
- const { removePrice } = require("../middleware/x402-fastify");
83
- return removePrice(params.route);
84
- } catch { return { error: "x402 middleware not loaded" }; }
85
- }
86
-
87
64
  default:
88
- return { error: `Unknown vault action: ${action}. Use: status, address, sign_tx, sign_message, public_key, x402_pricing, x402_set_price, x402_remove_price` };
65
+ return { error: `Unknown vault action: ${action}. Use: status, address, sign_tx, sign_message, public_key` };
89
66
  }
90
67
  }
91
68