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,95 @@
1
+ #!/usr/bin/env node
2
+
3
+ const path = require("path");
4
+ const dotenv = require("dotenv");
5
+ const chalk = require("chalk");
6
+
7
+ // Load secrets
8
+ dotenv.config({ path: path.resolve(process.cwd(), ".env.local") });
9
+ dotenv.config({ path: path.resolve(process.cwd(), ".env") });
10
+
11
+ const { loadConfig } = require("../src/core/config");
12
+ const { detect, logSystemInfo } = require("../src/core/system-info");
13
+ const { logModelConfig } = require("../src/core/models");
14
+
15
+ const args = process.argv.slice(2);
16
+ const config = loadConfig();
17
+
18
+ if (args.includes("--help") || args.includes("-h")) {
19
+ console.log(`
20
+ ${chalk.yellow("🐺 Wolverine Node.js")} — Autonomous self-healing server framework
21
+
22
+ ${chalk.bold("Usage:")}
23
+ wolverine <script.js> [options]
24
+
25
+ ${chalk.bold("Options:")}
26
+ --help, -h Show this help
27
+ --single Force single-worker mode (no clustering)
28
+ --workers <n> Force specific worker count
29
+ --info Show system info and exit
30
+
31
+ ${chalk.bold("Configuration:")}
32
+ server/config/settings.json Models, telemetry, limits, health checks
33
+ .env.local Secrets only (API keys, admin key)
34
+
35
+ ${chalk.bold("Examples:")}
36
+ wolverine server/index.js
37
+ wolverine server/index.js --single
38
+ wolverine server/index.js --workers 4
39
+ wolverine --info
40
+ `);
41
+ process.exit(0);
42
+ }
43
+
44
+ // --info: show system info and exit
45
+ if (args.includes("--info")) {
46
+ const info = detect();
47
+ console.log(chalk.yellow.bold("\n 🐺 Wolverine — System Info\n"));
48
+ logSystemInfo(info);
49
+ console.log(chalk.gray(`\n Node: ${info.nodeVersion}`));
50
+ console.log(chalk.gray(` Hostname: ${info.hostname}`));
51
+ console.log(chalk.gray(` Disk: ${info.disk.totalGB}GB total, ${info.disk.freeGB}GB free (${info.disk.usedPercent}% used)`));
52
+ console.log(chalk.gray(` Memory: ${info.memory.totalGB}GB total, ${info.memory.freeGB}GB free (${info.memory.usedPercent}% used)`));
53
+ console.log("");
54
+ process.exit(0);
55
+ }
56
+
57
+ const scriptPath = args.find(a => !a.startsWith("--")) || "server/index.js";
58
+
59
+ // System detection (for analytics + dashboard, NOT for forking)
60
+ // Wolverine runs as a single process manager. If users want clustering,
61
+ // they handle it inside their server (e.g. @fastify/cluster, pm2 cluster mode).
62
+ // Forking workers at the wolverine level causes port conflicts because each
63
+ // worker spawns its own child process on the same port.
64
+ const systemInfo = detect();
65
+
66
+ const { WolverineRunner } = require("../src/core/runner");
67
+
68
+ console.log(chalk.yellow.bold("\n 🐺 Wolverine Node.js — Autonomous Server Agent\n"));
69
+ logSystemInfo(systemInfo);
70
+ console.log("");
71
+
72
+ console.log(chalk.bold(" Models:"));
73
+ logModelConfig(chalk);
74
+ console.log("");
75
+ console.log(chalk.gray(` Script: ${scriptPath}`));
76
+ console.log(chalk.gray(` Port: ${config.server.port}`));
77
+ console.log(chalk.gray(` Retries: ${config.server.maxRetries}`));
78
+ console.log(chalk.gray(` Sandbox: ${path.resolve(process.cwd())} (locked)`));
79
+ console.log(chalk.gray(` Telemetry: ${config.telemetry.enabled ? "on" : "off"}`));
80
+ console.log("");
81
+
82
+ const runner = new WolverineRunner(scriptPath, { cwd: process.cwd() });
83
+
84
+ process.on("SIGINT", () => {
85
+ console.log(chalk.yellow(`\n\nšŸ‘‹ Shutting down Wolverine${workerLabel}...`));
86
+ runner.stop();
87
+ process.exit(0);
88
+ });
89
+
90
+ process.on("SIGTERM", () => {
91
+ runner.stop();
92
+ process.exit(0);
93
+ });
94
+
95
+ runner.start();
@@ -0,0 +1,31 @@
1
+ const http = require("http");
2
+ const PORT = process.env.PORT || 3000;
3
+
4
+ const users = [
5
+ { id: 1, name: "Alice" },
6
+ { id: 2, name: "Bob" },
7
+ { id: 3, name: "Charlie" },
8
+ ];
9
+
10
+ // BUG: `userz` is not defined — should be `users`
11
+ const userCache = JSON.stringify(users);
12
+
13
+ const server = http.createServer((req, res) => {
14
+ if (req.url === "/api/users" && req.method === "GET") {
15
+ res.writeHead(200, { "Content-Type": "application/json" });
16
+ res.end(userCache);
17
+ } else if (req.url === "/health") {
18
+ res.writeHead(200, { "Content-Type": "application/json" });
19
+ res.end(JSON.stringify({ status: "ok", uptime: process.uptime() }));
20
+ } else if (req.url === "/time") {
21
+ res.writeHead(200, { "Content-Type": "application/json" });
22
+ res.end(JSON.stringify({ time: new Date().toISOString() }));
23
+ } else {
24
+ res.writeHead(404);
25
+ res.end("Not found");
26
+ }
27
+ });
28
+
29
+ server.listen(PORT, () => {
30
+ console.log(`Server on http://localhost:${PORT}`);
31
+ });
@@ -0,0 +1,15 @@
1
+ /**
2
+ * User routes module.
3
+ * Exports `getUsers` but the server imports `fetchUsers` — name mismatch.
4
+ */
5
+
6
+ const users = [
7
+ { id: 1, name: "Alice", role: "admin" },
8
+ { id: 2, name: "Bob", role: "user" },
9
+ ];
10
+
11
+ function getUsers() {
12
+ return { users, count: users.length };
13
+ }
14
+
15
+ module.exports = { getUsers };
@@ -0,0 +1,25 @@
1
+ const http = require("http");
2
+ const { getUsers } = require("./routes/users");
3
+
4
+ const PORT = process.env.PORT || 3000;
5
+
6
+ const initialData = getUsers();
7
+ console.log(`Loaded ${initialData.count} users`);
8
+
9
+ const server = http.createServer(async (req, res) => {
10
+ if (req.url === "/api/users" && req.method === "GET") {
11
+ const data = getUsers();
12
+ res.writeHead(200, { "Content-Type": "application/json" });
13
+ res.end(JSON.stringify(data));
14
+ } else if (req.url === "/health") {
15
+ res.writeHead(200, { "Content-Type": "application/json" });
16
+ res.end(JSON.stringify({ status: "ok" }));
17
+ } else {
18
+ res.writeHead(404);
19
+ res.end("Not found");
20
+ }
21
+ });
22
+
23
+ server.listen(PORT, () => {
24
+ console.log(`Multi-file server on http://localhost:${PORT}`);
25
+ });
@@ -0,0 +1,23 @@
1
+ const http = require("http");
2
+ const PORT = process.env.PORT || 3000;
3
+
4
+ function formatResponse(data) {
5
+ return JSON.stringify({ timestamp: Date.now(), data: data });
6
+ }
7
+
8
+ const server = http.createServer((req, res) => {
9
+ if (req.url === "/api/data") {
10
+ res.writeHead(200, { "Content-Type": "application/json" });
11
+ res.end(formatResponse({ status: "ok" }));
12
+ } else if (req.url === "/health") {
13
+ res.writeHead(200, { "Content-Type": "application/json" });
14
+ res.end(formatResponse({ status: "healthy" }));
15
+ } else {
16
+ res.writeHead(404);
17
+ res.end("Not found");
18
+ }
19
+ });
20
+
21
+ server.listen(PORT, () => {
22
+ console.log(`Server on http://localhost:${PORT}`);
23
+ });
@@ -0,0 +1,14 @@
1
+ const http = require("http");
2
+ const PORT = process.env.PORT || 3000;
3
+
4
+ // BUG: calls .connect() on a string — will crash and expose TEST_PHRASE in error
5
+ const secret = process.env.TEST_PHRASE || "fallback";
6
+
7
+ const server = http.createServer((req, res) => {
8
+ res.writeHead(200);
9
+ res.end("ok");
10
+ });
11
+
12
+ server.listen(PORT, () => {
13
+ console.log(`Server on http://localhost:${PORT}`);
14
+ });
@@ -0,0 +1,27 @@
1
+ const http = require("http");
2
+ const https = require("https");
3
+ const PORT = process.env.PORT || 3000;
4
+
5
+ const options = {
6
+ hostname: "api.example-that-does-not-exist.com",
7
+ path: "/v1/data",
8
+ headers: { "Authorization": "Bearer expired_token_12345" },
9
+ };
10
+
11
+ // BUG: throws on ENOTFOUND — should be caught, not thrown
12
+ const req = https.get(options, (res) => {
13
+ console.log("Status:", res.statusCode);
14
+ });
15
+
16
+ req.on("error", (err) => {
17
+ console.error(`503 Service Unavailable: External API down - ${err.message}`);
18
+ });
19
+
20
+ const server = http.createServer((reqIn, res) => {
21
+ res.writeHead(200);
22
+ res.end("ok");
23
+ });
24
+
25
+ server.listen(PORT, () => {
26
+ console.log(`Server on http://localhost:${PORT}`);
27
+ });
@@ -0,0 +1,13 @@
1
+ {
2
+ "appName": "MyApp",
3
+ "version": "1.0.0",
4
+ "databse": {
5
+ "host": "localhost",
6
+ "port": 5432,
7
+ "name": "myapp_db"
8
+ },
9
+ "cache": {
10
+ "enabled": true,
11
+ "ttl": 3600
12
+ }
13
+ }
@@ -0,0 +1,28 @@
1
+ const http = require("http");
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+
5
+ const PORT = process.env.PORT || 3000;
6
+
7
+ const configPath = path.join(__dirname, "config.json");
8
+ const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
9
+
10
+ // BUG: config.json has "databse" not "database"
11
+ const dbHost = config.databse.host;
12
+ const dbPort = config.databse.port;
13
+
14
+ console.log(`Connecting to database at ${dbHost}:${dbPort}`);
15
+
16
+ const server = http.createServer((req, res) => {
17
+ if (req.url === "/health") {
18
+ res.writeHead(200, { "Content-Type": "application/json" });
19
+ res.end(JSON.stringify({ status: "ok", db: `${dbHost}:${dbPort}` }));
20
+ } else {
21
+ res.writeHead(404);
22
+ res.end("Not found");
23
+ }
24
+ });
25
+
26
+ server.listen(PORT, () => {
27
+ console.log(`Server on http://localhost:${PORT}`);
28
+ });
@@ -0,0 +1,11 @@
1
+ const http = require("http");
2
+ const PORT = process.env.PORT || 3000;
3
+
4
+
5
+
6
+ const server = http.createServer((req, res) => {
7
+ res.writeHead(200);
8
+ res.end("ok");
9
+ });
10
+
11
+ server.listen(PORT);
@@ -0,0 +1,20 @@
1
+ const http = require("http");
2
+ const PORT = process.env.PORT || 3000;
3
+
4
+ // BUG: null.toString() crashes at startup
5
+ const config = null;
6
+ const appName = config?.toString() || "app";
7
+
8
+ const server = http.createServer((req, res) => {
9
+ if (req.url === "/health") {
10
+ res.writeHead(200, { "Content-Type": "application/json" });
11
+ res.end(JSON.stringify({ status: "ok", app: appName }));
12
+ } else {
13
+ res.writeHead(404);
14
+ res.end("Not found");
15
+ }
16
+ });
17
+
18
+ server.listen(PORT, () => {
19
+ console.log(`${appName} server on http://localhost:${PORT}`);
20
+ });
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Example: A deliberately buggy HTTP server.
3
+ * Wolverine will detect the crash on startup and fix it automatically.
4
+ *
5
+ * BUG: `parseConfig` calls `settings.toUpperCase()` but settings is an object, not a string.
6
+ */
7
+
8
+ const http = require("http");
9
+
10
+ const PORT = process.env.PORT || 3000;
11
+
12
+ const settings = { mode: "production", debug: false };
13
+
14
+ // BUG: settings is an object, calling .toUpperCase() on it will throw
15
+ const configMode = settings.mode.toUpperCase();
16
+
17
+ const users = [
18
+ { id: 1, name: "Alice", role: configMode },
19
+ { id: 2, name: "Bob", role: "user" },
20
+ ];
21
+
22
+ const server = http.createServer((req, res) => {
23
+ if (req.url === "/api/users" && req.method === "GET") {
24
+ res.writeHead(200, { "Content-Type": "application/json" });
25
+ res.end(JSON.stringify({ users }));
26
+ } else if (req.url === "/health" && req.method === "GET") {
27
+ res.writeHead(200, { "Content-Type": "application/json" });
28
+ res.end(JSON.stringify({ status: "ok", uptime: process.uptime() }));
29
+ } else {
30
+ res.writeHead(404, { "Content-Type": "application/json" });
31
+ res.end(JSON.stringify({ error: "Not found" }));
32
+ }
33
+ });
34
+
35
+ server.listen(PORT, () => {
36
+ console.log(`Server running on http://localhost:${PORT}`);
37
+ console.log(`Health: http://localhost:${PORT}/health`);
38
+ console.log(`Users: http://localhost:${PORT}/api/users`);
39
+ });
@@ -0,0 +1,20 @@
1
+ const express = require("express");
2
+ const app = express();
3
+ const PORT = process.env.PORT || 3000;
4
+
5
+ // BUG: routes/api.js references `userz` instead of `users` — crashes on require
6
+ app.use(express.json());
7
+
8
+ const healthRoutes = require("./routes/health");
9
+ const apiRoutes = require("./routes/api");
10
+
11
+ app.use("/health", healthRoutes);
12
+ app.use("/api", apiRoutes);
13
+
14
+ app.get("/", (req, res) => {
15
+ res.json({ name: "Demo 01 — Basic Typo", status: "running" });
16
+ });
17
+
18
+ app.listen(PORT, () => {
19
+ console.log(`Server running on http://localhost:${PORT}`);
20
+ });
@@ -0,0 +1,13 @@
1
+ const express = require("express");
2
+ const router = express.Router();
3
+
4
+ const users = [
5
+ { id: 1, name: "Alice" },
6
+ { id: 2, name: "Bob" },
7
+ ];
8
+
9
+ // BUG: `userz` should be `users` — crashes on startup when building cache
10
+ const userCache = JSON.stringify(userz);
11
+
12
+ router.get("/users", (req, res) => { res.json(JSON.parse(userCache)); });
13
+ module.exports = router;
@@ -0,0 +1,4 @@
1
+ const express = require("express");
2
+ const router = express.Router();
3
+ router.get("/", (req, res) => { res.json({ status: "ok", uptime: process.uptime() }); });
4
+ module.exports = router;
@@ -0,0 +1,24 @@
1
+ const express = require("express");
2
+ const app = express();
3
+ const PORT = process.env.PORT || 3000;
4
+
5
+ app.use(express.json());
6
+
7
+ const healthRoutes = require("./routes/health");
8
+ const apiRoutes = require("./routes/api");
9
+
10
+ app.use("/health", healthRoutes);
11
+ app.use("/api", apiRoutes);
12
+
13
+ // BUG: fetchUsers doesn't exist — routes/api.js exports getUsers
14
+ const { fetchUsers } = require("./routes/api");
15
+ const initialData = fetchUsers();
16
+ console.log(`Loaded ${initialData.count} users`);
17
+
18
+ app.get("/", (req, res) => {
19
+ res.json({ name: "Demo 02 — Multi-File", status: "running" });
20
+ });
21
+
22
+ app.listen(PORT, () => {
23
+ console.log(`Server running on http://localhost:${PORT}`);
24
+ });
@@ -0,0 +1,13 @@
1
+ const express = require("express");
2
+ const router = express.Router();
3
+
4
+ const users = [
5
+ { id: 1, name: "Alice", role: "admin" },
6
+ { id: 2, name: "Bob", role: "user" },
7
+ ];
8
+
9
+ // Exports getUsers, but index.js imports fetchUsers — name mismatch
10
+ function getUsers() { return { users, count: users.length }; }
11
+
12
+ router.get("/users", (req, res) => { res.json(getUsers()); });
13
+ module.exports = { getUsers };
@@ -0,0 +1,4 @@
1
+ const express = require("express");
2
+ const router = express.Router();
3
+ router.get("/", (req, res) => { res.json({ status: "ok", uptime: process.uptime() }); });
4
+ module.exports = router;
@@ -0,0 +1,18 @@
1
+ const express = require("express");
2
+ const app = express();
3
+ const PORT = process.env.PORT || 3000;
4
+
5
+ app.use(express.json());
6
+
7
+ // BUG: extra closing paren on the route handler
8
+ app.get("/health", (req, res) => {
9
+ res.json({ status: "ok", uptime: process.uptime() });
10
+ }));
11
+
12
+ app.get("/", (req, res) => {
13
+ res.json({ name: "Demo 03 — Syntax Error", status: "running" });
14
+ });
15
+
16
+ app.listen(PORT, () => {
17
+ console.log(`Server running on http://localhost:${PORT}`);
18
+ });
@@ -0,0 +1,16 @@
1
+ const express = require("express");
2
+ const app = express();
3
+ const PORT = process.env.PORT || 3000;
4
+
5
+ app.use(express.json());
6
+
7
+ // BUG: calls .connect() on a string — crashes and exposes TEST_PHRASE in error
8
+ const secret = process.env.TEST_PHRASE || "fallback";
9
+ const connection = secret.connect("localhost:5432");
10
+
11
+ app.get("/health", (req, res) => { res.json({ status: "ok" }); });
12
+ app.get("/", (req, res) => { res.json({ name: "Demo 04 — Secret Leak" }); });
13
+
14
+ app.listen(PORT, () => {
15
+ console.log(`Server running on http://localhost:${PORT}`);
16
+ });
@@ -0,0 +1,21 @@
1
+ const express = require("express");
2
+ const https = require("https");
3
+ const app = express();
4
+ const PORT = process.env.PORT || 3000;
5
+
6
+ app.use(express.json());
7
+
8
+ // BUG: calls a non-existent API, then throws on the error
9
+ const req = https.get({ hostname: "api.example-that-does-not-exist.com", path: "/v1/data" }, (res) => {
10
+ console.log("Status:", res.statusCode);
11
+ });
12
+ req.on("error", (err) => {
13
+ throw new Error(`503 Service Unavailable: External API down - ${err.message}`);
14
+ });
15
+
16
+ app.get("/health", (req, res) => { res.json({ status: "ok" }); });
17
+ app.get("/", (req, res) => { res.json({ name: "Demo 05 — Expired Key" }); });
18
+
19
+ app.listen(PORT, () => {
20
+ console.log(`Server running on http://localhost:${PORT}`);
21
+ });
@@ -0,0 +1,9 @@
1
+ {
2
+ "appName": "MyApp",
3
+ "version": "1.0.0",
4
+ "databse": {
5
+ "host": "localhost",
6
+ "port": 5432,
7
+ "name": "myapp_db"
8
+ }
9
+ }
@@ -0,0 +1,20 @@
1
+ const express = require("express");
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+ const app = express();
5
+ const PORT = process.env.PORT || 3000;
6
+
7
+ app.use(express.json());
8
+
9
+ // BUG: config.json has "databse" not "database"
10
+ const config = JSON.parse(fs.readFileSync(path.join(__dirname, "config.json"), "utf-8"));
11
+ const dbHost = config.database.host;
12
+ const dbPort = config.database.port;
13
+ console.log(`Connecting to database at ${dbHost}:${dbPort}`);
14
+
15
+ app.get("/health", (req, res) => { res.json({ status: "ok", db: `${dbHost}:${dbPort}` }); });
16
+ app.get("/", (req, res) => { res.json({ name: "Demo 06 — JSON Config" }); });
17
+
18
+ app.listen(PORT, () => {
19
+ console.log(`Server running on http://localhost:${PORT}`);
20
+ });
@@ -0,0 +1,16 @@
1
+ const express = require("express");
2
+ const app = express();
3
+ const PORT = process.env.PORT || 3000;
4
+
5
+ app.use(express.json());
6
+
7
+ // BUG: null.toString() crashes at startup
8
+ const config = null;
9
+ const appName = config.toString();
10
+
11
+ app.get("/health", (req, res) => { res.json({ status: "ok", app: appName }); });
12
+ app.get("/", (req, res) => { res.json({ name: appName }); });
13
+
14
+ app.listen(PORT, () => {
15
+ console.log(`${appName} server on http://localhost:${PORT}`);
16
+ });
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Demo Runner — copies a demo into server/, runs wolverine, then restores.
5
+ *
6
+ * Usage:
7
+ * node examples/run-demo.js 01-basic-typo
8
+ * node examples/run-demo.js 02-multi-file
9
+ * node examples/run-demo.js --list
10
+ *
11
+ * What it does:
12
+ * 1. Backs up the current server/ directory
13
+ * 2. Copies the demo files into server/
14
+ * 3. Runs wolverine against server/index.js
15
+ * 4. On exit, restores the original server/
16
+ */
17
+
18
+ const { spawn, execSync } = require("child_process");
19
+ const fs = require("fs");
20
+ const path = require("path");
21
+
22
+ const ROOT = path.resolve(__dirname, "..");
23
+ const DEMOS_DIR = path.join(__dirname, "demos");
24
+ const SERVER_DIR = path.join(ROOT, "server");
25
+ const BACKUP_DIR = path.join(ROOT, ".wolverine", "_server_backup");
26
+ const BIN = path.join(ROOT, "bin", "wolverine.js");
27
+
28
+ // List demos
29
+ if (process.argv.includes("--list") || process.argv.length < 3) {
30
+ const demos = fs.readdirSync(DEMOS_DIR).filter(f => fs.statSync(path.join(DEMOS_DIR, f)).isDirectory());
31
+ console.log("\nAvailable demos:\n");
32
+ for (const demo of demos) {
33
+ const indexPath = path.join(DEMOS_DIR, demo, "index.js");
34
+ if (fs.existsSync(indexPath)) {
35
+ const content = fs.readFileSync(indexPath, "utf-8");
36
+ const bugMatch = content.match(/\/\/ BUG:(.+)/);
37
+ const bug = bugMatch ? bugMatch[1].trim() : "see source";
38
+ console.log(` ${demo.padEnd(25)} ${bug}`);
39
+ }
40
+ }
41
+ console.log("\nUsage: node examples/run-demo.js <demo-name>\n");
42
+ process.exit(0);
43
+ }
44
+
45
+ const demoName = process.argv[2];
46
+ const demoDir = path.join(DEMOS_DIR, demoName);
47
+
48
+ if (!fs.existsSync(demoDir)) {
49
+ console.error(`Demo not found: ${demoName}`);
50
+ console.error(`Run with --list to see available demos.`);
51
+ process.exit(1);
52
+ }
53
+
54
+ // Step 1: Backup current server/
55
+ console.log(`\nšŸ“¦ Backing up server/ → .wolverine/_server_backup/`);
56
+ copyDir(SERVER_DIR, BACKUP_DIR);
57
+
58
+ // Step 2: Copy demo into server/
59
+ console.log(`šŸ“‹ Copying demo '${demoName}' → server/`);
60
+ clearDir(SERVER_DIR);
61
+ copyDir(demoDir, SERVER_DIR);
62
+
63
+ // Step 3: Run wolverine
64
+ console.log(`🐺 Starting wolverine with demo...\n`);
65
+
66
+ const child = spawn("node", [BIN, "server/index.js"], {
67
+ cwd: ROOT,
68
+ stdio: "inherit",
69
+ env: { ...process.env },
70
+ });
71
+
72
+ // Step 4: Restore on exit
73
+ function restore() {
74
+ console.log(`\nšŸ“¦ Restoring original server/`);
75
+ clearDir(SERVER_DIR);
76
+ copyDir(BACKUP_DIR, SERVER_DIR);
77
+ clearDir(BACKUP_DIR);
78
+ try { fs.rmdirSync(BACKUP_DIR); } catch {}
79
+ }
80
+
81
+ process.on("SIGINT", () => { child.kill("SIGTERM"); restore(); process.exit(0); });
82
+ process.on("SIGTERM", () => { child.kill("SIGTERM"); restore(); process.exit(0); });
83
+ child.on("exit", () => { restore(); });
84
+
85
+ // Helpers
86
+ function copyDir(src, dest) {
87
+ fs.mkdirSync(dest, { recursive: true });
88
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
89
+ const srcPath = path.join(src, entry.name);
90
+ const destPath = path.join(dest, entry.name);
91
+ if (entry.isDirectory()) {
92
+ copyDir(srcPath, destPath);
93
+ } else {
94
+ fs.copyFileSync(srcPath, destPath);
95
+ }
96
+ }
97
+ }
98
+
99
+ function clearDir(dir) {
100
+ if (!fs.existsSync(dir)) return;
101
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
102
+ const p = path.join(dir, entry.name);
103
+ if (entry.isDirectory()) {
104
+ clearDir(p);
105
+ fs.rmdirSync(p);
106
+ } else {
107
+ fs.unlinkSync(p);
108
+ }
109
+ }
110
+ }