wolverine-ai 4.2.0 → 4.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wolverine-ai",
3
- "version": "4.2.0",
3
+ "version": "4.3.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": {
@@ -289,6 +289,76 @@ const TOOL_DEFINITIONS = [
289
289
  },
290
290
  },
291
291
  // ── COMPLETION ──
292
+ {
293
+ type: "function",
294
+ function: {
295
+ name: "check_memory",
296
+ description: "Check system and process memory usage. Returns RSS, heap, free system memory, and whether memory pressure is detected.",
297
+ parameters: { type: "object", properties: {}, required: [] },
298
+ },
299
+ },
300
+ {
301
+ type: "function",
302
+ function: {
303
+ name: "list_processes",
304
+ description: "List running Node.js processes. Useful for finding zombie/orphan processes or port conflicts.",
305
+ parameters: { type: "object", properties: {}, required: [] },
306
+ },
307
+ },
308
+ {
309
+ type: "function",
310
+ function: {
311
+ name: "check_logs",
312
+ description: "Read recent log output. Returns the last N lines from the server's log source.",
313
+ parameters: {
314
+ type: "object",
315
+ properties: {
316
+ lines: { type: "number", description: "Number of lines to return (default: 50)" },
317
+ filter: { type: "string", description: "Optional grep filter pattern" },
318
+ },
319
+ required: [],
320
+ },
321
+ },
322
+ },
323
+ {
324
+ type: "function",
325
+ function: {
326
+ name: "restart_service",
327
+ description: "Trigger a graceful server restart. Use after applying fixes that require a process restart to take effect.",
328
+ parameters: { type: "object", properties: {}, required: [] },
329
+ },
330
+ },
331
+ {
332
+ type: "function",
333
+ function: {
334
+ name: "check_network",
335
+ description: "Check network connectivity: DNS resolution, port availability, and external service reachability.",
336
+ parameters: {
337
+ type: "object",
338
+ properties: {
339
+ host: { type: "string", description: "Hostname to check DNS for (optional)" },
340
+ port: { type: "number", description: "Port to check availability (optional)" },
341
+ url: { type: "string", description: "URL to check reachability (optional)" },
342
+ },
343
+ required: [],
344
+ },
345
+ },
346
+ },
347
+ {
348
+ type: "function",
349
+ function: {
350
+ name: "inspect_env",
351
+ description: "List environment variable names (NOT values) that are set. Checks if required vars exist without exposing secrets.",
352
+ parameters: {
353
+ type: "object",
354
+ properties: {
355
+ check: { type: "array", items: { type: "string" }, description: "Specific var names to check if set (optional)" },
356
+ },
357
+ required: [],
358
+ },
359
+ },
360
+ },
361
+ // ── TASK MANAGEMENT ──
292
362
  {
293
363
  type: "function",
294
364
  function: {
@@ -569,6 +639,12 @@ class AgentEngine {
569
639
  case "run_db_fix": return this._runDbFix(args);
570
640
  case "audit_deps": return this._auditDeps(args);
571
641
  case "check_migration": return this._checkMigration(args);
642
+ case "check_memory": return this._checkMemory(args);
643
+ case "list_processes": return this._listProcesses(args);
644
+ case "check_logs": return this._checkLogs(args);
645
+ case "restart_service": return this._restartService(args);
646
+ case "check_network": return this._checkNetwork(args);
647
+ case "inspect_env": return this._inspectEnv(args);
572
648
  case "done": return this._done(args);
573
649
  // Legacy aliases
574
650
  case "list_files": return this._globFiles({ pattern: (args.dir || ".") + "/*" + (args.pattern || "") });
@@ -1062,6 +1138,112 @@ class AgentEngine {
1062
1138
  } catch (e) { return { content: `Migration check error: ${e.message}` }; }
1063
1139
  }
1064
1140
 
1141
+ // ── SERVER DIAGNOSTICS ──
1142
+
1143
+ _checkMemory() {
1144
+ const os = require("os");
1145
+ const mem = process.memoryUsage();
1146
+ const totalMB = Math.round(os.totalmem() / 1048576);
1147
+ const freeMB = Math.round(os.freemem() / 1048576);
1148
+ const usedPct = Math.round((1 - os.freemem() / os.totalmem()) * 100);
1149
+ const lines = [
1150
+ `Process RSS: ${Math.round(mem.rss / 1048576)}MB`,
1151
+ `Process Heap: ${Math.round(mem.heapUsed / 1048576)}MB / ${Math.round(mem.heapTotal / 1048576)}MB`,
1152
+ `Process External: ${Math.round(mem.external / 1048576)}MB`,
1153
+ `System: ${freeMB}MB free / ${totalMB}MB total (${usedPct}% used)`,
1154
+ usedPct > 90 ? "⚠️ MEMORY PRESSURE DETECTED — system above 90% usage" : "✅ Memory OK",
1155
+ ];
1156
+ return { content: lines.join("\n") };
1157
+ }
1158
+
1159
+ _listProcesses() {
1160
+ try {
1161
+ const cmd = process.platform === "win32"
1162
+ ? 'tasklist /FI "IMAGENAME eq node.exe" /FO CSV /NH'
1163
+ : "ps aux | grep -E 'node|PID' | grep -v grep";
1164
+ const output = execSync(cmd, { encoding: "utf-8", timeout: 5000 }).trim();
1165
+ return { content: output || "(no node processes found)" };
1166
+ } catch (e) { return { content: `Error listing processes: ${e.message}` }; }
1167
+ }
1168
+
1169
+ _checkLogs(args) {
1170
+ const lines = args.lines || 50;
1171
+ const filter = args.filter || "";
1172
+ try {
1173
+ let cmd;
1174
+ if (process.platform === "win32") {
1175
+ cmd = `powershell -c "Get-Content -Tail ${lines} .wolverine\\events.log"`;
1176
+ } else {
1177
+ // Try journalctl first (systemd), fall back to log file
1178
+ cmd = `journalctl -u wolverine --no-pager -n ${lines} 2>/dev/null || tail -n ${lines} .wolverine/events.log 2>/dev/null || echo 'No logs found'`;
1179
+ }
1180
+ if (filter) cmd += ` | grep -i '${filter.replace(/'/g, "'\\''")}'`;
1181
+ const output = execSync(cmd, { encoding: "utf-8", timeout: 10000, cwd: this.cwd }).trim();
1182
+ return { content: output.slice(0, 4000) || "(empty)" };
1183
+ } catch (e) { return { content: `Error reading logs: ${e.message}` }; }
1184
+ }
1185
+
1186
+ _restartService() {
1187
+ // Signal the parent wolverine process to restart the child server
1188
+ // We can't directly restart — but we can signal via a file that the runner checks
1189
+ try {
1190
+ const restartFlag = path.join(this.cwd, ".wolverine", "restart-requested");
1191
+ fs.writeFileSync(restartFlag, Date.now().toString(), "utf-8");
1192
+ return { content: "Restart requested. The server will restart after this heal completes." };
1193
+ } catch (e) { return { content: `Error requesting restart: ${e.message}` }; }
1194
+ }
1195
+
1196
+ _checkNetwork(args) {
1197
+ const results = [];
1198
+ try {
1199
+ // DNS check
1200
+ if (args.host) {
1201
+ try {
1202
+ const dns = require("dns");
1203
+ const addresses = execSync(`node -e "require('dns').resolve('${args.host.replace(/'/g, "")}', (e,a) => console.log(e ? 'FAIL:'+e.code : a.join(',')))"`, { encoding: "utf-8", timeout: 5000 }).trim();
1204
+ results.push(`DNS ${args.host}: ${addresses}`);
1205
+ } catch (e) { results.push(`DNS ${args.host}: FAILED — ${e.message}`); }
1206
+ }
1207
+ // Port check
1208
+ if (args.port) {
1209
+ try {
1210
+ const portCmd = process.platform === "win32"
1211
+ ? `netstat -ano | findstr ":${args.port}"`
1212
+ : `ss -tlnp | grep ":${args.port}" || echo "port ${args.port} is free"`;
1213
+ const portResult = execSync(portCmd, { encoding: "utf-8", timeout: 3000 }).trim();
1214
+ results.push(`Port ${args.port}: ${portResult || "free"}`);
1215
+ } catch { results.push(`Port ${args.port}: free (nothing listening)`); }
1216
+ }
1217
+ // URL reachability
1218
+ if (args.url) {
1219
+ try {
1220
+ const urlResult = execSync(`node -e "require('${args.url.startsWith('https') ? 'https' : 'http'}').get('${args.url.replace(/'/g, "")}', r => { console.log(r.statusCode); r.resume(); }).on('error', e => console.log('FAIL:'+e.code))"`, { encoding: "utf-8", timeout: 10000 }).trim();
1221
+ results.push(`URL ${args.url}: ${urlResult}`);
1222
+ } catch (e) { results.push(`URL ${args.url}: FAILED — ${e.message}`); }
1223
+ }
1224
+ if (results.length === 0) results.push("Provide host, port, or url to check.");
1225
+ return { content: results.join("\n") };
1226
+ } catch (e) { return { content: `Network check error: ${e.message}` }; }
1227
+ }
1228
+
1229
+ _inspectEnv(args) {
1230
+ if (args.check && Array.isArray(args.check)) {
1231
+ const results = args.check.map(v => `${v}: ${process.env[v] ? "SET (" + process.env[v].length + " chars)" : "NOT SET"}`);
1232
+ return { content: results.join("\n") };
1233
+ }
1234
+ // List all env var names (not values) grouped by category
1235
+ const keys = Object.keys(process.env).sort();
1236
+ const wolverine = keys.filter(k => /^WOLVERINE|^PORT$|^NODE_ENV$/i.test(k));
1237
+ const apiKeys = keys.filter(k => /KEY|SECRET|TOKEN|PASSWORD|AUTH/i.test(k));
1238
+ const other = keys.filter(k => !wolverine.includes(k) && !apiKeys.includes(k));
1239
+ const lines = [
1240
+ `Wolverine vars (${wolverine.length}): ${wolverine.join(", ") || "none"}`,
1241
+ `Secret vars (${apiKeys.length}): ${apiKeys.map(k => k + "=" + (process.env[k] ? "SET" : "MISSING")).join(", ") || "none"}`,
1242
+ `Total env vars: ${keys.length}`,
1243
+ ];
1244
+ return { content: lines.join("\n") };
1245
+ }
1246
+
1065
1247
  _done(args) {
1066
1248
  console.log(chalk.green(` ✅ Agent done: ${args.summary}`));
1067
1249
  if (this.logger) {
@@ -66,7 +66,7 @@ const SEED_DOCS = [
66
66
  metadata: { topic: "verification" },
67
67
  },
68
68
  {
69
- text: "Wolverine multi-file agent: turn-limited agent loop with 18 tools across 7 categories. Turn budget adapts to error type: simple (TypeError)=4, config/ENOENT=5, complex=8. Each AI call has 45s timeout via Promise.race — prevents indefinite hangs. If timeout occurs mid-fix, partial results returned. FILE: read_file (offset/limit), write_file (creates dirs), edit_file (find-and-replace), glob_files (pattern search), grep_code (regex with context), list_dir (directory listing with sizes), move_file (rename/relocate). SHELL: bash_exec (30s default, 60s cap), git_log, git_diff. DATABASE: inspect_db (tables/schema/SELECT on SQLite), run_db_fix (UPDATE/DELETE/ALTER with auto-backup). DIAGNOSTICS: check_port (find what uses a port), check_env (env vars, values redacted). DEPS: audit_deps (full npm health check), check_migration (known upgrade paths). RESEARCH: web_fetch (10s timeout). CONTROL: done. Prompt emphasizes fast action: fix immediately when solution is obvious, investigate only when cause unclear.",
69
+ text: "Wolverine multi-file agent: turn-limited agent loop with 24 tools across 8 categories. Turn budget adapts to error type: simple (TypeError)=4, config/ENOENT=5, complex=8. Each AI call has 90s timeout via Promise.race. FILE: read_file, write_file, edit_file, glob_files, grep_code, list_dir, move_file. SHELL: bash_exec (30s default, 60s cap), git_log, git_diff. DATABASE: inspect_db (tables/schema/SELECT on SQLite), run_db_fix (UPDATE/DELETE/ALTER with auto-backup). DIAGNOSTICS: check_port, check_env, check_memory (RSS/heap/system with OOM detection), list_processes (find zombie/orphan node processes), check_logs (read recent journalctl/log file), check_network (DNS/port/URL reachability), inspect_env (list env var names without values, check if required vars exist). SERVER: restart_service (request graceful restart after fix). DEPS: audit_deps, check_migration. RESEARCH: web_fetch. CONTROL: done.",
70
70
  metadata: { topic: "agent" },
71
71
  },
72
72
  {
@@ -110,7 +110,7 @@ const SEED_DOCS = [
110
110
  metadata: { topic: "sub-agent-workflow" },
111
111
  },
112
112
  {
113
- text: "Sub-agent tool restrictions: explore gets read_file/glob/grep/git_log/git_diff/list_dir/check_env/check_port/inspect_db/audit_deps. plan gets read_file/glob/grep/list_dir/inspect_db/check_env/audit_deps/check_migration/search_brain. fix gets read_file/write_file/edit_file/glob/grep/bash_exec/move_file/run_db_fix/audit_deps. verify gets read_file/glob/grep/bash_exec/inspect_db/check_port. research gets read_file/grep/web_fetch/search_brain. security gets read_file/glob/grep/inspect_db. database gets read_file/write_file/edit_file/glob/grep/bash_exec/inspect_db/run_db_fix. Each type gets only the tools it needs.",
113
+ text: "Sub-agent tool restrictions: explore gets read_file/glob/grep/git_log/git_diff/list_dir/check_env/check_port/check_memory/check_network/list_processes/inspect_db/audit_deps. plan gets read_file/glob/grep/list_dir/inspect_db/check_env/inspect_env/audit_deps/check_migration. fix gets read_file/write_file/edit_file/glob/grep/bash_exec/move_file/run_db_fix/audit_deps/restart_service. verify gets read_file/glob/grep/bash_exec/inspect_db/check_port/check_memory/check_logs. research gets read_file/grep/web_fetch/check_logs. security gets read_file/glob/grep/inspect_db/inspect_env. database gets read_file/write_file/edit_file/glob/grep/bash_exec/inspect_db/run_db_fix. 24 tools total, each sub-agent type gets tools relevant to its role.",
114
114
  metadata: { topic: "sub-agent-tools" },
115
115
  },
116
116
  {