wolverine-ai 6.3.1 → 6.4.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": "6.3.1",
3
+ "version": "6.4.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": {
@@ -0,0 +1,443 @@
1
+ /**
2
+ * Wolverine API — unified entry point for any OpenClaw process.
3
+ *
4
+ * Exposes every wolverine subsystem through a single, lazy-loaded API.
5
+ * Nothing is imported until first use, so requiring this module is free.
6
+ *
7
+ * Usage from any OpenClaw skill, plugin, or agent:
8
+ * const wolverine = require("wolverine-ai/src/claw/wolverine-api");
9
+ * const api = wolverine.init("/path/to/project");
10
+ *
11
+ * // Security
12
+ * api.security.detectInjection("some user input");
13
+ * api.security.redact("text with sk-abc123 key");
14
+ * api.security.sandbox.resolve("server/index.js");
15
+ *
16
+ * // Brain
17
+ * await api.brain.search("how to fix ECONNREFUSED");
18
+ * await api.brain.learn("Redis needs REDIS_URL env var", "fix");
19
+ *
20
+ * // AI
21
+ * const result = await api.ai.call({ model: "claude-sonnet-4-6", ... });
22
+ *
23
+ * // Errors
24
+ * api.errors.parse(stderrText);
25
+ * api.errors.classify(errorMessage);
26
+ * api.errors.routeTools("ECONNREFUSED");
27
+ *
28
+ * // Backup
29
+ * api.backup.create("before risky change");
30
+ * api.backup.rollbackLatest();
31
+ *
32
+ * // ... etc
33
+ */
34
+
35
+ const path = require("path");
36
+
37
+ let _projectRoot = null;
38
+ let _initialized = false;
39
+
40
+ // Lazy singletons — created on first access
41
+ const _cache = {};
42
+
43
+ function _require(relativePath) {
44
+ return require(path.join(_projectRoot, relativePath));
45
+ }
46
+
47
+ function _lazy(key, factory) {
48
+ if (!_cache[key]) _cache[key] = factory();
49
+ return _cache[key];
50
+ }
51
+
52
+ // ── Public API ──────────────────────────────────────────────────
53
+
54
+ /**
55
+ * Initialize the Wolverine API for a project.
56
+ * Call once — returns the API object. Subsequent calls return the same instance.
57
+ */
58
+ function init(projectRoot) {
59
+ if (_initialized && _projectRoot === path.resolve(projectRoot)) return api;
60
+ _projectRoot = path.resolve(projectRoot);
61
+ _initialized = true;
62
+
63
+ // Clear cache if re-initializing with different root
64
+ for (const key of Object.keys(_cache)) delete _cache[key];
65
+
66
+ return api;
67
+ }
68
+
69
+ const api = {
70
+ // ── Security ────────────────────────────────────────────────
71
+
72
+ get security() {
73
+ return _lazy("security", () => {
74
+ const { detectInjection, localScan, INJECTION_PATTERNS } = _require("src/security/injection-detector");
75
+ const { initRedactor, redact, redactObj, hasSecrets } = _require("src/security/secret-redactor");
76
+ const { Sandbox } = _require("src/security/sandbox");
77
+ const { RateLimiter } = _require("src/security/rate-limiter");
78
+
79
+ // Initialize redactor for this project
80
+ const redactor = initRedactor(_projectRoot);
81
+
82
+ return {
83
+ // Prompt injection detection (~50 patterns)
84
+ detectInjection,
85
+ localScan,
86
+ INJECTION_PATTERNS,
87
+
88
+ // Secret redaction
89
+ redact,
90
+ redactObj,
91
+ hasSecrets,
92
+ redactor,
93
+
94
+ // File sandbox
95
+ sandbox: new Sandbox(_projectRoot),
96
+ Sandbox,
97
+
98
+ // Rate limiting
99
+ createRateLimiter: (opts) => new RateLimiter(opts),
100
+ RateLimiter,
101
+ };
102
+ });
103
+ },
104
+
105
+ // ── Brain (semantic memory) ─────────────────────────────────
106
+
107
+ get brain() {
108
+ return _lazy("brain", () => {
109
+ const { Brain } = _require("src/brain/brain");
110
+ const { VectorStore } = _require("src/brain/vector-store");
111
+ const { embed, embedBatch, compact, compactAndEmbed } = _require("src/brain/embedder");
112
+ const { scanProject, mapToChunks } = _require("src/brain/function-map");
113
+ const { route, getRoutePrompt, TOOL_ROUTES } = _require("src/brain/tool-router");
114
+
115
+ const brain = new Brain(_projectRoot);
116
+ let _initPromise = null;
117
+
118
+ return {
119
+ // High-level brain operations
120
+ async search(query, opts) {
121
+ if (!_initPromise) _initPromise = brain.init();
122
+ await _initPromise;
123
+ return brain.search ? brain.search(query, opts)
124
+ : brain.recall ? brain.recall(query, opts)
125
+ : [];
126
+ },
127
+ async learn(content, category) {
128
+ if (!_initPromise) _initPromise = brain.init();
129
+ await _initPromise;
130
+ if (brain.addDocument) {
131
+ return brain.addDocument({ content, namespace: category || "learnings", source: "wolverine-claw", timestamp: Date.now() });
132
+ }
133
+ if (brain.remember) return brain.remember(content, { namespace: category });
134
+ },
135
+ async getContext(query) {
136
+ if (!_initPromise) _initPromise = brain.init();
137
+ await _initPromise;
138
+ return brain.getContext ? brain.getContext(query) : "";
139
+ },
140
+ async init() {
141
+ if (!_initPromise) _initPromise = brain.init();
142
+ return _initPromise;
143
+ },
144
+
145
+ // Low-level access
146
+ instance: brain,
147
+ VectorStore,
148
+ embed,
149
+ embedBatch,
150
+ compact,
151
+ compactAndEmbed,
152
+ scanProject,
153
+ mapToChunks,
154
+
155
+ // Tool router — maps error types to tool chains
156
+ routeTools: route,
157
+ getRoutePrompt,
158
+ TOOL_ROUTES,
159
+ };
160
+ });
161
+ },
162
+
163
+ // ── AI Client ───────────────────────────────────────────────
164
+
165
+ get ai() {
166
+ return _lazy("ai", () => {
167
+ const client = _require("src/core/ai-client");
168
+ const { getModel, getEmbeddingModel, getModelConfig, detectProvider, MODEL_ROLES } = _require("src/core/models");
169
+
170
+ return {
171
+ // Make AI calls (auto-detects provider from model name)
172
+ call: client.aiCall,
173
+ callWithHistory: client.aiCallWithHistory,
174
+
175
+ // Embeddings
176
+ embed: async (text) => {
177
+ const { embed } = _require("src/brain/embedder");
178
+ return embed(text);
179
+ },
180
+
181
+ // Model utilities
182
+ getModel,
183
+ getEmbeddingModel,
184
+ getModelConfig,
185
+ detectProvider,
186
+ MODEL_ROLES,
187
+
188
+ // Token tracking
189
+ setTokenTracker: client.setTokenTracker,
190
+ getTrackerSnapshot: client.getTrackerSnapshot,
191
+ };
192
+ });
193
+ },
194
+
195
+ // ── Error Handling ──────────────────────────────────────────
196
+
197
+ get errors() {
198
+ return _lazy("errors", () => {
199
+ const { parseError, classifyError } = _require("src/core/error-parser");
200
+ const { route, getRoutePrompt } = _require("src/brain/tool-router");
201
+ const { Notifier, HUMAN_REQUIRED_PATTERNS } = _require("src/notifications/notifier");
202
+ const { LoopGuard } = _require("src/skills/loop-guard");
203
+
204
+ return {
205
+ // Parse error text → structured {file, line, message, errorType}
206
+ parse: parseError,
207
+ // Classify error → type (missing_module, syntax, runtime, etc.)
208
+ classify: classifyError,
209
+ // Map error type → recommended tool chain
210
+ routeTools: route,
211
+ getRoutePrompt,
212
+
213
+ // Detect if error needs human vs can be auto-fixed
214
+ HUMAN_REQUIRED_PATTERNS,
215
+ createNotifier: (opts) => new Notifier(opts),
216
+
217
+ // Loop detection — prevent infinite fix attempts
218
+ createLoopGuard: (opts) => new LoopGuard(_projectRoot, opts),
219
+ };
220
+ });
221
+ },
222
+
223
+ // ── Backup & Recovery ───────────────────────────────────────
224
+
225
+ get backup() {
226
+ return _lazy("backup", () => {
227
+ const { BackupManager } = _require("src/backup/backup-manager");
228
+ const manager = new BackupManager(_projectRoot);
229
+
230
+ return {
231
+ create: (reason) => manager.createBackup(reason),
232
+ rollback: (id) => manager.rollbackTo(id),
233
+ rollbackLatest: () => manager.rollbackLatest(),
234
+ undoRollback: () => manager.undoRollback(),
235
+ list: () => manager.list(),
236
+ getStats: () => manager.getStats(),
237
+ promote: (id) => manager.promote(id),
238
+ prune: () => manager.prune(),
239
+ instance: manager,
240
+ };
241
+ });
242
+ },
243
+
244
+ // ── Skills ──────────────────────────────────────────────────
245
+
246
+ get skills() {
247
+ return _lazy("skills", () => {
248
+ const { SkillRegistry } = _require("src/skills/skill-registry");
249
+ const registry = new SkillRegistry();
250
+ registry.load();
251
+
252
+ return {
253
+ // Skill registry
254
+ match: (query) => registry.match(query),
255
+ list: () => registry.getAll(),
256
+ registry,
257
+
258
+ // SQL injection protection
259
+ get sql() {
260
+ const { scanForInjection, deepScan, sqlGuard } = _require("src/skills/sql");
261
+ return { scanForInjection, deepScan, sqlGuard };
262
+ },
263
+
264
+ // Dependency analysis
265
+ get deps() {
266
+ const deps = _require("src/skills/deps");
267
+ return {
268
+ diagnose: deps.diagnose,
269
+ healthReport: deps.healthReport,
270
+ getMigration: deps.getMigration,
271
+ };
272
+ },
273
+
274
+ // Backup (alias)
275
+ get backup() {
276
+ const b = _require("src/skills/backup");
277
+ return { backup: b.backup, rollback: b.rollback, rollbackLatest: b.rollbackLatest, undoRollback: b.undoRollback, listBackups: b.listBackups };
278
+ },
279
+ };
280
+ });
281
+ },
282
+
283
+ // ── Monitoring ──────────────────────────────────────────────
284
+
285
+ get monitor() {
286
+ return _lazy("monitor", () => {
287
+ const { ErrorMonitor } = _require("src/monitor/error-monitor");
288
+ const { PerfMonitor } = _require("src/monitor/perf-monitor");
289
+ const { ProcessMonitor } = _require("src/monitor/process-monitor");
290
+ const { AdaptiveLimiter } = _require("src/monitor/adaptive-limiter");
291
+
292
+ return {
293
+ ErrorMonitor,
294
+ PerfMonitor,
295
+ ProcessMonitor,
296
+ AdaptiveLimiter,
297
+ };
298
+ });
299
+ },
300
+
301
+ // ── Logging & Metrics ───────────────────────────────────────
302
+
303
+ get logger() {
304
+ return _lazy("logger", () => {
305
+ const { EventLogger, EVENT_TYPES, SEVERITY } = _require("src/logger/event-logger");
306
+ const { TokenTracker } = _require("src/logger/token-tracker");
307
+ const { RepairHistory } = _require("src/logger/repair-history");
308
+
309
+ return {
310
+ createEventLogger: () => new EventLogger(_projectRoot),
311
+ createTokenTracker: () => new TokenTracker(_projectRoot),
312
+ createRepairHistory: () => new RepairHistory(_projectRoot),
313
+ EVENT_TYPES,
314
+ SEVERITY,
315
+ };
316
+ });
317
+ },
318
+
319
+ // ── Config ──────────────────────────────────────────────────
320
+
321
+ get config() {
322
+ return _lazy("config", () => {
323
+ const { loadConfig, getConfig } = _require("src/core/config");
324
+ return { load: loadConfig, get: getConfig };
325
+ });
326
+ },
327
+
328
+ // ── Agent Engine ────────────────────────────────────────────
329
+
330
+ get agent() {
331
+ return _lazy("agent", () => {
332
+ const { AgentEngine, TOOL_DEFINITIONS, BLOCKED_COMMANDS } = _require("src/agent/agent-engine");
333
+ const { Sandbox } = _require("src/security/sandbox");
334
+
335
+ return {
336
+ // Create a sandboxed agent engine
337
+ create: (opts = {}) => new AgentEngine({
338
+ cwd: opts.cwd || _projectRoot,
339
+ sandbox: opts.sandbox || new Sandbox(opts.cwd || _projectRoot),
340
+ maxTurns: opts.maxTurns || 25,
341
+ maxTokens: opts.maxTokens || 100000,
342
+ logger: opts.logger,
343
+ mcp: opts.mcp,
344
+ }),
345
+ TOOL_DEFINITIONS,
346
+ BLOCKED_COMMANDS,
347
+ AgentEngine,
348
+ };
349
+ });
350
+ },
351
+
352
+ // ── Server Context ──────────────────────────────────────────
353
+
354
+ get context() {
355
+ return _lazy("context", () => {
356
+ const { scan, load, getSummary } = _require("src/core/server-context");
357
+ return {
358
+ scan: () => scan(_projectRoot),
359
+ load: () => load(_projectRoot),
360
+ getSummary: () => getSummary(_projectRoot),
361
+ };
362
+ });
363
+ },
364
+
365
+ // ── Verification ────────────────────────────────────────────
366
+
367
+ get verify() {
368
+ return _lazy("verify", () => {
369
+ const { verifyFix, syntaxCheck, bootProbe } = _require("src/core/verifier");
370
+ return { verifyFix, syntaxCheck, bootProbe };
371
+ });
372
+ },
373
+
374
+ // ── MCP (Model Context Protocol) ───────────────────────────
375
+
376
+ get mcp() {
377
+ return _lazy("mcp", () => {
378
+ const { McpRegistry } = _require("src/mcp/mcp-registry");
379
+ return {
380
+ create: (opts = {}) => new McpRegistry({
381
+ projectRoot: _projectRoot,
382
+ ...opts,
383
+ }),
384
+ McpRegistry,
385
+ };
386
+ });
387
+ },
388
+
389
+ // ── Convenience: all-in-one scan ───────────────────────────
390
+
391
+ /**
392
+ * Run a fast security scan on text (regex injection + secrets). Sync, no AI call.
393
+ * For deep AI-powered injection scan, use api.security.detectInjection() (async).
394
+ * Returns { safe, injection, secrets, redacted }.
395
+ */
396
+ scanText(text) {
397
+ const sec = this.security;
398
+ const injectionResult = sec.localScan(text);
399
+ const hasSecretContent = sec.hasSecrets(text);
400
+ const redacted = sec.redact(text);
401
+
402
+ return {
403
+ safe: injectionResult.safe !== false && !hasSecretContent,
404
+ injection: injectionResult,
405
+ secrets: hasSecretContent,
406
+ redacted,
407
+ };
408
+ },
409
+
410
+ /**
411
+ * Diagnose an error: parse, classify, route tools, check if human-needed.
412
+ * Returns { parsed, type, tools, humanRequired }.
413
+ */
414
+ diagnoseError(errorText) {
415
+ const err = this.errors;
416
+ const parsed = err.parse(errorText);
417
+ const toolRoute = err.routeTools(parsed.errorType || "unknown");
418
+
419
+ // Check if human required
420
+ let humanRequired = false;
421
+ for (const pattern of err.HUMAN_REQUIRED_PATTERNS) {
422
+ if (pattern.test ? pattern.test(errorText) : errorText.includes(pattern)) {
423
+ humanRequired = true;
424
+ break;
425
+ }
426
+ }
427
+
428
+ return {
429
+ parsed,
430
+ type: parsed.errorType,
431
+ tools: toolRoute,
432
+ humanRequired,
433
+ };
434
+ },
435
+
436
+ /** Get the project root. */
437
+ get projectRoot() { return _projectRoot; },
438
+
439
+ /** Check if initialized. */
440
+ get initialized() { return _initialized; },
441
+ };
442
+
443
+ module.exports = { init, api };