wolverine-ai 1.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.
Files changed (79) hide show
  1. package/PLATFORM.md +442 -0
  2. package/README.md +475 -0
  3. package/SERVER_BEST_PRACTICES.md +62 -0
  4. package/TELEMETRY.md +108 -0
  5. package/bin/wolverine.js +95 -0
  6. package/examples/01-basic-typo.js +31 -0
  7. package/examples/02-multi-file/routes/users.js +15 -0
  8. package/examples/02-multi-file/server.js +25 -0
  9. package/examples/03-syntax-error.js +23 -0
  10. package/examples/04-secret-leak.js +14 -0
  11. package/examples/05-expired-key.js +27 -0
  12. package/examples/06-json-config/config.json +13 -0
  13. package/examples/06-json-config/server.js +28 -0
  14. package/examples/07-rate-limit-loop.js +11 -0
  15. package/examples/08-sandbox-escape.js +20 -0
  16. package/examples/buggy-server.js +39 -0
  17. package/examples/demos/01-basic-typo/index.js +20 -0
  18. package/examples/demos/01-basic-typo/routes/api.js +13 -0
  19. package/examples/demos/01-basic-typo/routes/health.js +4 -0
  20. package/examples/demos/02-multi-file/index.js +24 -0
  21. package/examples/demos/02-multi-file/routes/api.js +13 -0
  22. package/examples/demos/02-multi-file/routes/health.js +4 -0
  23. package/examples/demos/03-syntax-error/index.js +18 -0
  24. package/examples/demos/04-secret-leak/index.js +16 -0
  25. package/examples/demos/05-expired-key/index.js +21 -0
  26. package/examples/demos/06-json-config/config.json +9 -0
  27. package/examples/demos/06-json-config/index.js +20 -0
  28. package/examples/demos/07-null-crash/index.js +16 -0
  29. package/examples/run-demo.js +110 -0
  30. package/package.json +67 -0
  31. package/server/config/settings.json +62 -0
  32. package/server/index.js +33 -0
  33. package/server/routes/api.js +12 -0
  34. package/server/routes/health.js +16 -0
  35. package/server/routes/time.js +12 -0
  36. package/src/agent/agent-engine.js +727 -0
  37. package/src/agent/goal-loop.js +140 -0
  38. package/src/agent/research-agent.js +120 -0
  39. package/src/agent/sub-agents.js +176 -0
  40. package/src/backup/backup-manager.js +321 -0
  41. package/src/brain/brain.js +315 -0
  42. package/src/brain/embedder.js +131 -0
  43. package/src/brain/function-map.js +263 -0
  44. package/src/brain/vector-store.js +267 -0
  45. package/src/core/ai-client.js +387 -0
  46. package/src/core/cluster-manager.js +144 -0
  47. package/src/core/config.js +89 -0
  48. package/src/core/error-parser.js +87 -0
  49. package/src/core/health-monitor.js +129 -0
  50. package/src/core/models.js +132 -0
  51. package/src/core/patcher.js +55 -0
  52. package/src/core/runner.js +464 -0
  53. package/src/core/system-info.js +141 -0
  54. package/src/core/verifier.js +146 -0
  55. package/src/core/wolverine.js +290 -0
  56. package/src/dashboard/server.js +1332 -0
  57. package/src/index.js +94 -0
  58. package/src/logger/event-logger.js +237 -0
  59. package/src/logger/pricing.js +96 -0
  60. package/src/logger/repair-history.js +109 -0
  61. package/src/logger/token-tracker.js +277 -0
  62. package/src/mcp/mcp-client.js +224 -0
  63. package/src/mcp/mcp-registry.js +228 -0
  64. package/src/mcp/mcp-security.js +152 -0
  65. package/src/monitor/perf-monitor.js +300 -0
  66. package/src/monitor/process-monitor.js +231 -0
  67. package/src/monitor/route-prober.js +191 -0
  68. package/src/notifications/notifier.js +227 -0
  69. package/src/platform/heartbeat.js +93 -0
  70. package/src/platform/queue.js +53 -0
  71. package/src/platform/register.js +64 -0
  72. package/src/platform/telemetry.js +76 -0
  73. package/src/security/admin-auth.js +150 -0
  74. package/src/security/injection-detector.js +174 -0
  75. package/src/security/rate-limiter.js +152 -0
  76. package/src/security/sandbox.js +128 -0
  77. package/src/security/secret-redactor.js +217 -0
  78. package/src/skills/skill-registry.js +129 -0
  79. package/src/skills/sql.js +375 -0
@@ -0,0 +1,129 @@
1
+ const http = require("http");
2
+ const chalk = require("chalk");
3
+
4
+ /**
5
+ * Health Monitor — periodically pings the server to detect hangs, freezes,
6
+ * and unresponsive states that don't produce a crash/exit.
7
+ *
8
+ * Works like PM2's health check: if the server stops responding,
9
+ * wolverine treats it as a crash and triggers the heal cycle.
10
+ */
11
+ class HealthMonitor {
12
+ constructor(options = {}) {
13
+ this.port = options.port || parseInt(process.env.PORT, 10) || 3000;
14
+ this.path = options.path || "/health";
15
+ this.intervalMs = options.intervalMs || 15000; // check every 15s
16
+ this.timeoutMs = options.timeoutMs || 5000; // 5s timeout per check
17
+ this.failThreshold = options.failThreshold || 3; // 3 consecutive fails = dead
18
+ this.startDelayMs = options.startDelayMs || 10000; // wait 10s before first check
19
+
20
+ this._timer = null;
21
+ this._consecutiveFailures = 0;
22
+ this._onUnhealthy = null;
23
+ this._running = false;
24
+ this._totalChecks = 0;
25
+ this._totalPasses = 0;
26
+ }
27
+
28
+ /**
29
+ * Start monitoring. Calls onUnhealthy callback when the server is deemed dead.
30
+ */
31
+ start(onUnhealthy) {
32
+ this._onUnhealthy = onUnhealthy;
33
+ this._consecutiveFailures = 0;
34
+ this._running = true;
35
+
36
+ // Delay first check to let the server boot
37
+ this._timer = setTimeout(() => {
38
+ if (!this._running) return;
39
+ this._check();
40
+ this._timer = setInterval(() => this._check(), this.intervalMs);
41
+ }, this.startDelayMs);
42
+ }
43
+
44
+ stop() {
45
+ this._running = false;
46
+ if (this._timer) {
47
+ clearTimeout(this._timer);
48
+ clearInterval(this._timer);
49
+ this._timer = null;
50
+ }
51
+ }
52
+
53
+ reset() {
54
+ this._consecutiveFailures = 0;
55
+ }
56
+
57
+ getStats() {
58
+ return {
59
+ totalChecks: this._totalChecks,
60
+ totalPasses: this._totalPasses,
61
+ consecutiveFailures: this._consecutiveFailures,
62
+ uptimePercent: this._totalChecks > 0
63
+ ? Math.round((this._totalPasses / this._totalChecks) * 100)
64
+ : 100,
65
+ };
66
+ }
67
+
68
+ _check() {
69
+ if (!this._running) return;
70
+ this._totalChecks++;
71
+
72
+ const req = http.request(
73
+ {
74
+ hostname: "127.0.0.1",
75
+ port: this.port,
76
+ path: this.path,
77
+ method: "GET",
78
+ timeout: this.timeoutMs,
79
+ },
80
+ (res) => {
81
+ let body = "";
82
+ res.on("data", (chunk) => { body += chunk; });
83
+ res.on("end", () => {
84
+ if (res.statusCode >= 200 && res.statusCode < 400) {
85
+ this._onPass();
86
+ } else {
87
+ this._onFail(`HTTP ${res.statusCode}`);
88
+ }
89
+ });
90
+ }
91
+ );
92
+
93
+ req.on("timeout", () => {
94
+ req.destroy();
95
+ this._onFail("timeout");
96
+ });
97
+
98
+ req.on("error", (err) => {
99
+ // ECONNREFUSED means the server isn't listening — this is expected
100
+ // right after a crash/restart, so only count it if we've been checking a while
101
+ this._onFail(err.code || err.message);
102
+ });
103
+
104
+ req.end();
105
+ }
106
+
107
+ _onPass() {
108
+ if (this._consecutiveFailures > 0) {
109
+ console.log(chalk.green(` 💓 Health check passed (recovered after ${this._consecutiveFailures} failures)`));
110
+ }
111
+ this._consecutiveFailures = 0;
112
+ this._totalPasses++;
113
+ }
114
+
115
+ _onFail(reason) {
116
+ this._consecutiveFailures++;
117
+ console.log(chalk.yellow(` 💔 Health check failed (${this._consecutiveFailures}/${this.failThreshold}): ${reason}`));
118
+
119
+ if (this._consecutiveFailures >= this.failThreshold) {
120
+ console.log(chalk.red(`\n🚨 Server unresponsive after ${this.failThreshold} consecutive health check failures.`));
121
+ this._consecutiveFailures = 0; // reset to avoid re-triggering immediately
122
+ if (this._onUnhealthy) {
123
+ this._onUnhealthy(reason);
124
+ }
125
+ }
126
+ }
127
+ }
128
+
129
+ module.exports = { HealthMonitor };
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Model Configuration — centralized model selection for every AI task.
3
+ *
4
+ * Users configure models in .env.local to optimize spend:
5
+ *
6
+ * REASONING_MODEL — Deep analysis, complex debugging (most expensive, most capable)
7
+ * CODING_MODEL — Code repair generation (important, needs strong coding ability)
8
+ * CHAT_MODEL — Explanations, summaries (good but cheaper)
9
+ * AUDIT_MODEL — Security scans, injection detection (runs on every error)
10
+ * UTILITY_MODEL — JSON formatting, regex validation, simple classification (cheapest)
11
+ *
12
+ * Defaults use gpt-4o tiers. Users can swap in any OpenAI-compatible model.
13
+ */
14
+
15
+ const MODEL_ROLES = {
16
+ // Deep reasoning — used for multi-step debugging when a simple fix fails
17
+ reasoning: {
18
+ envKey: "REASONING_MODEL",
19
+ default: "gpt-5.4",
20
+ description: "Deep analysis and complex multi-step debugging",
21
+ tier: "premium",
22
+ },
23
+
24
+ // Code generation — the main repair model
25
+ coding: {
26
+ envKey: "CODING_MODEL",
27
+ default: "gpt-5.3-codex",
28
+ description: "Code repair and fix generation",
29
+ tier: "premium",
30
+ },
31
+
32
+ // Chat/explanation — used for generating human-readable explanations
33
+ chat: {
34
+ envKey: "CHAT_MODEL",
35
+ default: "gpt-5.4-mini",
36
+ description: "Explanations, summaries, and user-facing messages",
37
+ tier: "standard",
38
+ },
39
+
40
+ // Security audit — injection detection, runs on every single error
41
+ audit: {
42
+ envKey: "AUDIT_MODEL",
43
+ default: "gpt-5.4-nano",
44
+ description: "Security scanning and prompt injection detection",
45
+ tier: "economy",
46
+ },
47
+
48
+ // Compacting — compresses text before embedding into brain
49
+ utility: {
50
+ envKey: "COMPACTING_MODEL",
51
+ default: "gpt-5.4-nano",
52
+ description: "Text compaction before brain embedding",
53
+ tier: "economy",
54
+ },
55
+
56
+ // Tool — chat responses that use function calling (call_endpoint, search_brain)
57
+ tool: {
58
+ envKey: "TOOL_MODEL",
59
+ default: "gpt-4o-mini",
60
+ description: "Chat with tool calling (must support function calling)",
61
+ tier: "standard",
62
+ },
63
+
64
+ // Classifier — routes commands to CHAT vs AGENT, picks tiers
65
+ classifier: {
66
+ envKey: "CLASSIFIER_MODEL",
67
+ default: "gpt-4o-mini",
68
+ description: "Command routing and classification (CHAT/AGENT, SMALL/MEDIUM/LARGE)",
69
+ tier: "economy",
70
+ },
71
+
72
+ // Research — deep research for solutions when fixes fail
73
+ research: {
74
+ envKey: "RESEARCH_MODEL",
75
+ default: "gpt-4o",
76
+ description: "Deep research for error solutions and documentation lookup",
77
+ tier: "premium",
78
+ },
79
+
80
+ // Embedding — vector representations for semantic search
81
+ embedding: {
82
+ envKey: "TEXT_EMBEDDING_MODEL",
83
+ default: "text-embedding-3-small",
84
+ description: "Text embeddings for brain vector store",
85
+ tier: "economy",
86
+ },
87
+ };
88
+
89
+ /**
90
+ * Get the configured model for a given role.
91
+ */
92
+ function getModel(role) {
93
+ const config = MODEL_ROLES[role];
94
+ if (!config) {
95
+ throw new Error(`Unknown model role: "${role}". Valid roles: ${Object.keys(MODEL_ROLES).join(", ")}`);
96
+ }
97
+ // Priority: env var → wolverine.config.js → hardcoded default
98
+ const { getConfig } = require("./config");
99
+ return process.env[config.envKey] || getConfig(`models.${role}`) || config.default;
100
+ }
101
+
102
+ /**
103
+ * Get all model assignments for logging.
104
+ */
105
+ function getModelConfig() {
106
+ const config = {};
107
+ for (const [role, def] of Object.entries(MODEL_ROLES)) {
108
+ config[role] = {
109
+ model: process.env[def.envKey] || def.default,
110
+ source: process.env[def.envKey] ? "env" : "default",
111
+ tier: def.tier,
112
+ };
113
+ }
114
+ return config;
115
+ }
116
+
117
+ /**
118
+ * Log the current model configuration.
119
+ */
120
+ function logModelConfig(chalk) {
121
+ const config = getModelConfig();
122
+ const tierColors = { premium: "cyan", standard: "blue", economy: "gray" };
123
+
124
+ for (const [role, info] of Object.entries(config)) {
125
+ const color = tierColors[info.tier] || "white";
126
+ const label = `${role.padEnd(10)} → ${info.model}`;
127
+ const source = info.source === "env" ? "(custom)" : "(default)";
128
+ console.log(chalk[color](` ${label} ${source}`));
129
+ }
130
+ }
131
+
132
+ module.exports = { getModel, getModelConfig, logModelConfig, MODEL_ROLES };
@@ -0,0 +1,55 @@
1
+ const path = require("path");
2
+
3
+ /**
4
+ * Apply a set of changes from the AI repair response.
5
+ * All file operations go through the sandbox.
6
+ * Backups are managed by BackupManager (not inline .bak files anymore).
7
+ *
8
+ * Returns an array of { file, success, error? } objects.
9
+ */
10
+ function applyPatch(changes, basePath, sandbox) {
11
+ const results = [];
12
+
13
+ for (const change of changes) {
14
+ const filePath = path.isAbsolute(change.file)
15
+ ? change.file
16
+ : path.resolve(basePath, change.file);
17
+
18
+ // All access through sandbox
19
+ if (!sandbox.exists(filePath)) {
20
+ results.push({ file: filePath, success: false, error: "File not found" });
21
+ continue;
22
+ }
23
+
24
+ const original = sandbox.readFile(filePath);
25
+
26
+ // Normalize line endings for matching
27
+ const normalizedOriginal = original.replace(/\r\n/g, "\n");
28
+ const normalizedOld = change.old.replace(/\r\n/g, "\n");
29
+
30
+ if (!normalizedOriginal.includes(normalizedOld)) {
31
+ results.push({
32
+ file: filePath,
33
+ success: false,
34
+ error: "Could not find the exact text to replace in the source file",
35
+ });
36
+ continue;
37
+ }
38
+
39
+ const patched = normalizedOriginal.replace(normalizedOld, change.new.replace(/\r\n/g, "\n"));
40
+ sandbox.writeFile(filePath, patched);
41
+
42
+ results.push({ file: filePath, success: true });
43
+ }
44
+
45
+ return results;
46
+ }
47
+
48
+ /**
49
+ * Rollback files from a backup entry using BackupManager.
50
+ */
51
+ function rollbackFromBackup(backupManager, backupId) {
52
+ return backupManager.rollbackTo(backupId);
53
+ }
54
+
55
+ module.exports = { applyPatch, rollbackFromBackup };