craftclose 0.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/LICENSE +21 -0
- package/README.md +176 -0
- package/SKILL.md +139 -0
- package/craftclose.example.yml +84 -0
- package/dist/ai-analyzer.d.ts +96 -0
- package/dist/ai-analyzer.d.ts.map +1 -0
- package/dist/ai-analyzer.js +275 -0
- package/dist/ai-analyzer.js.map +1 -0
- package/dist/alerts/discord.d.ts +23 -0
- package/dist/alerts/discord.d.ts.map +1 -0
- package/dist/alerts/discord.js +95 -0
- package/dist/alerts/discord.js.map +1 -0
- package/dist/alerts/index.d.ts +5 -0
- package/dist/alerts/index.d.ts.map +1 -0
- package/dist/alerts/index.js +8 -0
- package/dist/alerts/index.js.map +1 -0
- package/dist/alerts/telegram.d.ts +25 -0
- package/dist/alerts/telegram.d.ts.map +1 -0
- package/dist/alerts/telegram.js +72 -0
- package/dist/alerts/telegram.js.map +1 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +410 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +36 -0
- package/dist/config.js.map +1 -0
- package/dist/connectors/index.d.ts +8 -0
- package/dist/connectors/index.d.ts.map +1 -0
- package/dist/connectors/index.js +14 -0
- package/dist/connectors/index.js.map +1 -0
- package/dist/connectors/parsers.d.ts +24 -0
- package/dist/connectors/parsers.d.ts.map +1 -0
- package/dist/connectors/parsers.js +64 -0
- package/dist/connectors/parsers.js.map +1 -0
- package/dist/connectors/pterodactyl.d.ts +90 -0
- package/dist/connectors/pterodactyl.d.ts.map +1 -0
- package/dist/connectors/pterodactyl.js +221 -0
- package/dist/connectors/pterodactyl.js.map +1 -0
- package/dist/connectors/rcon.d.ts +51 -0
- package/dist/connectors/rcon.d.ts.map +1 -0
- package/dist/connectors/rcon.js +95 -0
- package/dist/connectors/rcon.js.map +1 -0
- package/dist/connectors/ssh.d.ts +65 -0
- package/dist/connectors/ssh.d.ts.map +1 -0
- package/dist/connectors/ssh.js +273 -0
- package/dist/connectors/ssh.js.map +1 -0
- package/dist/craft-close-skill.d.ts +106 -0
- package/dist/craft-close-skill.d.ts.map +1 -0
- package/dist/craft-close-skill.js +604 -0
- package/dist/craft-close-skill.js.map +1 -0
- package/dist/history.d.ts +32 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +194 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/openclaw-entry.d.ts +29 -0
- package/dist/openclaw-entry.d.ts.map +1 -0
- package/dist/openclaw-entry.js +45 -0
- package/dist/openclaw-entry.js.map +1 -0
- package/dist/patterns/index.d.ts +32 -0
- package/dist/patterns/index.d.ts.map +1 -0
- package/dist/patterns/index.js +248 -0
- package/dist/patterns/index.js.map +1 -0
- package/dist/security/action-allowlist.d.ts +21 -0
- package/dist/security/action-allowlist.d.ts.map +1 -0
- package/dist/security/action-allowlist.js +54 -0
- package/dist/security/action-allowlist.js.map +1 -0
- package/dist/security/audit-log.d.ts +22 -0
- package/dist/security/audit-log.d.ts.map +1 -0
- package/dist/security/audit-log.js +49 -0
- package/dist/security/audit-log.js.map +1 -0
- package/dist/security/index.d.ts +10 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +20 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/input-sanitizer.d.ts +32 -0
- package/dist/security/input-sanitizer.d.ts.map +1 -0
- package/dist/security/input-sanitizer.js +81 -0
- package/dist/security/input-sanitizer.js.map +1 -0
- package/dist/security/path-jail.d.ts +25 -0
- package/dist/security/path-jail.d.ts.map +1 -0
- package/dist/security/path-jail.js +73 -0
- package/dist/security/path-jail.js.map +1 -0
- package/dist/security/rcon-filter.d.ts +24 -0
- package/dist/security/rcon-filter.d.ts.map +1 -0
- package/dist/security/rcon-filter.js +76 -0
- package/dist/security/rcon-filter.js.map +1 -0
- package/dist/types.d.ts +154 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/package.json +74 -0
|
@@ -0,0 +1,604 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* CraftClose Skill — Main orchestrator
|
|
4
|
+
*
|
|
5
|
+
* Coordinates monitoring, crash detection, pattern matching, AI analysis, and alerting.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.CraftCloseSkill = void 0;
|
|
9
|
+
const ssh_js_1 = require("./connectors/ssh.js");
|
|
10
|
+
const rcon_js_1 = require("./connectors/rcon.js");
|
|
11
|
+
const pterodactyl_js_1 = require("./connectors/pterodactyl.js");
|
|
12
|
+
const parsers_js_1 = require("./connectors/parsers.js");
|
|
13
|
+
const telegram_js_1 = require("./alerts/telegram.js");
|
|
14
|
+
const discord_js_1 = require("./alerts/discord.js");
|
|
15
|
+
const index_js_1 = require("./patterns/index.js");
|
|
16
|
+
const history_js_1 = require("./history.js");
|
|
17
|
+
const ai_analyzer_js_1 = require("./ai-analyzer.js");
|
|
18
|
+
class CraftCloseSkill {
|
|
19
|
+
config;
|
|
20
|
+
monitoringInterval = null;
|
|
21
|
+
restartCounts = new Map();
|
|
22
|
+
// Alert channels
|
|
23
|
+
telegramAlert = null;
|
|
24
|
+
discordAlert = null;
|
|
25
|
+
// State tracking
|
|
26
|
+
serverStates = new Map();
|
|
27
|
+
activeIncidents = new Map();
|
|
28
|
+
// History
|
|
29
|
+
history;
|
|
30
|
+
// AI analysis
|
|
31
|
+
aiAnalyzer = null;
|
|
32
|
+
// Last check results cache
|
|
33
|
+
lastResults = [];
|
|
34
|
+
constructor(config, dbPath) {
|
|
35
|
+
this.config = config;
|
|
36
|
+
this.history = new history_js_1.HistoryTracker(dbPath ?? "./craftclose.db");
|
|
37
|
+
if (config.ai?.gemini_key) {
|
|
38
|
+
this.aiAnalyzer = new ai_analyzer_js_1.AIAnalyzer({
|
|
39
|
+
geminiKey: config.ai.gemini_key,
|
|
40
|
+
model: config.ai.model,
|
|
41
|
+
maxCallsPerHour: config.ai.max_calls_per_hour,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (config.alerts?.telegram) {
|
|
45
|
+
this.telegramAlert = new telegram_js_1.TelegramAlert(config.alerts.telegram);
|
|
46
|
+
}
|
|
47
|
+
if (config.alerts?.discord) {
|
|
48
|
+
this.discordAlert = new discord_js_1.DiscordAlert({ webhook_url: config.alerts.discord.webhook_url });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Start monitoring all configured servers.
|
|
53
|
+
*/
|
|
54
|
+
async start() {
|
|
55
|
+
const interval = (this.config.monitoring?.interval ?? 60) * 1000;
|
|
56
|
+
console.log(`[CraftClose] Starting monitoring for ${this.config.servers.length} server(s)`);
|
|
57
|
+
console.log(`[CraftClose] Check interval: ${interval / 1000}s`);
|
|
58
|
+
console.log(`[CraftClose] Auto-restart: ${this.config.monitoring?.auto_restart !== false}`);
|
|
59
|
+
// Initial check
|
|
60
|
+
await this.checkAllServers();
|
|
61
|
+
// Start monitoring loop
|
|
62
|
+
this.monitoringInterval = setInterval(() => {
|
|
63
|
+
this.checkAllServers().catch((err) => {
|
|
64
|
+
console.error("[CraftClose] Monitoring cycle error:", err);
|
|
65
|
+
});
|
|
66
|
+
}, interval);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Stop monitoring.
|
|
70
|
+
*/
|
|
71
|
+
stop() {
|
|
72
|
+
if (this.monitoringInterval) {
|
|
73
|
+
clearInterval(this.monitoringInterval);
|
|
74
|
+
this.monitoringInterval = null;
|
|
75
|
+
console.log("[CraftClose] Monitoring stopped");
|
|
76
|
+
}
|
|
77
|
+
this.history.prune();
|
|
78
|
+
this.history.close();
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get a formatted status string for all servers (for chat/CLI display).
|
|
82
|
+
*/
|
|
83
|
+
async getStatus() {
|
|
84
|
+
const results = await this.checkAllServers();
|
|
85
|
+
const lines = ["**CraftClose Status**", ""];
|
|
86
|
+
for (const r of results) {
|
|
87
|
+
const icon = r.online ? "🟢" : "🔴";
|
|
88
|
+
const tps = r.tps === undefined ? "" : ` | TPS: ${r.tps.toFixed(1)}`;
|
|
89
|
+
const players = r.players ? ` | Players: ${r.players.online}/${r.players.max}` : "";
|
|
90
|
+
const cpu = r.cpu === undefined ? "" : ` | CPU: ${r.cpu.toFixed(0)}%`;
|
|
91
|
+
lines.push(`${icon} **${r.server}**${tps}${players}${cpu}`);
|
|
92
|
+
}
|
|
93
|
+
return lines.join("\n");
|
|
94
|
+
}
|
|
95
|
+
/** Get the most recent health check results (from memory, not a new check) */
|
|
96
|
+
getLastResults() {
|
|
97
|
+
return [...this.lastResults];
|
|
98
|
+
}
|
|
99
|
+
/** Get the config (without sensitive fields — callers handle filtering) */
|
|
100
|
+
getConfig() {
|
|
101
|
+
return this.config;
|
|
102
|
+
}
|
|
103
|
+
/** Get access to the history tracker for querying */
|
|
104
|
+
getHistoryTracker() {
|
|
105
|
+
return this.history;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Run a single health check across all servers.
|
|
109
|
+
*/
|
|
110
|
+
async checkAllServers() {
|
|
111
|
+
const results = [];
|
|
112
|
+
for (const server of this.config.servers) {
|
|
113
|
+
try {
|
|
114
|
+
const result = await this.checkServer(server.name);
|
|
115
|
+
results.push(result);
|
|
116
|
+
this.history.record(result);
|
|
117
|
+
await this.processCheckResult(server.name, result);
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
console.error(`[CraftClose] Error checking ${server.name}:`, err);
|
|
121
|
+
const errorResult = {
|
|
122
|
+
server: server.name,
|
|
123
|
+
timestamp: new Date(),
|
|
124
|
+
online: false,
|
|
125
|
+
error: err instanceof Error ? err.message : String(err),
|
|
126
|
+
};
|
|
127
|
+
results.push(errorResult);
|
|
128
|
+
this.history.record(errorResult);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
this.lastResults = results;
|
|
132
|
+
return results;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Process a health check result: recovery detection, crash handling, TPS warnings.
|
|
136
|
+
*/
|
|
137
|
+
async processCheckResult(serverName, result) {
|
|
138
|
+
const wasOnline = this.serverStates.get(serverName);
|
|
139
|
+
this.serverStates.set(serverName, result.online);
|
|
140
|
+
// Recovery detection: offline → online
|
|
141
|
+
if (result.online && wasOnline === false) {
|
|
142
|
+
this.activeIncidents.delete(serverName);
|
|
143
|
+
const playerInfo = result.players ? ` Players: ${result.players.online}/${result.players.max}` : "";
|
|
144
|
+
await this.sendAlert({
|
|
145
|
+
server: serverName,
|
|
146
|
+
event: "recovery",
|
|
147
|
+
title: `🟢 ${serverName} is BACK ONLINE`,
|
|
148
|
+
description: `Server recovered.${playerInfo}`,
|
|
149
|
+
severity: "recovery",
|
|
150
|
+
timestamp: new Date(),
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
if (!result.online) {
|
|
154
|
+
await this.handleServerDown(serverName, result);
|
|
155
|
+
}
|
|
156
|
+
// TPS warning detection (only if online)
|
|
157
|
+
if (result.online && result.tps !== undefined && result.tps < 10) {
|
|
158
|
+
await this.handleTpsWarning(serverName, result);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Attempt SSH health check, merging results into the given HealthCheckResult.
|
|
163
|
+
* Returns the SshConnector for cleanup.
|
|
164
|
+
*/
|
|
165
|
+
async trySshHealth(serverName, server, result) {
|
|
166
|
+
if (!server.ssh)
|
|
167
|
+
return null;
|
|
168
|
+
const connector = new ssh_js_1.SshConnector({
|
|
169
|
+
serverName,
|
|
170
|
+
host: server.host,
|
|
171
|
+
config: server.ssh,
|
|
172
|
+
logPath: server.log_path,
|
|
173
|
+
});
|
|
174
|
+
try {
|
|
175
|
+
const health = await connector.getHealth();
|
|
176
|
+
result.cpu = health.cpu;
|
|
177
|
+
result.ram = health.ram;
|
|
178
|
+
result.disk = health.disk;
|
|
179
|
+
result.uptime = health.uptime;
|
|
180
|
+
if (health.online)
|
|
181
|
+
result.online = true;
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
185
|
+
console.error(`[CraftClose] SSH health check failed for ${serverName}: ${message}`);
|
|
186
|
+
result.error = `SSH: ${message}`;
|
|
187
|
+
}
|
|
188
|
+
return connector;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Attempt RCON health check, merging results into the given HealthCheckResult.
|
|
192
|
+
* Returns the RconConnector for cleanup.
|
|
193
|
+
* If RCON host is localhost and SSH is available, tunnels through SSH.
|
|
194
|
+
*/
|
|
195
|
+
async tryRconHealth(serverName, server, result, sshConnector) {
|
|
196
|
+
if (!server.rcon)
|
|
197
|
+
return { rcon: null };
|
|
198
|
+
let rconHost = server.rcon.host ?? server.host;
|
|
199
|
+
let rconPort = server.rcon.port;
|
|
200
|
+
let closeTunnel;
|
|
201
|
+
// SSH tunnel for localhost RCON
|
|
202
|
+
if (sshConnector && (server.rcon.host === "127.0.0.1" || server.rcon.host === "localhost")) {
|
|
203
|
+
try {
|
|
204
|
+
const tunnel = await sshConnector.createTunnel("127.0.0.1", server.rcon.port);
|
|
205
|
+
rconHost = "127.0.0.1";
|
|
206
|
+
rconPort = tunnel.localPort;
|
|
207
|
+
closeTunnel = tunnel.close;
|
|
208
|
+
}
|
|
209
|
+
catch (err) {
|
|
210
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
211
|
+
console.error(`[CraftClose] SSH tunnel failed for ${serverName}: ${message}`);
|
|
212
|
+
// Fall through to direct RCON attempt
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
const connector = new rcon_js_1.RconConnector({
|
|
216
|
+
serverName,
|
|
217
|
+
host: server.host,
|
|
218
|
+
config: { ...server.rcon, host: rconHost, port: rconPort },
|
|
219
|
+
});
|
|
220
|
+
try {
|
|
221
|
+
const [tps, players, version] = await Promise.all([
|
|
222
|
+
connector.getTps(),
|
|
223
|
+
connector.getPlayers(),
|
|
224
|
+
connector.getVersion(),
|
|
225
|
+
]);
|
|
226
|
+
if (tps !== null)
|
|
227
|
+
result.tps = tps;
|
|
228
|
+
result.players = players;
|
|
229
|
+
result.version = version;
|
|
230
|
+
result.online = true;
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
234
|
+
console.error(`[CraftClose] RCON health check failed for ${serverName}: ${message}`);
|
|
235
|
+
const prefix = result.error ? `${result.error}; ` : "";
|
|
236
|
+
result.error = `${prefix}RCON: ${message}`;
|
|
237
|
+
}
|
|
238
|
+
return { rcon: connector, closeTunnel };
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Attempt Pterodactyl health check, merging results into the given HealthCheckResult.
|
|
242
|
+
*/
|
|
243
|
+
async tryPteroHealth(serverName, server, result) {
|
|
244
|
+
if (!server.pterodactyl)
|
|
245
|
+
return null;
|
|
246
|
+
const connector = new pterodactyl_js_1.PterodactylConnector({
|
|
247
|
+
serverName,
|
|
248
|
+
config: server.pterodactyl,
|
|
249
|
+
});
|
|
250
|
+
try {
|
|
251
|
+
const health = await connector.getHealth();
|
|
252
|
+
if (health.online !== undefined)
|
|
253
|
+
result.online = health.online;
|
|
254
|
+
if (health.cpu !== undefined)
|
|
255
|
+
result.cpu = health.cpu;
|
|
256
|
+
if (health.ram)
|
|
257
|
+
result.ram = health.ram;
|
|
258
|
+
if (health.disk)
|
|
259
|
+
result.disk = health.disk;
|
|
260
|
+
if (health.uptime)
|
|
261
|
+
result.uptime = health.uptime;
|
|
262
|
+
}
|
|
263
|
+
catch (err) {
|
|
264
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
265
|
+
console.error(`[CraftClose] Pterodactyl health check failed for ${serverName}: ${message}`);
|
|
266
|
+
result.error = `Pterodactyl: ${message}`;
|
|
267
|
+
}
|
|
268
|
+
return connector;
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Attempt Docker exec fallback for TPS/players when no RCON is configured.
|
|
272
|
+
*/
|
|
273
|
+
async tryDockerRcon(server, result, sshConnector) {
|
|
274
|
+
try {
|
|
275
|
+
const tpsResponse = await sshConnector.dockerRcon(server.name, "tps");
|
|
276
|
+
if (tpsResponse) {
|
|
277
|
+
const tps = (0, parsers_js_1.parseTpsResponse)(tpsResponse);
|
|
278
|
+
if (tps !== null)
|
|
279
|
+
result.tps = tps;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
// Docker RCON TPS not available — silently skip
|
|
284
|
+
}
|
|
285
|
+
try {
|
|
286
|
+
const listResponse = await sshConnector.dockerRcon(server.name, "list");
|
|
287
|
+
if (listResponse) {
|
|
288
|
+
const players = (0, parsers_js_1.parsePlayersResponse)(listResponse);
|
|
289
|
+
if (players)
|
|
290
|
+
result.players = players;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
// Docker RCON list not available — silently skip
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Check a single server's health.
|
|
299
|
+
*/
|
|
300
|
+
async checkServer(serverName) {
|
|
301
|
+
const server = this.config.servers.find(s => s.name === serverName);
|
|
302
|
+
if (!server) {
|
|
303
|
+
throw new Error(`Server '${serverName}' not found in config`);
|
|
304
|
+
}
|
|
305
|
+
if (!server.ssh && !server.rcon && !server.pterodactyl) {
|
|
306
|
+
throw new Error(`Server '${serverName}' has no connector configured (SSH, RCON, or Pterodactyl required).`);
|
|
307
|
+
}
|
|
308
|
+
const result = {
|
|
309
|
+
server: serverName,
|
|
310
|
+
timestamp: new Date(),
|
|
311
|
+
online: false,
|
|
312
|
+
};
|
|
313
|
+
let sshConnector = null;
|
|
314
|
+
let rconConnector = null;
|
|
315
|
+
let closeTunnel;
|
|
316
|
+
try {
|
|
317
|
+
// Pterodactyl: resource monitoring (CPU/RAM/disk/status)
|
|
318
|
+
await this.tryPteroHealth(serverName, server, result);
|
|
319
|
+
sshConnector = await this.trySshHealth(serverName, server, result);
|
|
320
|
+
const rconResult = await this.tryRconHealth(serverName, server, result, sshConnector);
|
|
321
|
+
rconConnector = rconResult.rcon;
|
|
322
|
+
closeTunnel = rconResult.closeTunnel;
|
|
323
|
+
// Docker exec fallback: if no RCON config, SSH is available, and server is online
|
|
324
|
+
if (!server.rcon && sshConnector && result.online) {
|
|
325
|
+
await this.tryDockerRcon(server, result, sshConnector);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
finally {
|
|
329
|
+
closeTunnel?.();
|
|
330
|
+
await Promise.allSettled([
|
|
331
|
+
sshConnector?.disconnect(),
|
|
332
|
+
rconConnector?.disconnect(),
|
|
333
|
+
]);
|
|
334
|
+
}
|
|
335
|
+
return result;
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Handle a server that's detected as down.
|
|
339
|
+
* Includes crash detection, pattern matching, dedup, restart, and alerting.
|
|
340
|
+
*/
|
|
341
|
+
async handleServerDown(serverName, result) {
|
|
342
|
+
const now = new Date();
|
|
343
|
+
const crash = {
|
|
344
|
+
server: serverName,
|
|
345
|
+
timestamp: now,
|
|
346
|
+
type: this.classifyCrashType(result),
|
|
347
|
+
severity: "critical",
|
|
348
|
+
};
|
|
349
|
+
// Step 1: Tail log and run pattern matching (best-effort)
|
|
350
|
+
await this.enrichCrashWithPatterns(serverName, crash);
|
|
351
|
+
// Step 1.5: AI analysis (best-effort, non-blocking)
|
|
352
|
+
if (this.aiAnalyzer && crash.raw_log) {
|
|
353
|
+
try {
|
|
354
|
+
const server = this.config.servers.find(s => s.name === serverName);
|
|
355
|
+
const analysis = await this.aiAnalyzer.analyzeCrash({
|
|
356
|
+
log: crash.raw_log,
|
|
357
|
+
context: {
|
|
358
|
+
version: result.version,
|
|
359
|
+
plugins: result.players?.list,
|
|
360
|
+
ram_mb: result.ram?.total,
|
|
361
|
+
player_count: result.players?.online,
|
|
362
|
+
server_type: server?.type,
|
|
363
|
+
},
|
|
364
|
+
});
|
|
365
|
+
if (analysis) {
|
|
366
|
+
crash.ai_analysis = analysis;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
catch (err) {
|
|
370
|
+
console.error(`[CraftClose] AI analysis failed (non-fatal): ${err}`);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
// Step 2: Auto-restart if enabled and circuit breaker allows
|
|
374
|
+
if (this.config.monitoring?.auto_restart !== false && this.canRestart(serverName)) {
|
|
375
|
+
await this.restartServer(serverName);
|
|
376
|
+
}
|
|
377
|
+
// Step 3: Record crash
|
|
378
|
+
this.history.recordCrash(crash);
|
|
379
|
+
// Step 4: Alert deduplication
|
|
380
|
+
const incident = this.activeIncidents.get(serverName);
|
|
381
|
+
if (incident) {
|
|
382
|
+
// Already in an incident — only send follow-up every 5 minutes
|
|
383
|
+
if (now.getTime() - incident.lastAlert.getTime() < 5 * 60 * 1000) {
|
|
384
|
+
console.log(`[CraftClose] Suppressing duplicate alert for ${serverName} (incident ongoing)`);
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
incident.lastAlert = now;
|
|
388
|
+
incident.alertCount++;
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
this.activeIncidents.set(serverName, { startTime: now, lastAlert: now, alertCount: 1 });
|
|
392
|
+
}
|
|
393
|
+
// Step 5: Send alert with pattern info
|
|
394
|
+
const description = crash.pattern_match?.cause
|
|
395
|
+
?? crash.ai_analysis?.root_cause
|
|
396
|
+
?? "Server process is not responding.";
|
|
397
|
+
await this.sendAlert({
|
|
398
|
+
server: serverName,
|
|
399
|
+
event: "crash",
|
|
400
|
+
title: `🔴 ${serverName} is DOWN`,
|
|
401
|
+
description,
|
|
402
|
+
severity: "critical",
|
|
403
|
+
timestamp: now,
|
|
404
|
+
ai_analysis: crash.ai_analysis,
|
|
405
|
+
pattern_match: crash.pattern_match,
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Classify crash type based on available data.
|
|
410
|
+
*/
|
|
411
|
+
classifyCrashType(result) {
|
|
412
|
+
if (result.error?.includes("timed out") || result.error?.includes("ECONNREFUSED")) {
|
|
413
|
+
return "timeout";
|
|
414
|
+
}
|
|
415
|
+
if (result.tps !== undefined && result.tps === 0) {
|
|
416
|
+
return "tps_zero";
|
|
417
|
+
}
|
|
418
|
+
return "process_dead";
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Enrich a crash event with log tail pattern matching (best-effort).
|
|
422
|
+
* Tries Pterodactyl file read first, then SSH tail as fallback.
|
|
423
|
+
*/
|
|
424
|
+
async enrichCrashWithPatterns(serverName, crash) {
|
|
425
|
+
const server = this.config.servers.find(s => s.name === serverName);
|
|
426
|
+
const logText = await this.fetchCrashLog(serverName, server ?? undefined);
|
|
427
|
+
if (!logText)
|
|
428
|
+
return;
|
|
429
|
+
crash.raw_log = logText;
|
|
430
|
+
if (/OutOfMemoryError/i.test(logText)) {
|
|
431
|
+
crash.type = "oom";
|
|
432
|
+
}
|
|
433
|
+
const pattern = (0, index_js_1.matchPattern)(logText);
|
|
434
|
+
if (pattern) {
|
|
435
|
+
crash.pattern_match = {
|
|
436
|
+
pattern_id: pattern.id,
|
|
437
|
+
category: pattern.category,
|
|
438
|
+
title: pattern.title,
|
|
439
|
+
cause: pattern.cause,
|
|
440
|
+
fix: pattern.fix,
|
|
441
|
+
confidence: pattern.confidence,
|
|
442
|
+
};
|
|
443
|
+
if (pattern.category === "memory") {
|
|
444
|
+
crash.type = "oom";
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Attempt to fetch recent log text via Pterodactyl or SSH.
|
|
450
|
+
*/
|
|
451
|
+
async fetchCrashLog(serverName, server) {
|
|
452
|
+
// Try Pterodactyl file read first
|
|
453
|
+
if (server?.pterodactyl) {
|
|
454
|
+
try {
|
|
455
|
+
const ptero = new pterodactyl_js_1.PterodactylConnector({ serverName, config: server.pterodactyl });
|
|
456
|
+
const fullLog = await ptero.readFile("/logs/latest.log");
|
|
457
|
+
const lines = fullLog.split("\n");
|
|
458
|
+
return lines.slice(-200).join("\n");
|
|
459
|
+
}
|
|
460
|
+
catch (err) {
|
|
461
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
462
|
+
console.error(`[CraftClose] Pterodactyl log read failed for ${serverName}: ${msg}`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
// Fallback: SSH tail
|
|
466
|
+
if (server?.ssh) {
|
|
467
|
+
const ssh = new ssh_js_1.SshConnector({
|
|
468
|
+
serverName,
|
|
469
|
+
host: server.host,
|
|
470
|
+
config: server.ssh,
|
|
471
|
+
logPath: server.log_path,
|
|
472
|
+
});
|
|
473
|
+
try {
|
|
474
|
+
return await ssh.tailLog(200);
|
|
475
|
+
}
|
|
476
|
+
catch (err) {
|
|
477
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
478
|
+
console.error(`[CraftClose] SSH log tail failed for ${serverName}: ${message}`);
|
|
479
|
+
}
|
|
480
|
+
finally {
|
|
481
|
+
await ssh.disconnect();
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return null;
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Handle low TPS warning with deduplication.
|
|
488
|
+
*/
|
|
489
|
+
async handleTpsWarning(serverName, result) {
|
|
490
|
+
const incidentKey = `${serverName}:tps_warning`;
|
|
491
|
+
const now = new Date();
|
|
492
|
+
const incident = this.activeIncidents.get(incidentKey);
|
|
493
|
+
if (incident) {
|
|
494
|
+
if (now.getTime() - incident.lastAlert.getTime() < 5 * 60 * 1000) {
|
|
495
|
+
return; // Suppress duplicate warning
|
|
496
|
+
}
|
|
497
|
+
incident.lastAlert = now;
|
|
498
|
+
incident.alertCount++;
|
|
499
|
+
}
|
|
500
|
+
else {
|
|
501
|
+
this.activeIncidents.set(incidentKey, { startTime: now, lastAlert: now, alertCount: 1 });
|
|
502
|
+
}
|
|
503
|
+
const tpsStr = result.tps === undefined ? "N/A" : result.tps.toFixed(1);
|
|
504
|
+
const playerInfo = result.players ? ` Players online: ${result.players.online}` : "";
|
|
505
|
+
await this.sendAlert({
|
|
506
|
+
server: serverName,
|
|
507
|
+
event: "warning",
|
|
508
|
+
title: `🟡 ${serverName} — Low TPS: ${tpsStr}`,
|
|
509
|
+
description: `Server TPS dropped to ${tpsStr}/20.0. Performance degraded.${playerInfo}`,
|
|
510
|
+
severity: "warning",
|
|
511
|
+
timestamp: now,
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
/**
|
|
515
|
+
* Circuit breaker: check if we're allowed to restart.
|
|
516
|
+
* Max 3 restarts per 10-minute window.
|
|
517
|
+
*/
|
|
518
|
+
canRestart(serverName) {
|
|
519
|
+
const maxRestarts = this.config.monitoring?.max_restarts ?? 3;
|
|
520
|
+
const windowMs = 10 * 60 * 1000; // 10 minutes
|
|
521
|
+
const now = new Date();
|
|
522
|
+
const entry = this.restartCounts.get(serverName);
|
|
523
|
+
if (!entry || now.getTime() - entry.windowStart.getTime() > windowMs) {
|
|
524
|
+
this.restartCounts.set(serverName, { count: 1, windowStart: now });
|
|
525
|
+
return true;
|
|
526
|
+
}
|
|
527
|
+
if (entry.count >= maxRestarts) {
|
|
528
|
+
console.warn(`[CraftClose] Circuit breaker: ${serverName} hit ${maxRestarts} restarts in 10 min. Stopping auto-restart.`);
|
|
529
|
+
return false;
|
|
530
|
+
}
|
|
531
|
+
entry.count++;
|
|
532
|
+
return true;
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Restart a server via Pterodactyl power action or SSH restart command.
|
|
536
|
+
*/
|
|
537
|
+
async restartServer(serverName) {
|
|
538
|
+
const server = this.config.servers.find(s => s.name === serverName);
|
|
539
|
+
if (!server)
|
|
540
|
+
return;
|
|
541
|
+
// Prefer Pterodactyl power action if configured
|
|
542
|
+
if (server.pterodactyl) {
|
|
543
|
+
console.log(`[CraftClose] Restarting ${serverName} via Pterodactyl...`);
|
|
544
|
+
try {
|
|
545
|
+
const ptero = new pterodactyl_js_1.PterodactylConnector({ serverName, config: server.pterodactyl });
|
|
546
|
+
await ptero.power("restart");
|
|
547
|
+
console.log(`[CraftClose] Pterodactyl restart sent for ${serverName}`);
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
catch (err) {
|
|
551
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
552
|
+
console.error(`[CraftClose] Pterodactyl restart failed for ${serverName}: ${msg}`);
|
|
553
|
+
// Fall through to SSH restart if available
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
// Fallback: SSH + restart_command
|
|
557
|
+
if (server.ssh && server.restart_command) {
|
|
558
|
+
console.log(`[CraftClose] Restarting ${serverName} via SSH...`);
|
|
559
|
+
const ssh = new ssh_js_1.SshConnector({
|
|
560
|
+
serverName,
|
|
561
|
+
host: server.host,
|
|
562
|
+
config: server.ssh,
|
|
563
|
+
});
|
|
564
|
+
try {
|
|
565
|
+
const result = await ssh.exec(server.restart_command);
|
|
566
|
+
if (result.code === 0) {
|
|
567
|
+
console.log(`[CraftClose] Restart command succeeded for ${serverName}`);
|
|
568
|
+
}
|
|
569
|
+
else {
|
|
570
|
+
console.error(`[CraftClose] Restart command failed for ${serverName}: ${result.stderr}`);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
finally {
|
|
574
|
+
await ssh.disconnect();
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
else if (!server.pterodactyl) {
|
|
578
|
+
console.log(`[CraftClose] Cannot restart ${serverName}: no restart method available`);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Send an alert to configured channels (Telegram, Discord).
|
|
583
|
+
* Alert failures are caught and logged — they never stop monitoring.
|
|
584
|
+
*/
|
|
585
|
+
async sendAlert(payload) {
|
|
586
|
+
console.log(`[CraftClose] Alert: ${payload.title} — ${payload.description}`);
|
|
587
|
+
const promises = [];
|
|
588
|
+
if (this.telegramAlert) {
|
|
589
|
+
promises.push(this.telegramAlert.send(payload).catch(err => {
|
|
590
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
591
|
+
console.error(`[CraftClose] Telegram alert failed: ${msg}`);
|
|
592
|
+
}));
|
|
593
|
+
}
|
|
594
|
+
if (this.discordAlert) {
|
|
595
|
+
promises.push(this.discordAlert.send(payload).catch(err => {
|
|
596
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
597
|
+
console.error(`[CraftClose] Discord alert failed: ${msg}`);
|
|
598
|
+
}));
|
|
599
|
+
}
|
|
600
|
+
await Promise.allSettled(promises);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
exports.CraftCloseSkill = CraftCloseSkill;
|
|
604
|
+
//# sourceMappingURL=craft-close-skill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"craft-close-skill.js","sourceRoot":"","sources":["../src/craft-close-skill.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* History Tracker — Store TPS and player count history in SQLite.
|
|
3
|
+
*
|
|
4
|
+
* Uses better-sqlite3 with graceful fallback — if the native module
|
|
5
|
+
* fails to load, history tracking is disabled but monitoring continues.
|
|
6
|
+
*/
|
|
7
|
+
import type { HealthCheckResult, CrashEvent } from "./types.js";
|
|
8
|
+
export declare class HistoryTracker {
|
|
9
|
+
private db;
|
|
10
|
+
constructor(dbPath: string);
|
|
11
|
+
private initialize;
|
|
12
|
+
private migrate;
|
|
13
|
+
/** Whether the database is available for tracking */
|
|
14
|
+
get available(): boolean;
|
|
15
|
+
/** Record a health check result */
|
|
16
|
+
record(result: HealthCheckResult): void;
|
|
17
|
+
/** Record a crash event */
|
|
18
|
+
recordCrash(event: CrashEvent): void;
|
|
19
|
+
/** Get recent health history for a server */
|
|
20
|
+
getHistory(server: string, limit?: number): HealthCheckResult[];
|
|
21
|
+
private mapRowToResult;
|
|
22
|
+
/** Get crash history for a server (or all servers) */
|
|
23
|
+
getCrashes(server?: string, limit?: number): CrashEvent[];
|
|
24
|
+
private mapRowToCrash;
|
|
25
|
+
/** Get average TPS over last N checks */
|
|
26
|
+
getAverageTps(server: string, checks?: number): number | null;
|
|
27
|
+
/** Prune old records (keep last N days) */
|
|
28
|
+
prune(days?: number): void;
|
|
29
|
+
/** Close the database connection */
|
|
30
|
+
close(): void;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=history.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAiChE,qBAAa,cAAc;IACzB,OAAO,CAAC,EAAE,CAAuC;gBAErC,MAAM,EAAE,MAAM;IAc1B,OAAO,CAAC,UAAU;IA2ClB,OAAO,CAAC,OAAO;IAUf,qDAAqD;IACrD,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,mCAAmC;IACnC,MAAM,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI;IAyBvC,2BAA2B;IAC3B,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAsBpC,6CAA6C;IAC7C,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,iBAAiB,EAAE;IAUnE,OAAO,CAAC,cAAc;IAwBtB,sDAAsD;IACtD,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,UAAU,EAAE;IAa7D,OAAO,CAAC,aAAa;IAgCrB,yCAAyC;IACzC,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,MAAW,GAAG,MAAM,GAAG,IAAI;IAUjE,2CAA2C;IAC3C,KAAK,CAAC,IAAI,GAAE,MAAU,GAAG,IAAI;IAQ7B,oCAAoC;IACpC,KAAK,IAAI,IAAI;CAId"}
|