@irsyadulibad/servermon 1.1.1 → 1.2.1

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/README.md CHANGED
@@ -14,15 +14,17 @@
14
14
 
15
15
  ## ✨ Features
16
16
 
17
- | Category | What it tracks |
18
- | -------------------- | -------------------------------------------------------------- |
19
- | 💻 **CPU** | Model, core count, usage %, load average (1/5/15 min) |
20
- | 🧠 **RAM** | Used / total, usage %, swap usage |
21
- | 💾 **Disk** | Per-mount used / total, usage % — deduplicated |
22
- | 🌐 **Network** | RX / TX rate (bytes/sec, sampled over 1s) |
23
- | 🌡 **Temperature** | CPU package temp via thermal zones (`/sys/class/thermal`) |
24
- | 📊 **Top Processes** | Top 5 by CPU % — PID, name, CPU %, MEM % |
25
- | 🚨 **Alerts** | Auto-flag when CPU > 85%, RAM > 90%, disk > 90%, or swap > 50% |
17
+ | Category | What it tracks |
18
+ | --------------------- | -------------------------------------------------------------- |
19
+ | 💻 **CPU** | Model, core count, usage %, load average (1/5/15 min) |
20
+ | 🧠 **RAM** | Used / total, usage %, swap usage |
21
+ | 💾 **Disk** | Per-mount used / total, usage % — deduplicated |
22
+ | 🌐 **Network** | RX / TX rate (bytes/sec, sampled over 1s) |
23
+ | 🌡 **Temperature** | CPU package temp via thermal zones (`/sys/class/thermal`) |
24
+ | 📊 **Top Processes** | Top 5 by CPU % — PID, name, CPU %, MEM % |
25
+ | 🚨 **Alerts** | Auto-flag when CPU > 85%, RAM > 90%, disk > 90%, or swap > 50% |
26
+ | 🌍 **Multi-server** | Monitor multiple servers with one bot via `--name` flag |
27
+ | ⏱ **One-shot report** | Send a report on demand without starting the daemon |
26
28
 
27
29
  Each report is color-coded with 🟢🟡🔴 health indicators and visual bar charts.
28
30
 
@@ -39,7 +41,7 @@ bun i -g @irsyadulibad/servermon
39
41
  ### First run — interactive setup
40
42
 
41
43
  ```bash
42
- servermon
44
+ servermon setup
43
45
  ```
44
46
 
45
47
  You'll be prompted for:
@@ -56,31 +58,80 @@ DM your bot **once** (any message). The daemon auto-detects your chat ID.
56
58
  ### Start monitoring
57
59
 
58
60
  ```bash
59
- servermon
61
+ servermon start
60
62
  ```
61
63
 
62
64
  That's it. The daemon auto-detects your chat ID, sends an initial report, then loops.
63
65
 
64
66
  ---
65
67
 
66
- ## 🛠 Development
68
+ ## 📖 Usage
69
+
70
+ ```
71
+ Usage: servermon <command> [options]
72
+
73
+ Commands:
74
+ setup [--name <srv>] First-time setup (bot token + interval) for a server
75
+ start [--name <srv>] Start the monitoring daemon for a server
76
+ report [--name <srv>] Send a one-time report without starting the daemon
77
+ list List all configured servers
78
+ delete <name> Delete a configured server
79
+ service <sub> Manage systemd service
80
+
81
+ Options:
82
+ -n, --name Server name
83
+ -h, --help Show help
84
+ -v, --version Show version number
85
+ ```
86
+
87
+ ### Examples
67
88
 
68
89
  ```bash
69
- git clone https://github.com/irsyadlab/server-monitoring.git
70
- cd server-monitoring
71
- bun install
90
+ # Basic setup
91
+ servermon setup
92
+
93
+ # Setup with a server name (for multi-server)
94
+ servermon setup --name prod
95
+
96
+ # Start monitoring a specific server
97
+ servermon start --name staging
98
+
99
+ # Start monitoring ALL configured servers at once
100
+ servermon start
101
+
102
+ # Send a one-time report (daemon not required)
103
+ servermon report --name prod
72
104
 
73
- # Run in dev mode
74
- bun start
105
+ # List all configured servers
106
+ servermon list
75
107
 
76
- # Global link (for testing)
77
- bun link
78
- servermon
108
+ # Delete a server config (interactive confirm)
109
+ servermon delete prod
79
110
 
80
- # Unlink
81
- bun unlink
111
+ # Force delete without confirmation
112
+ servermon delete prod --yes
82
113
  ```
83
114
 
115
+ ### 🌍 Multi-server mode
116
+
117
+ `servermon start` (without `--name`) automatically runs **all configured servers** in a single daemon process — each server gets its own config, but they share one process and interval.
118
+
119
+ ```
120
+ ┌─────────────────────────────┐
121
+ │ servermon start │
122
+ │ (multi-server daemon) │
123
+ │ │
124
+ │ ├─ default → Telegram │
125
+ │ ├─ prod → Telegram │
126
+ │ └─ staging → Telegram │
127
+ └─────────────────────────────┘
128
+ ```
129
+
130
+ Reports appear with the server name in the header:
131
+
132
+ > 🖥 **server01** [**prod**] — ✅ HEALTHY
133
+ > 🖥 **server02** [**staging**] — ⚠️ WARNING
134
+
84
135
  ---
85
136
 
86
137
  ## 📷 Example Report
@@ -111,7 +162,13 @@ bun unlink
111
162
  ✨ All systems normal
112
163
  ```
113
164
 
114
- When thresholds are crossed, alerts appear inline.
165
+ When thresholds are crossed, alerts appear inline:
166
+
167
+ ```
168
+ 🚨 CRITICAL
169
+ 🔴 RAM hampir penuh: 91.2%
170
+ 🔴 Disk /: 91%
171
+ ```
115
172
 
116
173
  ---
117
174
 
@@ -131,12 +188,23 @@ When thresholds are crossed, alerts appear inline.
131
188
 
132
189
  ```
133
190
  server-monitoring/
134
- ├── cli.ts # Global binary entry (interactive setup + launcher)
135
- ├── index.ts # Daemon core (start/loop/report)
191
+ ├── cli.ts # CLI wrapper (delegates to src/cli)
192
+ ├── index.ts # Module entry (re-exports daemon functions)
136
193
  ├── src/
137
- │ ├── config.ts # Config manager (~/.irsyadulibad/servermon/)
138
- ├── monitor.ts # Metrics collector (CPU, RAM, disk, net, temp, procs)
139
- └── reporter.ts # HTML formatter & Telegram sender
194
+ │ ├── types/ # Shared type definitions
195
+ │ └── index.ts
196
+ ├── config/ # Config CRUD (load, save, delete, listServers)
197
+ │ │ └── index.ts
198
+ │ ├── monitor/ # Metrics collector + formatting utilities
199
+ │ │ └── index.ts
200
+ │ ├── reporter/ # HTML formatter & Telegram sender
201
+ │ │ └── index.ts
202
+ │ ├── daemon/ # start(), startAll(), autoDetectChatId()
203
+ │ │ └── index.ts
204
+ │ └── cli/ # CLI command routing (CrustJS) + interactive setup + banner
205
+ │ ├── banner.ts
206
+ │ ├── index.ts
207
+ │ └── service.ts # systemd service subcommands
140
208
  ├── eslint.config.js
141
209
  ├── .prettierrc
142
210
  ├── package.json
@@ -157,16 +225,47 @@ Stored at `~/.irsyadulibad/servermon/config.json`:
157
225
  }
158
226
  ```
159
227
 
160
- - `token` Telegram bot token (required)
161
- - `interval` — seconds between reports, min: 30 (default: 300)
162
- - `chatId` — auto-detected on first run, persisted for subsequent runs
228
+ For named servers, configs are stored separately:
229
+
230
+ ```
231
+ ~/.irsyadulibad/servermon/
232
+ ├── config.json # default server
233
+ ├── config-prod.json # servermon setup --name prod
234
+ └── config-staging.json # servermon setup --name staging
235
+ ```
236
+
237
+ | Field | Description |
238
+ | ---------- | --------------------------------------------------------- |
239
+ | `token` | Telegram bot token (required) |
240
+ | `interval` | Seconds between reports (min: 30, default: 300) |
241
+ | `chatId` | Auto-detected on first run, persisted for subsequent runs |
242
+ | `name` | Server name label (set via `--name`, shown in reports) |
163
243
 
164
244
  ---
165
245
 
166
246
  ## 📦 Deploy as systemd service
167
247
 
168
248
  ```bash
169
- # After installing globally
249
+ # Install & start
250
+ servermon service install
251
+
252
+ # Check health
253
+ servermon service status
254
+
255
+ # View real-time logs
256
+ servermon service logs
257
+
258
+ # Stop / restart / uninstall
259
+ servermon service stop
260
+ servermon service restart
261
+ servermon service uninstall --yes
262
+ ```
263
+
264
+ This creates a user-level systemd service at `~/.config/systemd/user/servermon.service` that runs `servermon start` (multi-server mode) and automatically restarts on failure.
265
+
266
+ ### Manual (if you prefer):
267
+
268
+ ```bash
170
269
  sudo tee /etc/systemd/system/servermon.service << 'EOF'
171
270
  [Unit]
172
271
  Description=Server Monitor Daemon
@@ -174,7 +273,7 @@ After=network.target
174
273
 
175
274
  [Service]
176
275
  Type=simple
177
- ExecStart=/root/.bun/bin/servermon
276
+ ExecStart=/home/irsyad/.bun/bin/servermon start
178
277
  Restart=always
179
278
  RestartSec=10
180
279
 
@@ -190,9 +289,10 @@ sudo systemctl enable --now servermon
190
289
 
191
290
  ## 🛠 Built With
192
291
 
193
- - [Bun](https://bun.com) — fast all-in-one JavaScript runtime
292
+ - [Bun](https://bun.sh) — fast all-in-one JavaScript runtime
194
293
  - [TypeScript](https://www.typescriptlang.org/) — type safety
195
294
  - [Telegram Bot API](https://core.telegram.org/bots/api) — message delivery
295
+ - [CrustJS](https://crustjs.com) — CLI framework (command routing, plugins, type-safe parsing)
196
296
  - [ESLint](https://eslint.org/) + [Prettier](https://prettier.io/) — code quality
197
297
 
198
298
  ---
package/cli.ts CHANGED
@@ -1,220 +1,9 @@
1
1
  #!/usr/bin/env bun
2
2
  /**
3
- * Server Monitor — Global CLI entry point.
4
- * Handles first-time interactive setup and starts the monitoring daemon.
3
+ * Server Monitor — CLI entry point.
4
+ * Uses @crustjs/core for command parsing and routing.
5
5
  */
6
-
7
- import { loadConfig, saveConfig, configPath, configDir } from "./src/config";
8
-
9
- function banner() {
10
- console.log("╔══════════════════════════════════════╗");
11
- console.log("║ 🖥 SERVER MONITOR DAEMON 🖥 ║");
12
- console.log("║ Telegram • Bun • TypeScript ║");
13
- console.log("╚══════════════════════════════════════╝");
14
- console.log();
15
- }
16
-
17
- // ─────────────────────────────────────
18
- // systemd auto‑setup
19
- // ─────────────────────────────────────
20
- async function setupSystemd(): Promise<void> {
21
- banner();
22
- console.log("⚙️ Systemd Service Setup\n");
23
-
24
- const config = await loadConfig();
25
- if (!config) {
26
- console.error("❌ No config found. Run `servermon` first to set up.");
27
- console.error(" Then try: servermon --install-service");
28
- process.exit(1);
29
- }
30
-
31
- // Resolve paths
32
- const bunPath = Bun.which("bun");
33
- if (!bunPath) {
34
- console.error("❌ bun not found in PATH");
35
- process.exit(1);
36
- }
37
-
38
- const servermonPath = Bun.which("servermon");
39
- if (!servermonPath) {
40
- console.error("❌ servermon binary not found.");
41
- console.error(" Install with: bun i -g @irsyadulibad/servermon");
42
- process.exit(1);
43
- }
44
-
45
- console.log(`🔍 bun: ${bunPath}`);
46
- console.log(`🔍 servermon: ${servermonPath}`);
47
- console.log(`📁 Config: ${configPath()}`);
48
- console.log();
49
-
50
- // Create systemd user directory
51
- const home = process.env.HOME ?? "~";
52
- const systemdDir = `${home}/.config/systemd/user`;
53
- await Bun.write(`${systemdDir}/.gitkeep`, ""); // ensure dir exists
54
- try { await (Bun as any).mkdir?.(systemdDir, { recursive: true }); } catch { /* Bun.mkdir may not exist; fs fallback */ }
55
- try { await import("node:fs").then(m => m.mkdirSync(systemdDir, { recursive: true })); } catch {}
56
-
57
- const serviceFile = `${systemdDir}/servermon.service`;
58
- const bunDir = bunPath.replace(/\/bun$/, ""); // strip binary name, keep directory
59
- const serviceContent = `[Unit]
60
- Description=Server Monitor — Telegram system health reports
61
- After=network-online.target
62
- Wants=network-online.target
63
-
64
- [Service]
65
- Type=simple
66
- ExecStart=${servermonPath} start
67
- Restart=always
68
- RestartSec=30
69
- Environment=NODE_ENV=production
70
- Environment=PATH=${bunDir}:/usr/local/bin:/usr/bin:/bin
71
-
72
- [Install]
73
- WantedBy=default.target
74
- `;
75
-
76
- await Bun.write(serviceFile, serviceContent);
77
- console.log(`📄 Written: ${serviceFile}`);
78
- console.log();
79
-
80
- // systemctl commands
81
- const cmds = [
82
- ["systemctl", "--user", "daemon-reload"],
83
- ["systemctl", "--user", "enable", "servermon.service"],
84
- ["systemctl", "--user", "start", "servermon.service"],
85
- ["systemctl", "--user", "status", "servermon.service", "--no-pager", "-l"],
86
- ];
87
-
88
- for (const cmd of cmds) {
89
- console.log(`🏃 ${cmd.join(" ")}`);
90
- const proc = Bun.spawnSync({ cmd, stdout: "pipe", stderr: "pipe" });
91
- const out = new TextDecoder().decode(proc.stdout);
92
- const err = new TextDecoder().decode(proc.stderr);
93
- if (out) console.log(out);
94
- if (err && proc.exitCode !== 0) console.error(err);
95
- }
96
-
97
- console.log();
98
- console.log("✅ Systemd service installed!");
99
- console.log();
100
- console.log("📌 For auto-start at boot, enable lingering:");
101
- console.log(` loginctl enable-linger`);
102
- console.log();
103
- console.log("📋 Useful commands:");
104
- console.log(" systemctl --user status servermon # check status");
105
- console.log(" systemctl --user stop servermon # stop daemon");
106
- console.log(" systemctl --user restart servermon # restart");
107
- console.log(" journalctl --user -u servermon -f # watch logs");
108
- }
109
-
110
- async function interactiveSetup(): Promise<void> {
111
- console.log("🖥 Server Monitor — First Time Setup");
112
- console.log(` Config will be saved to: ${configDir()}\n`);
113
-
114
- const token = prompt("🔑 Telegram Bot Token: ")?.trim();
115
- if (!token) {
116
- console.error("❌ Bot token is required. Get one from @BotFather on Telegram.");
117
- process.exit(1);
118
- }
119
-
120
- if (!token.includes(":")) {
121
- console.error("❌ Invalid bot token format. Should look like: 123456:ABC-DEF1234gh...");
122
- process.exit(1);
123
- }
124
-
125
- console.log();
126
- console.log("⏱ Choose report interval:");
127
- console.log(" 1. Every 5 minutes");
128
- console.log(" 2. Every 1 hour");
129
- console.log(" 3. Every 3 hours");
130
- console.log(" 4. Every 6 hours");
131
- console.log(" 5. Every 12 hours");
132
- console.log(" 6. Custom (in seconds)");
133
-
134
- const choice = prompt(" Pick [1-6] (default: 1): ")?.trim() || "1";
135
-
136
- const intervals: Record<string, number> = {
137
- "1": 300,
138
- "2": 3600,
139
- "3": 10800,
140
- "4": 21600,
141
- "5": 43200,
142
- };
143
-
144
- let interval: number;
145
- if (choice === "6") {
146
- const custom = prompt(" Enter interval in seconds: ")?.trim();
147
- interval = Math.max(30, parseInt(custom || "300") || 300);
148
- } else {
149
- interval = intervals[choice] ?? 300;
150
- }
151
-
152
- const label =
153
- interval >= 3600
154
- ? `${(interval / 3600).toFixed(0)} hour(s)`
155
- : `${(interval / 60).toFixed(0)} min`;
156
-
157
- await saveConfig({ token, interval });
158
- console.log(`\n✅ Config saved!`);
159
- console.log(` 📁 ${configPath()}`);
160
- console.log(` ⏱ Interval: ${interval}s (${label})`);
161
- console.log(`\n📡 Next step: DM your bot once on Telegram, then run \`servermon\`.`);
162
- }
163
-
164
- async function main() {
165
- const cmd = process.argv[2];
166
-
167
- // --- subcommand routing ---
168
- if (cmd === "setup") {
169
- banner();
170
- await interactiveSetup();
171
- return;
172
- }
173
-
174
- if (cmd === "start") {
175
- banner();
176
- const config = await loadConfig();
177
- if (!config) {
178
- console.error("❌ No config found. Run `servermon setup` first.");
179
- process.exit(1);
180
- }
181
- process.env["TELEGRAM_BOT_TOKEN"] = config.token;
182
- process.env["MONITOR_INTERVAL"] = String(config.interval);
183
- if (config.chatId) process.env["TELEGRAM_CHAT_ID"] = config.chatId;
184
-
185
- console.log(`📁 Config: ${configPath()}`);
186
- console.log(`📡 Bot: ...${config.token.slice(-8)}`);
187
- if (config.chatId) console.log(`💬 Chat: ${config.chatId}`);
188
- console.log();
189
-
190
- const { start } = await import("./index.ts");
191
- await start();
192
- return;
193
- }
194
-
195
- if (cmd === "install-service" || cmd === "--install-service" || cmd === "--setup-systemd") {
196
- await setupSystemd();
197
- return;
198
- }
199
-
200
- // --- no/invalid subcommand → help ---
201
- console.log("╔══════════════════════════════════════╗");
202
- console.log("║ 🖥 SERVER MONITOR DAEMON 🖥 ║");
203
- console.log("║ Telegram • Bun • TypeScript ║");
204
- console.log("╚══════════════════════════════════════╝");
205
- console.log();
206
- console.log("Usage: servermon <command>");
207
- console.log();
208
- console.log("Commands:");
209
- console.log(" setup First-time setup (bot token + interval)");
210
- console.log(" start Start the monitoring daemon");
211
- console.log(" install-service Install as systemd user service");
212
- console.log();
213
- console.log("Examples:");
214
- console.log(" servermon setup");
215
- console.log(" servermon start");
216
- console.log(" servermon install-service");
217
- }
6
+ import { main } from "./src/cli";
218
7
 
219
8
  main().catch((err) => {
220
9
  console.error("❌ Fatal error:", err?.message ?? err);
package/index.ts CHANGED
@@ -1,118 +1,5 @@
1
- import { sendReport } from "./src/reporter";
2
- import { saveConfig } from "./src/config";
3
-
4
- const botToken = process.env["TELEGRAM_BOT_TOKEN"] ?? "";
5
- let chatId = process.env["TELEGRAM_CHAT_ID"] ?? "";
6
- const rawInterval = process.env["MONITOR_INTERVAL"] ?? "300";
7
- const intervalSec = Math.max(30, parseInt(rawInterval) || 300);
8
-
9
- // --- Auto-detect chat ID ---
10
- async function autoDetectChatId(token: string): Promise<string | null> {
11
- try {
12
- const resp = await fetch(`https://api.telegram.org/bot${token}/getUpdates?limit=5`);
13
- const data = (await resp.json()) as {
14
- ok: boolean;
15
- result?: Array<{
16
- message?: { chat?: { id: number; title?: string; first_name?: string } };
17
- channel_post?: { chat?: { id: number; title?: string } };
18
- }>;
19
- };
20
- if (!data.ok || !data.result?.length) return null;
21
-
22
- const chatIds = new Set<string>();
23
- for (const update of data.result.reverse()) {
24
- const chat = update.message?.chat || update.channel_post?.chat;
25
- if (chat?.id) chatIds.add(String(chat.id));
26
- }
27
- if (chatIds.size === 0) return null;
28
-
29
- const id = [...chatIds][0]!;
30
- const chatInfo = data.result.find(
31
- (u) => String(u.message?.chat?.id || u.channel_post?.chat?.id) === id
32
- );
33
- const chatName =
34
- chatInfo?.message?.chat?.title ||
35
- chatInfo?.message?.chat?.first_name ||
36
- chatInfo?.channel_post?.chat?.title ||
37
- "Unknown";
38
- console.log(`🔍 Auto-detected chat: ${chatName} (ID: ${id})`);
39
- return id;
40
- } catch {
41
- return null;
42
- }
43
- }
44
-
45
- // --- Daemon ---
46
- export async function start() {
47
- if (!botToken) {
48
- console.error("❌ TELEGRAM_BOT_TOKEN not set. Run `servermon` first to configure.");
49
- process.exit(1);
50
- }
51
-
52
- if (!chatId) {
53
- console.log("🔍 TELEGRAM_CHAT_ID not set — auto-detecting...");
54
- let detected = await autoDetectChatId(botToken);
55
-
56
- if (!detected) {
57
- console.log("⏳ Waiting for you to DM the bot on Telegram...");
58
- console.log(" (polling every 10s — no restart needed)");
59
- console.log();
60
-
61
- const POLL_INTERVAL_MS = 10_000;
62
-
63
- while (!detected) {
64
- await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
65
- detected = await autoDetectChatId(botToken);
66
- }
67
- }
68
-
69
- chatId = detected;
70
- // Persist to config
71
- try {
72
- const { loadConfig } = await import("./src/config");
73
- const cfg = await loadConfig();
74
- if (cfg) {
75
- cfg.chatId = chatId;
76
- await saveConfig(cfg);
77
- console.log("💾 Chat ID saved to config");
78
- }
79
- } catch {
80
- // ok
81
- }
82
- }
83
-
84
- console.log(`⏱ Interval: ${intervalSec}s (${(intervalSec / 60).toFixed(0)} menit)`);
85
- console.log(`📡 Bot: ...${botToken.slice(-8)}`);
86
- console.log(`💬 Chat: ${chatId}`);
87
- console.log();
88
-
89
- async function tick() {
90
- const start2 = Date.now();
91
- const ok = await sendReport(botToken, chatId);
92
- const elapsed = Date.now() - start2;
93
- const ts = new Date().toLocaleString("id-ID", { timeZone: "Asia/Jakarta" });
94
- console.log(`[${ts}] ${ok ? "✅" : "❌"} ${elapsed}ms`);
95
- }
96
-
97
- // Run once at startup
98
- await tick();
99
-
100
- // Loop
101
- setInterval(tick, intervalSec * 1000);
102
-
103
- process.on("SIGINT", () => {
104
- console.log("\n👋 Shutting down...");
105
- process.exit(0);
106
- });
107
- process.on("SIGTERM", () => {
108
- console.log("\n👋 Shutting down...");
109
- process.exit(0);
110
- });
111
- }
112
-
113
- // Direct run support (for dev / bun index.ts)
114
- // When imported by cli.ts, cli.ts calls start() explicitly
115
- const isDirectlyRun = import.meta.url.endsWith(process.argv[1]?.replace(/^.*\//, "") ?? "");
116
- if (isDirectlyRun || process.argv[1]?.endsWith("index.ts")) {
117
- start();
118
- }
1
+ /**
2
+ * Server Monitor module entry point.
3
+ * Re-exports daemon functions for programmatic use.
4
+ */
5
+ export { start, startAll } from "./src/daemon";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@irsyadulibad/servermon",
3
- "version": "1.1.1",
3
+ "version": "1.2.1",
4
4
  "description": "Lightweight server monitoring daemon — collects system metrics and sends structured reports to Telegram. Built with Bun + TypeScript.",
5
5
  "module": "index.ts",
6
6
  "type": "module",
@@ -8,6 +8,9 @@
8
8
  "servermon": "./cli.ts"
9
9
  },
10
10
  "private": false,
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
11
14
  "scripts": {
12
15
  "start": "bun index.ts",
13
16
  "build": "bun build --compile index.ts --outfile server-monitor",
@@ -27,5 +30,9 @@
27
30
  },
28
31
  "peerDependencies": {
29
32
  "typescript": "^5"
33
+ },
34
+ "dependencies": {
35
+ "@crustjs/core": "^0.0.19",
36
+ "@crustjs/plugins": "^0.1.2"
30
37
  }
31
38
  }
@@ -0,0 +1,65 @@
1
+ /* ------------------------------------------------------------------ */
2
+ /* ANSI-coloured ASCII banner */
3
+ /* ------------------------------------------------------------------ */
4
+
5
+ const c = {
6
+ cyan: "\x1b[36m",
7
+ gray: "\x1b[90m",
8
+ gold: "\x1b[33m",
9
+ reset: "\x1b[0m",
10
+ };
11
+
12
+ export function printBanner(): void {
13
+ console.log("");
14
+ console.log(
15
+ `${c.cyan} ███████ ███████ ██████ ██ ██ ███████ ██████ ███ ███ ██████ ███ ██${c.reset}`
16
+ );
17
+ console.log(
18
+ `${c.cyan} ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ████ ██ ██ ████ ██${c.reset}`
19
+ );
20
+ console.log(
21
+ `${c.cyan} ███████ █████ ██████ ██ ██ █████ ██████ ██ ████ ██ ██ ██ ██ ██ ██${c.reset}`
22
+ );
23
+ console.log(
24
+ `${c.cyan} ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██${c.reset}`
25
+ );
26
+ console.log(
27
+ `${c.cyan} ███████ ███████ ██ ██ ████ ███████ ██ ██ ██ ██ ██████ ██ ████${c.reset}`
28
+ );
29
+ console.log("");
30
+ console.log(
31
+ `${c.gray} ─────────────────────────────────────────────────────────────────────${c.reset}`
32
+ );
33
+ console.log(` 🖥 Server Monitor Daemon • Telegram • Bun • TypeScript`);
34
+ console.log(
35
+ `${c.gray} ─────────────────────────────────────────────────────────────────────${c.reset}`
36
+ );
37
+ console.log(`${c.gold} by irsyadulibad${c.reset}`);
38
+ console.log("");
39
+ }
40
+
41
+ export function printHelp(): void {
42
+ printBanner();
43
+ console.log("Usage: servermon <command> [--name <server>]");
44
+ console.log();
45
+ console.log("Commands:");
46
+ console.log(" setup [--name <srv>] First-time setup (bot token + interval) for a server");
47
+ console.log(" start [--name <srv>] Start the monitoring daemon for a server");
48
+ console.log(" report [--name <srv>] Send a one-time report without starting the daemon");
49
+ console.log(" list List all configured servers");
50
+ console.log(" delete --name <srv> Delete a configured server");
51
+ console.log(
52
+ " service <sub> Manage systemd service (install, status, stop, restart, logs, uninstall)"
53
+ );
54
+ console.log();
55
+ console.log("Examples:");
56
+ console.log(" servermon setup");
57
+ console.log(" servermon setup --name prod");
58
+ console.log(" servermon start --name staging");
59
+ console.log(" servermon report --name prod");
60
+ console.log(" servermon list");
61
+ console.log(" servermon delete --name prod");
62
+ console.log(" servermon service install");
63
+ console.log(" servermon service status");
64
+ console.log(" servermon service logs");
65
+ }