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,273 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SSH Connector — Connect to Minecraft servers via SSH
|
|
4
|
+
*
|
|
5
|
+
* Handles health checks (CPU, RAM, disk), log tailing, and command execution.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.SshConnector = void 0;
|
|
9
|
+
const ssh2_1 = require("ssh2");
|
|
10
|
+
const node_fs_1 = require("node:fs");
|
|
11
|
+
const node_os_1 = require("node:os");
|
|
12
|
+
/** Resolve ~ to actual home directory in a path */
|
|
13
|
+
function resolvePath(p) {
|
|
14
|
+
if (p.startsWith("~/") || p === "~") {
|
|
15
|
+
return p.replace("~", (0, node_os_1.homedir)());
|
|
16
|
+
}
|
|
17
|
+
return p;
|
|
18
|
+
}
|
|
19
|
+
/** Parse "used total percent" output into {used, total, percent} or null */
|
|
20
|
+
function parseThreePartMetric(stdout) {
|
|
21
|
+
const parts = stdout.split(" ");
|
|
22
|
+
if (parts.length !== 3)
|
|
23
|
+
return null;
|
|
24
|
+
const used = Number.parseInt(parts[0], 10);
|
|
25
|
+
const total = Number.parseInt(parts[1], 10);
|
|
26
|
+
const percent = Number.parseFloat(parts[2]);
|
|
27
|
+
if (Number.isNaN(used) || Number.isNaN(total) || Number.isNaN(percent))
|
|
28
|
+
return null;
|
|
29
|
+
return { used, total, percent };
|
|
30
|
+
}
|
|
31
|
+
class SshConnector {
|
|
32
|
+
options;
|
|
33
|
+
conn = null;
|
|
34
|
+
connected = false;
|
|
35
|
+
constructor(options) {
|
|
36
|
+
this.options = options;
|
|
37
|
+
}
|
|
38
|
+
/** Ensure the SSH client is connected. Reuses existing connection. */
|
|
39
|
+
async ensureConnected() {
|
|
40
|
+
if (this.conn && this.connected) {
|
|
41
|
+
return this.conn;
|
|
42
|
+
}
|
|
43
|
+
const { host, config, timeout } = this.options;
|
|
44
|
+
const conn = new ssh2_1.Client();
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
const timer = setTimeout(() => {
|
|
47
|
+
conn.destroy();
|
|
48
|
+
reject(new Error(`SSH connection to ${host} timed out after ${timeout ?? 10000}ms`));
|
|
49
|
+
}, timeout ?? 10000);
|
|
50
|
+
conn.on("ready", () => {
|
|
51
|
+
clearTimeout(timer);
|
|
52
|
+
this.conn = conn;
|
|
53
|
+
this.connected = true;
|
|
54
|
+
resolve(conn);
|
|
55
|
+
});
|
|
56
|
+
conn.on("error", (err) => {
|
|
57
|
+
clearTimeout(timer);
|
|
58
|
+
this.connected = false;
|
|
59
|
+
reject(new Error(`SSH connection to ${host} failed: ${err.message}`));
|
|
60
|
+
});
|
|
61
|
+
conn.on("close", () => {
|
|
62
|
+
this.connected = false;
|
|
63
|
+
this.conn = null;
|
|
64
|
+
});
|
|
65
|
+
let privateKey;
|
|
66
|
+
if (config.key) {
|
|
67
|
+
try {
|
|
68
|
+
privateKey = (0, node_fs_1.readFileSync)(resolvePath(config.key));
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Key file not found — fall through to password auth
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
conn.connect({
|
|
75
|
+
host: config.host ?? host,
|
|
76
|
+
port: config.port ?? 22,
|
|
77
|
+
username: config.user,
|
|
78
|
+
privateKey,
|
|
79
|
+
password: config.password,
|
|
80
|
+
readyTimeout: timeout ?? 10000,
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Execute a single command over SSH with a per-command timeout.
|
|
86
|
+
*/
|
|
87
|
+
async execCommand(command, cmdTimeout) {
|
|
88
|
+
const conn = await this.ensureConnected();
|
|
89
|
+
const effectiveTimeout = cmdTimeout ?? this.options.timeout ?? 10000;
|
|
90
|
+
return new Promise((resolve, reject) => {
|
|
91
|
+
const timer = setTimeout(() => {
|
|
92
|
+
reject(new Error(`SSH command timed out after ${effectiveTimeout}ms: ${command}`));
|
|
93
|
+
}, effectiveTimeout);
|
|
94
|
+
conn.exec(command, (err, stream) => {
|
|
95
|
+
if (err) {
|
|
96
|
+
clearTimeout(timer);
|
|
97
|
+
reject(new Error(`SSH exec error: ${err.message}`));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
let stdout = "";
|
|
101
|
+
let stderr = "";
|
|
102
|
+
stream.on("data", (data) => {
|
|
103
|
+
stdout += data.toString();
|
|
104
|
+
});
|
|
105
|
+
stream.stderr.on("data", (data) => {
|
|
106
|
+
stderr += data.toString();
|
|
107
|
+
});
|
|
108
|
+
stream.on("close", (code) => {
|
|
109
|
+
clearTimeout(timer);
|
|
110
|
+
resolve({ stdout: stdout.trim(), stderr: stderr.trim(), code: code ?? 0 });
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Test SSH connectivity.
|
|
117
|
+
*/
|
|
118
|
+
async testConnection() {
|
|
119
|
+
try {
|
|
120
|
+
const result = await this.execCommand("echo ok");
|
|
121
|
+
return result.stdout === "ok";
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Get server health metrics via SSH.
|
|
129
|
+
*/
|
|
130
|
+
async getHealth() {
|
|
131
|
+
const result = {
|
|
132
|
+
server: this.options.serverName,
|
|
133
|
+
timestamp: new Date(),
|
|
134
|
+
};
|
|
135
|
+
// Check for the server by container/server name first, then fall back to generic MC process detection
|
|
136
|
+
const serverName = this.options.serverName;
|
|
137
|
+
const processCmd = `docker ps --format '{{.Names}} {{.Status}}' 2>/dev/null | grep -i '${serverName}' || docker ps --format '{{.Names}} {{.Status}}' 2>/dev/null | grep -iE 'paper|minecraft|spigot' || pgrep -fa 'java.*minecraft\\|java.*paper\\|java.*spigot'`;
|
|
138
|
+
// Run all health commands in parallel
|
|
139
|
+
const [cpuResult, ramResult, diskResult, processResult, uptimeResult] = await Promise.allSettled([
|
|
140
|
+
this.execCommand("top -bn1 | grep 'Cpu(s)' | awk '{print $2}'"),
|
|
141
|
+
this.execCommand("free -m | awk '/Mem:/ {printf \"%d %d %.1f\", $3, $2, $3/$2*100}'"),
|
|
142
|
+
this.execCommand("df -m / | awk 'NR==2 {printf \"%d %d %.1f\", $3, $2, $5}'"),
|
|
143
|
+
this.execCommand(processCmd),
|
|
144
|
+
this.execCommand("uptime -p"),
|
|
145
|
+
]);
|
|
146
|
+
// Parse CPU
|
|
147
|
+
if (cpuResult.status === "fulfilled" && cpuResult.value.stdout) {
|
|
148
|
+
const cpu = Number.parseFloat(cpuResult.value.stdout.replace(",", "."));
|
|
149
|
+
if (!Number.isNaN(cpu)) {
|
|
150
|
+
result.cpu = cpu;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Parse RAM
|
|
154
|
+
if (ramResult.status === "fulfilled" && ramResult.value.stdout) {
|
|
155
|
+
result.ram = parseThreePartMetric(ramResult.value.stdout) ?? undefined;
|
|
156
|
+
}
|
|
157
|
+
// Parse Disk
|
|
158
|
+
if (diskResult.status === "fulfilled" && diskResult.value.stdout) {
|
|
159
|
+
result.disk = parseThreePartMetric(diskResult.value.stdout) ?? undefined;
|
|
160
|
+
}
|
|
161
|
+
// Parse process check — if we got output, the process is running
|
|
162
|
+
result.online = processResult.status === "fulfilled" && processResult.value.code === 0 && processResult.value.stdout.length > 0;
|
|
163
|
+
// Parse uptime
|
|
164
|
+
if (uptimeResult.status === "fulfilled" && uptimeResult.value.stdout) {
|
|
165
|
+
result.uptime = uptimeResult.value.stdout.replace(/^up\s+/, "");
|
|
166
|
+
}
|
|
167
|
+
return result;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Read the last N lines of the server log.
|
|
171
|
+
*/
|
|
172
|
+
async tailLog(lines = 200) {
|
|
173
|
+
// If a log path is configured, use it directly
|
|
174
|
+
if (this.options.logPath) {
|
|
175
|
+
const result = await this.execCommand(`tail -n ${lines} ${this.options.logPath}`);
|
|
176
|
+
if (result.code === 0) {
|
|
177
|
+
return result.stdout;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// Try Docker logs first (blueth-stable container)
|
|
181
|
+
const dockerContainers = ["blueth-stable", "minecraft", "paper", "mc"];
|
|
182
|
+
for (const container of dockerContainers) {
|
|
183
|
+
try {
|
|
184
|
+
const result = await this.execCommand(`docker logs --tail ${lines} ${container} 2>&1`);
|
|
185
|
+
if (result.code === 0 && result.stdout) {
|
|
186
|
+
return result.stdout;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
// Container doesn't exist, try next
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// Try common bare-metal log paths
|
|
194
|
+
const logPaths = [
|
|
195
|
+
"~/minecraft/logs/latest.log",
|
|
196
|
+
"/opt/minecraft/logs/latest.log",
|
|
197
|
+
"/home/minecraft/logs/latest.log",
|
|
198
|
+
"/srv/minecraft/logs/latest.log",
|
|
199
|
+
];
|
|
200
|
+
for (const logPath of logPaths) {
|
|
201
|
+
try {
|
|
202
|
+
const result = await this.execCommand(`tail -n ${lines} ${logPath}`);
|
|
203
|
+
if (result.code === 0 && result.stdout) {
|
|
204
|
+
return result.stdout;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
// Path doesn't exist, try next
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
throw new Error("Could not find server log. Set log_path in config.");
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Execute a command on the remote server (e.g., restart).
|
|
215
|
+
*/
|
|
216
|
+
async exec(command) {
|
|
217
|
+
return this.execCommand(command);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Create a local port forward through the SSH connection.
|
|
221
|
+
* Returns the local port number and a close function.
|
|
222
|
+
*/
|
|
223
|
+
async createTunnel(remoteHost, remotePort) {
|
|
224
|
+
const conn = await this.ensureConnected();
|
|
225
|
+
const net = await import("node:net");
|
|
226
|
+
return new Promise((resolve, reject) => {
|
|
227
|
+
const server = net.createServer((socket) => {
|
|
228
|
+
conn.forwardOut("127.0.0.1", 0, remoteHost, remotePort, (err, stream) => {
|
|
229
|
+
if (err) {
|
|
230
|
+
socket.destroy();
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
socket.pipe(stream).pipe(socket);
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
server.listen(0, "127.0.0.1", () => {
|
|
237
|
+
const addr = server.address();
|
|
238
|
+
if (!addr || typeof addr === "string") {
|
|
239
|
+
reject(new Error("Failed to bind tunnel"));
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
resolve({
|
|
243
|
+
localPort: addr.port,
|
|
244
|
+
close: () => server.close(),
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
server.on("error", reject);
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Execute a Minecraft console command via docker exec.
|
|
252
|
+
* Useful when RCON isn't configured at all.
|
|
253
|
+
*/
|
|
254
|
+
async dockerRcon(containerName, command) {
|
|
255
|
+
// Sanitize inputs to prevent command injection
|
|
256
|
+
const safeContainer = containerName.replaceAll(/[^a-zA-Z0-9_.-]/g, "");
|
|
257
|
+
const safeCommand = command.replaceAll("'", String.raw `'\''`);
|
|
258
|
+
const result = await this.execCommand(`docker exec ${safeContainer} rcon-cli ${safeCommand} 2>/dev/null || docker exec -i ${safeContainer} mc-send-to-console '${safeCommand}' 2>/dev/null`);
|
|
259
|
+
return result.stdout.trim();
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Close the SSH connection.
|
|
263
|
+
*/
|
|
264
|
+
async disconnect() {
|
|
265
|
+
if (this.conn) {
|
|
266
|
+
this.conn.end();
|
|
267
|
+
this.conn = null;
|
|
268
|
+
this.connected = false;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
exports.SshConnector = SshConnector;
|
|
273
|
+
//# sourceMappingURL=ssh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssh.js","sourceRoot":"","sources":["../../src/connectors/ssh.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CraftClose Skill — Main orchestrator
|
|
3
|
+
*
|
|
4
|
+
* Coordinates monitoring, crash detection, pattern matching, AI analysis, and alerting.
|
|
5
|
+
*/
|
|
6
|
+
import type { CraftCloseConfig, HealthCheckResult } from "./types.js";
|
|
7
|
+
import { HistoryTracker } from "./history.js";
|
|
8
|
+
export declare class CraftCloseSkill {
|
|
9
|
+
private readonly config;
|
|
10
|
+
private monitoringInterval;
|
|
11
|
+
private readonly restartCounts;
|
|
12
|
+
private readonly telegramAlert;
|
|
13
|
+
private readonly discordAlert;
|
|
14
|
+
private readonly serverStates;
|
|
15
|
+
private readonly activeIncidents;
|
|
16
|
+
private readonly history;
|
|
17
|
+
private readonly aiAnalyzer;
|
|
18
|
+
private lastResults;
|
|
19
|
+
constructor(config: CraftCloseConfig, dbPath?: string);
|
|
20
|
+
/**
|
|
21
|
+
* Start monitoring all configured servers.
|
|
22
|
+
*/
|
|
23
|
+
start(): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Stop monitoring.
|
|
26
|
+
*/
|
|
27
|
+
stop(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Get a formatted status string for all servers (for chat/CLI display).
|
|
30
|
+
*/
|
|
31
|
+
getStatus(): Promise<string>;
|
|
32
|
+
/** Get the most recent health check results (from memory, not a new check) */
|
|
33
|
+
getLastResults(): HealthCheckResult[];
|
|
34
|
+
/** Get the config (without sensitive fields — callers handle filtering) */
|
|
35
|
+
getConfig(): CraftCloseConfig;
|
|
36
|
+
/** Get access to the history tracker for querying */
|
|
37
|
+
getHistoryTracker(): HistoryTracker;
|
|
38
|
+
/**
|
|
39
|
+
* Run a single health check across all servers.
|
|
40
|
+
*/
|
|
41
|
+
checkAllServers(): Promise<HealthCheckResult[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Process a health check result: recovery detection, crash handling, TPS warnings.
|
|
44
|
+
*/
|
|
45
|
+
private processCheckResult;
|
|
46
|
+
/**
|
|
47
|
+
* Attempt SSH health check, merging results into the given HealthCheckResult.
|
|
48
|
+
* Returns the SshConnector for cleanup.
|
|
49
|
+
*/
|
|
50
|
+
private trySshHealth;
|
|
51
|
+
/**
|
|
52
|
+
* Attempt RCON health check, merging results into the given HealthCheckResult.
|
|
53
|
+
* Returns the RconConnector for cleanup.
|
|
54
|
+
* If RCON host is localhost and SSH is available, tunnels through SSH.
|
|
55
|
+
*/
|
|
56
|
+
private tryRconHealth;
|
|
57
|
+
/**
|
|
58
|
+
* Attempt Pterodactyl health check, merging results into the given HealthCheckResult.
|
|
59
|
+
*/
|
|
60
|
+
private tryPteroHealth;
|
|
61
|
+
/**
|
|
62
|
+
* Attempt Docker exec fallback for TPS/players when no RCON is configured.
|
|
63
|
+
*/
|
|
64
|
+
private tryDockerRcon;
|
|
65
|
+
/**
|
|
66
|
+
* Check a single server's health.
|
|
67
|
+
*/
|
|
68
|
+
checkServer(serverName: string): Promise<HealthCheckResult>;
|
|
69
|
+
/**
|
|
70
|
+
* Handle a server that's detected as down.
|
|
71
|
+
* Includes crash detection, pattern matching, dedup, restart, and alerting.
|
|
72
|
+
*/
|
|
73
|
+
private handleServerDown;
|
|
74
|
+
/**
|
|
75
|
+
* Classify crash type based on available data.
|
|
76
|
+
*/
|
|
77
|
+
private classifyCrashType;
|
|
78
|
+
/**
|
|
79
|
+
* Enrich a crash event with log tail pattern matching (best-effort).
|
|
80
|
+
* Tries Pterodactyl file read first, then SSH tail as fallback.
|
|
81
|
+
*/
|
|
82
|
+
private enrichCrashWithPatterns;
|
|
83
|
+
/**
|
|
84
|
+
* Attempt to fetch recent log text via Pterodactyl or SSH.
|
|
85
|
+
*/
|
|
86
|
+
private fetchCrashLog;
|
|
87
|
+
/**
|
|
88
|
+
* Handle low TPS warning with deduplication.
|
|
89
|
+
*/
|
|
90
|
+
private handleTpsWarning;
|
|
91
|
+
/**
|
|
92
|
+
* Circuit breaker: check if we're allowed to restart.
|
|
93
|
+
* Max 3 restarts per 10-minute window.
|
|
94
|
+
*/
|
|
95
|
+
private canRestart;
|
|
96
|
+
/**
|
|
97
|
+
* Restart a server via Pterodactyl power action or SSH restart command.
|
|
98
|
+
*/
|
|
99
|
+
private restartServer;
|
|
100
|
+
/**
|
|
101
|
+
* Send an alert to configured channels (Telegram, Discord).
|
|
102
|
+
* Alert failures are caught and logged — they never stop monitoring.
|
|
103
|
+
*/
|
|
104
|
+
private sendAlert;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=craft-close-skill.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"craft-close-skill.d.ts","sourceRoot":"","sources":["../src/craft-close-skill.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EAGlB,MAAM,YAAY,CAAC;AAQpB,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAG9C,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmB;IAC1C,OAAO,CAAC,kBAAkB,CAA+C;IACzE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgE;IAG9F,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA8B;IAC5D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6B;IAG1D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAmC;IAChE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAoF;IAGpH,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IAGzC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA2B;IAGtD,OAAO,CAAC,WAAW,CAA2B;gBAElC,MAAM,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,MAAM;IAoBrD;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB5B;;OAEG;IACH,IAAI,IAAI,IAAI;IAUZ;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;IAelC,8EAA8E;IAC9E,cAAc,IAAI,iBAAiB,EAAE;IAIrC,2EAA2E;IAC3E,SAAS,IAAI,gBAAgB;IAI7B,qDAAqD;IACrD,iBAAiB,IAAI,cAAc;IAInC;;OAEG;IACG,eAAe,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;IA0BrD;;OAEG;YACW,kBAAkB;IA4BhC;;;OAGG;YACW,YAAY;IA8B1B;;;;OAIG;YACW,aAAa;IAqD3B;;OAEG;YACW,cAAc;IA4B5B;;OAEG;YACW,aAAa;IA0B3B;;OAEG;IACG,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA4CjE;;;OAGG;YACW,gBAAgB;IAyE9B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAUzB;;;OAGG;YACW,uBAAuB;IA4BrC;;OAEG;YACW,aAAa;IAoC3B;;OAEG;YACW,gBAAgB;IA2B9B;;;OAGG;IACH,OAAO,CAAC,UAAU;IAoBlB;;OAEG;YACW,aAAa;IA2C3B;;;OAGG;YACW,SAAS;CAuBxB"}
|