wolverine-ai 4.9.0 → 5.0.0

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,6 +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
42
 
42
43
  ${chalk.bold("Configuration:")}
43
44
  server/config/settings.json Models, telemetry, limits, health checks
@@ -66,6 +67,22 @@ if (args.includes("--info")) {
66
67
  }
67
68
 
68
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);
84
+ }
85
+
69
86
  if (args.includes("--init")) {
70
87
  const { scan } = require("../src/core/server-context");
71
88
  console.log(chalk.blue("\n 🔍 Scanning server/ directory...\n"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wolverine-ai",
3
- "version": "4.9.0",
3
+ "version": "5.0.0",
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": {
@@ -66,7 +66,8 @@
66
66
  "ethers": "^6.0.0",
67
67
  "ioredis": "^5.0.0",
68
68
  "pg": "^8.0.0",
69
- "stripe": "^18.0.0"
69
+ "stripe": "^18.0.0",
70
+ "viem": "^2.0.0"
70
71
  },
71
72
  "engines": {
72
73
  "node": ">=18.0.0"
@@ -190,8 +190,8 @@ const SEED_DOCS = [
190
190
  metadata: { topic: "notifications" },
191
191
  },
192
192
  {
193
- text: "Vault: encrypted key storage in .wolverine/vault/. AES-256-GCM encryption. master.key (32 bytes raw) encrypts eth.vault (Ethereum private key). Generated on first run if missing. Private key NEVER exists as a JS string — only Buffer, wiped after use. wallet-ops.js exposes getWalletAddress(), signTransaction(), signMessage() all decrypt→use→wipe with generic error messages only. Injection detector blocks heal if 0x+64hex chars detected in error (key_leak_critical). Redactor scrubs all hex key patterns. Sandbox blocks agent access to vault paths. Backed up in every snapshot. Rollback-protected (NEVER_ROLLBACK list). Vault skill in src/skills/vault.js for agent discovery. Server code uses vault skill to earn/spend ETH without touching the private key.",
194
- metadata: { topic: "vault-security" },
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.",
194
+ metadata: { topic: "vault-x402" },
195
195
  },
196
196
  {
197
197
  text: "MCP integration: connect external tools via Model Context Protocol. Configure in .wolverine/mcp.json with per-server tool allowlists. Security: arg sanitization (secrets redacted before sending to MCP servers), result injection scanning, rate limiting per server, audit logging. Tools appear as mcp__server__tool in the agent. Supports stdio and HTTP transports.",
@@ -0,0 +1,290 @@
1
+ const path = require("path");
2
+ const fs = require("fs");
3
+
4
+ /**
5
+ * x402 Payment Middleware for Fastify — monetize any API route with USDC on Base.
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.
9
+ *
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
+ * });
15
+ *
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" }
22
+ * }
23
+ * }
24
+ *
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"
28
+ */
29
+
30
+ // In-memory route pricing — survives hot updates without restart
31
+ let _routePricing = {};
32
+ let _payTo = null;
33
+ let _network = "eip155:8453"; // Base mainnet default
34
+ 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
+
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
+ }
61
+
62
+ function _persistPricing() {
63
+ try {
64
+ 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
+ }
76
+ } 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
+
88
+ // Auto-detect payTo from vault if not provided
89
+ if (!_payTo) {
90
+ try {
91
+ const { getWalletAddress } = require("../vault/wallet-ops");
92
+ _payTo = await getWalletAddress();
93
+ } catch {}
94
+ }
95
+
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;
114
+ }
115
+
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 ──
123
+ fastify.addHook("onRequest", async (request, reply) => {
124
+ if (!_initialized || Object.keys(_routePricing).length === 0) return;
125
+
126
+ const routeKey = `${request.method} ${request.url.split("?")[0]}`;
127
+ const routeConfig = _routePricing[routeKey];
128
+ if (!routeConfig) return; // Free route
129
+
130
+ const paymentSig = request.headers["payment-signature"];
131
+
132
+ // No payment — return 402 with payment instructions
133
+ if (!paymentSig) {
134
+ const paymentRequired = {
135
+ accepts: [{
136
+ scheme: "exact",
137
+ price: routeConfig.price,
138
+ network: _network,
139
+ payTo: _payTo,
140
+ }],
141
+ description: `Payment required for ${routeKey}`,
142
+ mimeType: "application/json",
143
+ };
144
+
145
+ reply
146
+ .code(402)
147
+ .header("Payment-Required", JSON.stringify(paymentRequired))
148
+ .header("X-402-Version", "1.0")
149
+ .send({
150
+ error: "Payment Required",
151
+ price: routeConfig.price,
152
+ network: _network,
153
+ payTo: _payTo,
154
+ protocol: "x402",
155
+ });
156
+ return;
157
+ }
158
+
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" });
201
+ }
202
+
203
+ const { route } = request.body || {};
204
+ if (!route) return reply.code(400).send({ error: "route required" });
205
+ return removePrice(route);
206
+ });
207
+
208
+ fastify.get("/x402/pricing", async () => {
209
+ return {
210
+ payTo: _payTo,
211
+ network: _network,
212
+ routes: _routePricing,
213
+ };
214
+ });
215
+ }
216
+
217
+ /**
218
+ * Verify a payment signature via the facilitator.
219
+ */
220
+ async function _verifyPayment(paymentSig, routeConfig) {
221
+ try {
222
+ // Try @x402/core facilitator client if available
223
+ const { HTTPFacilitatorClient } = require("@x402/core/server");
224
+ const { ExactEvmScheme } = require("@x402/evm/exact/server");
225
+
226
+ const facilitator = new HTTPFacilitatorClient({ url: _facilitatorUrl });
227
+ const result = await facilitator.verify({
228
+ paymentSignature: paymentSig,
229
+ routeConfig: {
230
+ accepts: [{
231
+ scheme: "exact",
232
+ price: routeConfig.price,
233
+ network: _network,
234
+ payTo: _payTo,
235
+ }],
236
+ },
237
+ });
238
+ return { valid: result.valid, receipt: result.receipt, txHash: result.txHash };
239
+ } catch {
240
+ // Fallback: manual HTTP verification to facilitator
241
+ try {
242
+ const https = require("https");
243
+ const http = require("http");
244
+ const url = new (require("url").URL)(_facilitatorUrl + "/verify");
245
+ const body = JSON.stringify({
246
+ paymentSignature: paymentSig,
247
+ routeConfig: {
248
+ accepts: [{
249
+ scheme: "exact",
250
+ price: routeConfig.price,
251
+ network: _network,
252
+ payTo: _payTo,
253
+ }],
254
+ },
255
+ });
256
+
257
+ return new Promise((resolve) => {
258
+ const client = url.protocol === "https:" ? https : http;
259
+ const req = client.request({
260
+ hostname: url.hostname,
261
+ port: url.port,
262
+ path: url.pathname,
263
+ method: "POST",
264
+ headers: { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(body) },
265
+ timeout: 10000,
266
+ }, (res) => {
267
+ let data = "";
268
+ res.on("data", (c) => data += c);
269
+ 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 }); }
274
+ });
275
+ });
276
+ req.on("error", () => resolve({ valid: false }));
277
+ req.on("timeout", () => { req.destroy(); resolve({ valid: false }); });
278
+ req.write(body);
279
+ req.end();
280
+ });
281
+ } catch { return { valid: false }; }
282
+ }
283
+ }
284
+
285
+ x402Plugin[Symbol.for("skip-override")] = true; // Fastify plugin compat
286
+
287
+ module.exports = x402Plugin;
288
+ module.exports.getPricing = getPricing;
289
+ module.exports.setPrice = setPrice;
290
+ module.exports.removePrice = removePrice;
@@ -10,8 +10,8 @@
10
10
  */
11
11
 
12
12
  const SKILL_NAME = "vault";
13
- const SKILL_DESCRIPTION = "Secure Ethereum wallet — sign transactions, get address, check balance. Private key encrypted at rest, never exposed in code or errors.";
14
- const SKILL_KEYWORDS = ["wallet", "ethereum", "eth", "sign", "transaction", "vault", "private key", "address", "crypto", "blockchain", "send", "transfer"];
13
+ const SKILL_DESCRIPTION = "Secure Ethereum wallet + x402 payments — sign transactions, get address, manage paid API routes. Private key encrypted at rest via AES-256-GCM, never exposed in code or errors. x402 support: set prices on routes, receive USDC on Base network.";
14
+ const SKILL_KEYWORDS = ["wallet", "ethereum", "eth", "sign", "transaction", "vault", "private key", "address", "crypto", "blockchain", "send", "transfer", "x402", "payment", "usdc", "base", "price", "paid", "api"];
15
15
  const SKILL_USAGE = `
16
16
  vault.status() — check if vault is initialized, show address
17
17
  vault.address() — get the wallet's Ethereum address
@@ -61,8 +61,31 @@ 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
+
64
87
  default:
65
- return { error: `Unknown vault action: ${action}. Use: status, address, sign_tx, sign_message, public_key` };
88
+ return { error: `Unknown vault action: ${action}. Use: status, address, sign_tx, sign_message, public_key, x402_pricing, x402_set_price, x402_remove_price` };
66
89
  }
67
90
  }
68
91