bm2 1.0.22 → 1.0.24

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bm2",
3
- "version": "1.0.22",
3
+ "version": "1.0.24",
4
4
  "description": "A blazing-fast, full-featured process manager built entirely on Bun native APIs. The modern PM2 replacement — zero Node.js dependencies, pure Bun performance.",
5
5
  "main": "src/api.ts",
6
6
  "module": "src/api.ts",
@@ -65,10 +65,12 @@
65
65
  ],
66
66
  "dependencies": {
67
67
  "cli-table3": "^0.6.5",
68
+ "pidusage": "^4.0.1",
68
69
  "ws": "^8.19.0"
69
70
  },
70
71
  "devDependencies": {
71
72
  "@types/bun": "^1.3.9",
73
+ "@types/pidusage": "^2.0.5",
72
74
  "@types/ws": "^8.18.1",
73
75
  "bun-types": "latest",
74
76
  "typescript": "^5.9.3"
@@ -32,6 +32,9 @@ import {
32
32
  DEFAULT_LOG_MAX_SIZE,
33
33
  DEFAULT_LOG_RETAIN,
34
34
  } from "./constants";
35
+ import pidusage from "pidusage";
36
+ import { readdir } from "node:fs/promises";
37
+
35
38
 
36
39
  export class ProcessContainer {
37
40
  public id: number;
@@ -238,42 +241,37 @@ export class ProcessContainer {
238
241
  } catch {}
239
242
  }
240
243
 
244
+
241
245
  private startMonitoring() {
242
- this.monitorInterval = setInterval(async () => {
243
- if (!this.pid || this.status !== "online") return;
244
-
245
- try {
246
- if (process.platform === "linux") {
247
- const statusFile = Bun.file(`/proc/${this.pid}/status`);
248
- if (await statusFile.exists()) {
249
- const content = await statusFile.text();
250
- const vmRss = content.match(/VmRSS:\s+(\d+)\s+kB/);
251
- if (vmRss) this.memory = parseInt(vmRss[1]!) * 1024;
246
+ this.monitorInterval = setInterval(async () => {
247
+
248
+ if (!this.pid || this.status !== "online") return;
249
+
250
+ try {
251
+
252
+ // 1. Fetch cross-platform CPU and Memory usage
253
+ const stats = await pidusage(this.pid);
254
+
255
+ // pidusage returns memory directly in bytes and cpu as a percentage
256
+ this.memory = stats.memory;
257
+ this.cpu = stats.cpu;
258
+
259
+ // 2. Track file descriptors (handles) on Linux
260
+ // (pidusage does not provide this metric, so we keep the original logic)
261
+ if (process.platform === "linux") {
262
+ try {
263
+ this.handles = (await readdir(`/proc/${this.pid}/fd`)).length;
264
+ } catch {}
252
265
  }
253
-
254
- try {
255
- const { readdirSync } = require("fs");
256
- this.handles = readdirSync(`/proc/${this.pid}/fd`).length;
257
- } catch {}
258
- } else {
259
- const ps = Bun.spawn(["ps", "-o", "rss=,pcpu=", "-p", String(this.pid)], {
260
- stdout: "pipe", stderr: "pipe",
261
- });
262
- const output = await new Response(ps.stdout).text();
263
- const parts = output.trim().split(/\s+/);
264
- if (parts.length >= 2) {
265
- this.memory = parseInt(parts[0]!) * 1024;
266
- this.cpu = parseFloat(parts[1]!);
266
+
267
+ // 3. Max memory restart
268
+ if (this.config.maxMemoryRestart && this.memory > this.config.maxMemoryRestart) {
269
+ console.log(`[bm2] ${this.name} exceeded memory limit (${this.memory} > ${this.config.maxMemoryRestart}), restarting...`);
270
+ await this.restart();
267
271
  }
268
- }
269
-
270
- // Max memory restart
271
- if (this.config.maxMemoryRestart && this.memory > this.config.maxMemoryRestart) {
272
- console.log(`[bm2] ${this.name} exceeded memory limit (${this.memory} > ${this.config.maxMemoryRestart}), restarting...`);
273
- await this.restart();
274
- }
275
- } catch {}
276
- }, MONITOR_INTERVAL);
272
+
273
+ } catch {}
274
+ }, MONITOR_INTERVAL);
277
275
  }
278
276
 
279
277
  private startLogRotation(logPaths: { outFile: string; errFile: string }) {
@@ -106,6 +106,8 @@ export function printProcessTable(processes: ProcessState[]) {
106
106
  style: { border: ["dim"] },
107
107
  chars: minimalBorders(),
108
108
  });
109
+
110
+ //console.log("processes===>", processes)
109
111
 
110
112
  for (const p of processes) {
111
113
  const cpu = p.monit?.cpu ?? 0;
@@ -158,6 +160,7 @@ export function liveWatchProcess(processes: ProcessState[], interval = 5_000) {
158
160
  // Render table
159
161
  const render = () => {
160
162
  clear();
163
+
161
164
  printProcessTable(getSortedProcesses());
162
165
 
163
166
  console.log(color("─".repeat(50), "dim"));
@@ -15,7 +15,8 @@
15
15
  */
16
16
 
17
17
  import { join } from "path";
18
-
18
+ import { $ } from "bun";
19
+
19
20
  export class StartupManager {
20
21
  async generate(platform?: string): Promise<string> {
21
22
  const os = platform || process.platform;
@@ -59,7 +60,7 @@ ExecStop=${bunPath} run ${bm2Path} kill
59
60
  WantedBy=multi-user.target
60
61
  `;
61
62
 
62
- const servicePath = "/etc/systemd/system/bm2.service";
63
+ const servicePath = "/etc/systemd/system/bm2.service";
63
64
  return `# BM2 Systemd Service
64
65
  # Save to: ${servicePath}
65
66
  # Then run:
@@ -101,29 +102,41 @@ ${unit}`;
101
102
  </dict>
102
103
  </plist>`;
103
104
 
104
- const plistPath = `${process.env.HOME}/Library/LaunchAgents/com.bm2.daemon.plist`;
105
- return `# BM2 LaunchAgent (macOS)
106
- # Save to: ${plistPath}
107
- # Then run:
108
- # launchctl load ${plistPath}
105
+ const plistPath = `${process.env.HOME}/Library/LaunchAgents/com.bm2.daemon.plist`;
106
+
107
+ return `# BM2 LaunchAgent (macOS)
108
+ # Save to: ${plistPath}
109
+ # Then run:
110
+ # launchctl load ${plistPath}
109
111
 
110
- ${plist}`;
111
- }
112
+ ${plist}`;
113
+ }
112
114
 
113
115
  async install(): Promise<string> {
114
116
  const os = process.platform;
115
117
  const content = await this.generate(os);
116
118
 
117
119
  if (os === "linux") {
120
+
118
121
  const servicePath = "/etc/systemd/system/bm2.service";
119
- // Extract just the unit content
120
- //const unitContent = content.split("\n\n").slice(1).join("\n\n");
121
- await Bun.write(servicePath, content);
122
-
123
- Bun.spawn(["sudo", "systemctl", "daemon-reload"], { stdout: "inherit" }).exited;
124
- Bun.spawn(["sudo", "systemctl", "enable", "bm2"], { stdout: "inherit" }).exited;
125
-
126
- return `Service installed at ${servicePath}\nRun: sudo systemctl start bm2`;
122
+
123
+ const unitStart = content.indexOf("[Unit]");
124
+ const unitContent = content.substring(unitStart);
125
+
126
+ try {
127
+ await Bun.write(servicePath, unitContent);
128
+ } catch {
129
+ return "Failed to create the service file. Please ensure you have sufficient permissions (try running with sudo).";
130
+ }
131
+
132
+ // Bun.spawn(["sudo", "systemctl", "daemon-reload"], { stdout: "inherit" }).exited;
133
+ // Bun.spawn(["sudo", "systemctl", "enable", "bm2"], { stdout: "inherit" }).exited;
134
+
135
+ await $`systemctl daemon-reload`;
136
+ await $`systemctl enable bm2`;
137
+ await $`systemctl start bm2`;
138
+
139
+ return `Service installed at ${servicePath}`;
127
140
  } else if (os === "darwin") {
128
141
  const plistPath = `${process.env.HOME}/Library/LaunchAgents/com.bm2.daemon.plist`;
129
142
  // Extract plist content
@@ -141,17 +154,21 @@ ${unit}`;
141
154
  const os = process.platform;
142
155
 
143
156
  if (os === "linux") {
144
- Bun.spawn(["sudo", "systemctl", "stop", "bm2"], { stdout: "inherit" });
145
- Bun.spawn(["sudo", "systemctl", "disable", "bm2"], { stdout: "inherit" });
146
- const { unlinkSync } = require("fs");
147
- try { unlinkSync("/etc/systemd/system/bm2.service"); } catch {}
148
- Bun.spawn(["sudo", "systemctl", "daemon-reload"], { stdout: "inherit" });
157
+
158
+ await $`systemctl stop bm2`;
159
+ await $`systemctl disable bm2`;
160
+
161
+ await $`rm -f /etc/systemd/system/bm2.service`;
162
+ await $`systemctl daemon-reload`;
163
+
149
164
  return "BM2 service removed";
165
+
150
166
  } else if (os === "darwin") {
167
+
151
168
  const plistPath = `${process.env.HOME}/Library/LaunchAgents/com.bm2.daemon.plist`;
152
- Bun.spawn(["launchctl", "unload", plistPath], { stdout: "inherit" });
153
- const { unlinkSync } = require("fs");
154
- try { unlinkSync(plistPath); } catch {}
169
+
170
+ await $`launchctl unload ${plistPath}`;
171
+ await $`rm -f ${plistPath}`;
155
172
  return "BM2 launch agent removed";
156
173
  }
157
174