@irsyadulibad/servermon 1.0.4 β†’ 1.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.
Files changed (4) hide show
  1. package/README.md +1 -1
  2. package/cli.ts +138 -17
  3. package/index.ts +13 -4
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -33,7 +33,7 @@ Each report is color-coded with πŸŸ’πŸŸ‘πŸ”΄ health indicators and visual bar ch
33
33
  ### Install globally
34
34
 
35
35
  ```bash
36
- bun i -g github:irsyadulibad/servermon
36
+ bun i -g @irsyadulibad/servermon
37
37
  ```
38
38
 
39
39
  ### First run β€” interactive setup
package/cli.ts CHANGED
@@ -14,6 +14,97 @@ function banner() {
14
14
  console.log();
15
15
  }
16
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 serviceContent = `[Unit]
59
+ Description=Server Monitor β€” Telegram system health reports
60
+ After=network-online.target
61
+ Wants=network-online.target
62
+
63
+ [Service]
64
+ Type=simple
65
+ ExecStart=${servermonPath} start
66
+ Restart=always
67
+ RestartSec=30
68
+ Environment=NODE_ENV=production
69
+
70
+ [Install]
71
+ WantedBy=default.target
72
+ `;
73
+
74
+ await Bun.write(serviceFile, serviceContent);
75
+ console.log(`πŸ“„ Written: ${serviceFile}`);
76
+ console.log();
77
+
78
+ // systemctl commands
79
+ const cmds = [
80
+ ["systemctl", "--user", "daemon-reload"],
81
+ ["systemctl", "--user", "enable", "servermon.service"],
82
+ ["systemctl", "--user", "start", "servermon.service"],
83
+ ["systemctl", "--user", "status", "servermon.service", "--no-pager", "-l"],
84
+ ];
85
+
86
+ for (const cmd of cmds) {
87
+ console.log(`πŸƒ ${cmd.join(" ")}`);
88
+ const proc = Bun.spawnSync({ cmd, stdout: "pipe", stderr: "pipe" });
89
+ const out = new TextDecoder().decode(proc.stdout);
90
+ const err = new TextDecoder().decode(proc.stderr);
91
+ if (out) console.log(out);
92
+ if (err && proc.exitCode !== 0) console.error(err);
93
+ }
94
+
95
+ console.log();
96
+ console.log("βœ… Systemd service installed!");
97
+ console.log();
98
+ console.log("πŸ“Œ For auto-start at boot, enable lingering:");
99
+ console.log(` loginctl enable-linger`);
100
+ console.log();
101
+ console.log("πŸ“‹ Useful commands:");
102
+ console.log(" systemctl --user status servermon # check status");
103
+ console.log(" systemctl --user stop servermon # stop daemon");
104
+ console.log(" systemctl --user restart servermon # restart");
105
+ console.log(" journalctl --user -u servermon -f # watch logs");
106
+ }
107
+
17
108
  async function interactiveSetup(): Promise<void> {
18
109
  console.log("πŸ–₯ Server Monitor β€” First Time Setup");
19
110
  console.log(` Config will be saved to: ${configDir()}\n`);
@@ -65,32 +156,62 @@ async function interactiveSetup(): Promise<void> {
65
156
  console.log(`\nβœ… Config saved!`);
66
157
  console.log(` πŸ“ ${configPath()}`);
67
158
  console.log(` ⏱ Interval: ${interval}s (${label})`);
68
- console.log(`\nπŸ“‘ Next step: DM your bot once on Telegram, then re-run \`servermon\`.`);
159
+ console.log(`\nπŸ“‘ Next step: DM your bot once on Telegram, then run \`servermon\`.`);
69
160
  }
70
161
 
71
162
  async function main() {
72
- banner();
73
-
74
- const config = await loadConfig();
163
+ const cmd = process.argv[2];
75
164
 
76
- if (!config) {
165
+ // --- subcommand routing ---
166
+ if (cmd === "setup") {
167
+ banner();
77
168
  await interactiveSetup();
78
- process.exit(0);
169
+ return;
79
170
  }
80
171
 
81
- // Push config into env for the daemon
82
- process.env["TELEGRAM_BOT_TOKEN"] = config.token;
83
- process.env["MONITOR_INTERVAL"] = String(config.interval);
84
- if (config.chatId) process.env["TELEGRAM_CHAT_ID"] = config.chatId;
172
+ if (cmd === "start") {
173
+ banner();
174
+ const config = await loadConfig();
175
+ if (!config) {
176
+ console.error("❌ No config found. Run `servermon setup` first.");
177
+ process.exit(1);
178
+ }
179
+ process.env["TELEGRAM_BOT_TOKEN"] = config.token;
180
+ process.env["MONITOR_INTERVAL"] = String(config.interval);
181
+ if (config.chatId) process.env["TELEGRAM_CHAT_ID"] = config.chatId;
182
+
183
+ console.log(`πŸ“ Config: ${configPath()}`);
184
+ console.log(`πŸ“‘ Bot: ...${config.token.slice(-8)}`);
185
+ if (config.chatId) console.log(`πŸ’¬ Chat: ${config.chatId}`);
186
+ console.log();
187
+
188
+ const { start } = await import("./index.ts");
189
+ await start();
190
+ return;
191
+ }
85
192
 
86
- console.log(`πŸ“ Config: ${configPath()}`);
87
- console.log(`πŸ“‘ Bot: ...${config.token.slice(-8)}`);
88
- if (config.chatId) console.log(`πŸ’¬ Chat: ${config.chatId}`);
89
- console.log();
193
+ if (cmd === "install-service" || cmd === "--install-service" || cmd === "--setup-systemd") {
194
+ await setupSystemd();
195
+ return;
196
+ }
90
197
 
91
- // Start the daemon
92
- const { start } = await import("./index.ts");
93
- await start();
198
+ // --- no/invalid subcommand β†’ help ---
199
+ console.log("╔══════════════════════════════════════╗");
200
+ console.log("β•‘ πŸ–₯ SERVER MONITOR DAEMON πŸ–₯ β•‘");
201
+ console.log("β•‘ Telegram β€’ Bun β€’ TypeScript β•‘");
202
+ console.log("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•");
203
+ console.log();
204
+ console.log("Usage: servermon <command>");
205
+ console.log();
206
+ console.log("Commands:");
207
+ console.log(" setup First-time setup (bot token + interval)");
208
+ console.log(" start Start the monitoring daemon");
209
+ console.log(" install-service Install as systemd user service");
210
+ console.log();
211
+ console.log("Examples:");
212
+ console.log(" servermon setup");
213
+ console.log(" servermon start");
214
+ console.log(" servermon install-service");
94
215
  }
95
216
 
96
217
  main().catch((err) => {
package/index.ts CHANGED
@@ -51,12 +51,21 @@ export async function start() {
51
51
 
52
52
  if (!chatId) {
53
53
  console.log("πŸ” TELEGRAM_CHAT_ID not set β€” auto-detecting...");
54
- const detected = await autoDetectChatId(botToken);
54
+ let detected = await autoDetectChatId(botToken);
55
+
55
56
  if (!detected) {
56
- console.error("❌ No recent chats found. DM your bot first, then re-run.");
57
- console.error(" Or set TELEGRAM_CHAT_ID in ~/.irsyadulibad/servermon/config.json");
58
- process.exit(1);
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
+ }
59
67
  }
68
+
60
69
  chatId = detected;
61
70
  // Persist to config
62
71
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@irsyadulibad/servermon",
3
- "version": "1.0.4",
3
+ "version": "1.1.0",
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",