wolverine-ai 6.0.1 → 6.1.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
|
@@ -246,12 +246,15 @@ if (args.includes("--backups")) {
|
|
|
246
246
|
const scriptPath = args.find(a => !a.startsWith("--")) || "server/index.js";
|
|
247
247
|
|
|
248
248
|
// Initialize server/ from template if it doesn't exist (first run)
|
|
249
|
-
const { initServer, ensureX402Deps } = require("../src/core/init-server");
|
|
249
|
+
const { initServer, ensureX402Deps, securityAudit } = require("../src/core/init-server");
|
|
250
250
|
initServer(process.cwd(), scriptPath);
|
|
251
251
|
|
|
252
252
|
// Ensure x402 payment deps are installed (if vault exists)
|
|
253
253
|
ensureX402Deps(process.cwd());
|
|
254
254
|
|
|
255
|
+
// Security audit — detect and auto-fix CVEs on startup
|
|
256
|
+
securityAudit(process.cwd());
|
|
257
|
+
|
|
255
258
|
// System detection (for analytics + dashboard, NOT for forking)
|
|
256
259
|
// Wolverine runs as a single process manager. If users want clustering,
|
|
257
260
|
// they handle it inside their server (e.g. @fastify/cluster, pm2 cluster mode).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wolverine-ai",
|
|
3
|
-
"version": "6.0
|
|
3
|
+
"version": "6.1.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": {
|
package/src/core/config.js
CHANGED
|
@@ -98,6 +98,36 @@ function loadConfig() {
|
|
|
98
98
|
windowMs: parseInt(process.env.WOLVERINE_ERROR_WINDOW_MS, 10) || fileConfig.errorMonitor?.windowMs || 30000,
|
|
99
99
|
cooldownMs: parseInt(process.env.WOLVERINE_ERROR_COOLDOWN_MS, 10) || fileConfig.errorMonitor?.cooldownMs || 60000,
|
|
100
100
|
},
|
|
101
|
+
|
|
102
|
+
heal: {
|
|
103
|
+
healTimeoutMs: parseInt(process.env.WOLVERINE_HEAL_TIMEOUT_MS, 10) || fileConfig.heal?.healTimeoutMs || 300000,
|
|
104
|
+
globalMaxHeals: parseInt(process.env.WOLVERINE_RATE_MAX_GLOBAL_HEALS, 10) || fileConfig.heal?.globalMaxHeals || 5,
|
|
105
|
+
globalWindowMs: parseInt(process.env.WOLVERINE_RATE_GLOBAL_WINDOW_MS, 10) || fileConfig.heal?.globalWindowMs || 300000,
|
|
106
|
+
loopMaxAttempts: parseInt(process.env.WOLVERINE_LOOP_MAX_ATTEMPTS, 10) || fileConfig.heal?.loopMaxAttempts || 3,
|
|
107
|
+
loopWindowMs: parseInt(process.env.WOLVERINE_LOOP_WINDOW_MS, 10) || fileConfig.heal?.loopWindowMs || 600000,
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
agent: {
|
|
111
|
+
aiCallTimeoutMs: parseInt(process.env.WOLVERINE_AI_CALL_TIMEOUT_MS, 10) || fileConfig.agent?.aiCallTimeoutMs || 90000,
|
|
112
|
+
maxTurns: parseInt(process.env.WOLVERINE_AGENT_MAX_TURNS, 10) || fileConfig.agent?.maxTurns || 8,
|
|
113
|
+
tokenBudget: {
|
|
114
|
+
simple: fileConfig.agent?.tokenBudget?.simple || 20000,
|
|
115
|
+
moderate: fileConfig.agent?.tokenBudget?.moderate || 50000,
|
|
116
|
+
complex: fileConfig.agent?.tokenBudget?.complex || 100000,
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
adaptiveLimiter: {
|
|
121
|
+
thresholdYellow: fileConfig.adaptiveLimiter?.thresholdYellow || 70,
|
|
122
|
+
thresholdRed: fileConfig.adaptiveLimiter?.thresholdRed || 85,
|
|
123
|
+
reserveMB: fileConfig.adaptiveLimiter?.reserveMB || 200,
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
backup: {
|
|
127
|
+
stabilityMs: fileConfig.backup?.stabilityMs || 1800000,
|
|
128
|
+
retentionDays: fileConfig.backup?.retentionDays || 7,
|
|
129
|
+
maxFileSizeMB: fileConfig.backup?.maxFileSizeMB || 10,
|
|
130
|
+
},
|
|
101
131
|
};
|
|
102
132
|
|
|
103
133
|
// Migrate old settings.json to new format + ensure defaults
|
package/src/core/init-server.js
CHANGED
|
@@ -89,4 +89,47 @@ function ensureX402Deps(cwd) {
|
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
/**
|
|
93
|
+
* Run security audit on startup — detect and auto-fix CVEs.
|
|
94
|
+
* Only runs if node_modules exists. Non-blocking (doesn't prevent startup).
|
|
95
|
+
*/
|
|
96
|
+
function securityAudit(cwd) {
|
|
97
|
+
if (!fs.existsSync(path.join(cwd, "node_modules"))) return;
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
const { audit } = require("../skills/deps");
|
|
101
|
+
const result = audit(cwd);
|
|
102
|
+
|
|
103
|
+
if (result.vulnerabilities === 0) return;
|
|
104
|
+
|
|
105
|
+
const severity = result.critical > 0 ? "critical" : result.high > 0 ? "high" : "moderate";
|
|
106
|
+
console.log(chalk.yellow(` 🛡️ Security: ${result.vulnerabilities} vulnerabilities (${result.critical} critical, ${result.high} high, ${result.moderate} moderate)`));
|
|
107
|
+
|
|
108
|
+
// Auto-fix if possible (non-breaking only)
|
|
109
|
+
if (result.critical > 0 || result.high > 0) {
|
|
110
|
+
console.log(chalk.blue(" 🛡️ Running npm audit fix..."));
|
|
111
|
+
try {
|
|
112
|
+
const { execSync } = require("child_process");
|
|
113
|
+
const output = execSync("npm audit fix 2>&1", { cwd, encoding: "utf-8", timeout: 60000 });
|
|
114
|
+
const changed = output.match(/changed (\d+) package/);
|
|
115
|
+
if (changed) {
|
|
116
|
+
console.log(chalk.green(` ✅ Fixed: ${changed[0]}`));
|
|
117
|
+
} else {
|
|
118
|
+
console.log(chalk.gray(" 🛡️ No auto-fixable vulnerabilities (may need --force or manual update)"));
|
|
119
|
+
}
|
|
120
|
+
} catch (e) {
|
|
121
|
+
console.log(chalk.gray(` 🛡️ npm audit fix: ${e.message?.slice(0, 80)}`));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Re-check
|
|
125
|
+
const after = audit(cwd);
|
|
126
|
+
if (after.vulnerabilities < result.vulnerabilities) {
|
|
127
|
+
console.log(chalk.green(` ✅ Reduced from ${result.vulnerabilities} to ${after.vulnerabilities} vulnerabilities`));
|
|
128
|
+
} else if (after.critical > 0 || after.high > 0) {
|
|
129
|
+
console.log(chalk.yellow(` ⚠️ ${after.critical + after.high} critical/high vulnerabilities remain — run 'npm audit' for details`));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
} catch {}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
module.exports = { initServer, ensureX402Deps, securityAudit };
|
package/src/core/runner.js
CHANGED
|
@@ -69,8 +69,8 @@ class WolverineRunner {
|
|
|
69
69
|
windowMs: cfg.rateLimiting.windowMs,
|
|
70
70
|
minGapMs: cfg.rateLimiting.minGapMs,
|
|
71
71
|
maxTokensPerHour: cfg.rateLimiting.maxTokensPerHour,
|
|
72
|
-
maxGlobalHealsPerWindow:
|
|
73
|
-
globalWindowMs:
|
|
72
|
+
maxGlobalHealsPerWindow: cfg.heal?.globalMaxHeals || 5,
|
|
73
|
+
globalWindowMs: cfg.heal?.globalWindowMs || 300000,
|
|
74
74
|
});
|
|
75
75
|
this.backupManager = new BackupManager(this.cwd);
|
|
76
76
|
this.logger = new EventLogger(this.cwd);
|
package/src/core/wolverine.js
CHANGED
|
@@ -32,7 +32,9 @@ const { getSummary: getServerContextSummary } = require("./server-context");
|
|
|
32
32
|
* The engine tries fast path first. If that fails verification, it escalates to the agent.
|
|
33
33
|
*/
|
|
34
34
|
async function heal(opts) {
|
|
35
|
-
const
|
|
35
|
+
const { loadConfig } = require("./config");
|
|
36
|
+
const _cfg = loadConfig();
|
|
37
|
+
const HEAL_TIMEOUT_MS = _cfg.heal?.healTimeoutMs || 300000;
|
|
36
38
|
try {
|
|
37
39
|
return await Promise.race([
|
|
38
40
|
_healImpl(opts),
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"env": "development"
|
|
8
8
|
},
|
|
9
9
|
|
|
10
|
-
"_models": "AI models for each task. Provider auto-detected from name (claude-* = Anthropic, gpt-* = OpenAI). Using one model across all roles maximizes prompt cache hits.
|
|
10
|
+
"_models": "AI models for each task. Provider auto-detected from name (claude-* = Anthropic, gpt-* = OpenAI). Using one model across all roles maximizes prompt cache hits. Dashboard at localhost:PORT+1/analytics shows per-role cost/speed.",
|
|
11
11
|
"models": {
|
|
12
12
|
"reasoning": "claude-sonnet-4-6",
|
|
13
13
|
"coding": "claude-sonnet-4-6",
|
|
@@ -22,20 +22,40 @@
|
|
|
22
22
|
"_embedding": "Vector embedding model for brain memory. Requires OPENAI_API_KEY.",
|
|
23
23
|
"embedding": "text-embedding-3-small",
|
|
24
24
|
|
|
25
|
-
"_server": "port:
|
|
25
|
+
"_server": "port: listen port (3000) | maxRetries: crashes before giving up | maxMemoryMB: OOM kill threshold",
|
|
26
26
|
"server": {
|
|
27
27
|
"port": 3000,
|
|
28
28
|
"maxRetries": 3,
|
|
29
29
|
"maxMemoryMB": 512
|
|
30
30
|
},
|
|
31
31
|
|
|
32
|
+
"_heal": "Controls the self-healing pipeline. healTimeoutMs: max time for entire heal attempt. globalMaxHeals/globalWindowMs: rate limit across ALL errors (prevents runaway costs). loopMaxAttempts/loopWindowMs: same-error loop guard (stops after N failures on the same error).",
|
|
33
|
+
"heal": {
|
|
34
|
+
"healTimeoutMs": 300000,
|
|
35
|
+
"globalMaxHeals": 5,
|
|
36
|
+
"globalWindowMs": 300000,
|
|
37
|
+
"loopMaxAttempts": 3,
|
|
38
|
+
"loopWindowMs": 600000
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
"_agent": "Controls the AI agent. aiCallTimeoutMs: max time per AI API call. maxTurns: agent tool-use iterations per heal. tokenBudget: max tokens per heal attempt (simple/moderate/complex auto-selected).",
|
|
42
|
+
"agent": {
|
|
43
|
+
"aiCallTimeoutMs": 90000,
|
|
44
|
+
"maxTurns": 8,
|
|
45
|
+
"tokenBudget": {
|
|
46
|
+
"simple": 20000,
|
|
47
|
+
"moderate": 50000,
|
|
48
|
+
"complex": 100000
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
|
|
32
52
|
"_telemetry": "Heartbeat reports server health to the dashboard. Disable for fully offline servers.",
|
|
33
53
|
"telemetry": {
|
|
34
54
|
"enabled": true,
|
|
35
55
|
"heartbeatIntervalMs": 60000
|
|
36
56
|
},
|
|
37
57
|
|
|
38
|
-
"_rateLimiting": "AI call budget. Prevents runaway costs from heal loops.",
|
|
58
|
+
"_rateLimiting": "AI call budget per window. Prevents runaway costs from heal loops.",
|
|
39
59
|
"rateLimiting": {
|
|
40
60
|
"maxCallsPerWindow": 32,
|
|
41
61
|
"windowMs": 100000,
|
|
@@ -51,13 +71,27 @@
|
|
|
51
71
|
"startDelayMs": 10000
|
|
52
72
|
},
|
|
53
73
|
|
|
54
|
-
"_errorMonitor": "Tracks caught 500 errors per route. defaultThreshold: how many 500s
|
|
74
|
+
"_errorMonitor": "Tracks caught 500 errors per route. defaultThreshold: how many 500s trigger a heal. cooldownMs: min time between heals on same route.",
|
|
55
75
|
"errorMonitor": {
|
|
56
76
|
"defaultThreshold": 1,
|
|
57
77
|
"windowMs": 30000,
|
|
58
78
|
"cooldownMs": 60000
|
|
59
79
|
},
|
|
60
80
|
|
|
81
|
+
"_adaptiveLimiter": "CPU/memory-based request throttling. thresholdYellow: start shedding 30% of requests. thresholdRed: reject all non-essential. reserveMB: memory reserved for heal tools.",
|
|
82
|
+
"adaptiveLimiter": {
|
|
83
|
+
"thresholdYellow": 70,
|
|
84
|
+
"thresholdRed": 85,
|
|
85
|
+
"reserveMB": 200
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
"_backup": "Server snapshots. stabilityMs: time before backup is marked STABLE. retentionDays: auto-delete old backups. maxFileSizeMB: skip files larger than this.",
|
|
89
|
+
"backup": {
|
|
90
|
+
"stabilityMs": 1800000,
|
|
91
|
+
"retentionDays": 7,
|
|
92
|
+
"maxFileSizeMB": 10
|
|
93
|
+
},
|
|
94
|
+
|
|
61
95
|
"_autoUpdate": "Auto-updates wolverine framework from git. Only updates src/ and bin/ — never touches server/ code.",
|
|
62
96
|
"autoUpdate": {
|
|
63
97
|
"enabled": false,
|