@irsyadulibad/servermon 1.0.4 β†’ 1.1.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.
Files changed (4) hide show
  1. package/README.md +1 -1
  2. package/cli.ts +140 -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,99 @@ 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 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
+
17
110
  async function interactiveSetup(): Promise<void> {
18
111
  console.log("πŸ–₯ Server Monitor β€” First Time Setup");
19
112
  console.log(` Config will be saved to: ${configDir()}\n`);
@@ -65,32 +158,62 @@ async function interactiveSetup(): Promise<void> {
65
158
  console.log(`\nβœ… Config saved!`);
66
159
  console.log(` πŸ“ ${configPath()}`);
67
160
  console.log(` ⏱ Interval: ${interval}s (${label})`);
68
- console.log(`\nπŸ“‘ Next step: DM your bot once on Telegram, then re-run \`servermon\`.`);
161
+ console.log(`\nπŸ“‘ Next step: DM your bot once on Telegram, then run \`servermon\`.`);
69
162
  }
70
163
 
71
164
  async function main() {
72
- banner();
73
-
74
- const config = await loadConfig();
165
+ const cmd = process.argv[2];
75
166
 
76
- if (!config) {
167
+ // --- subcommand routing ---
168
+ if (cmd === "setup") {
169
+ banner();
77
170
  await interactiveSetup();
78
- process.exit(0);
171
+ return;
79
172
  }
80
173
 
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;
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
+ }
85
194
 
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();
195
+ if (cmd === "install-service" || cmd === "--install-service" || cmd === "--setup-systemd") {
196
+ await setupSystemd();
197
+ return;
198
+ }
90
199
 
91
- // Start the daemon
92
- const { start } = await import("./index.ts");
93
- await start();
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");
94
217
  }
95
218
 
96
219
  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.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",