bs9 1.4.0 → 1.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bs9",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
4
4
  "description": "Bun Sentinel 9 - High-performance, non-root process manager for Bun",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -27,6 +27,7 @@ interface ServiceStatus {
27
27
  memory?: string;
28
28
  uptime?: string;
29
29
  tasks?: string;
30
+ pid?: string;
30
31
  }
31
32
 
32
33
  export async function statusCommand(options: StatusOptions): Promise<void> {
@@ -93,20 +94,46 @@ async function getLinuxServices(): Promise<ServiceStatus[]> {
93
94
  description,
94
95
  };
95
96
 
96
- // Get additional metrics
97
+ // Get additional metrics with better error handling
97
98
  try {
98
- const showOutput = execSync(`systemctl --user show ${name} -p CPUUsageNSec MemoryCurrent ActiveEnterTimestamp TasksCurrent`, { encoding: "utf-8" });
99
+ // Get comprehensive service information
100
+ const showOutput = execSync(`systemctl --user show ${name}`, { encoding: "utf-8" });
101
+
102
+ // Extract CPU usage
99
103
  const cpuMatch = showOutput.match(/CPUUsageNSec=(\d+)/);
104
+ if (cpuMatch) {
105
+ status.cpu = formatCPU(Number(cpuMatch[1]));
106
+ }
107
+
108
+ // Extract memory usage
100
109
  const memMatch = showOutput.match(/MemoryCurrent=(\d+)/);
110
+ if (memMatch) {
111
+ const memoryBytes = Number(memMatch[1]);
112
+ status.memory = formatMemory(memoryBytes);
113
+ }
114
+
115
+ // Extract startup time and calculate uptime
101
116
  const timeMatch = showOutput.match(/ActiveEnterTimestamp=(.+)/);
117
+ if (timeMatch) {
118
+ status.uptime = formatUptime(timeMatch[1]);
119
+ }
120
+
121
+ // Extract task count
102
122
  const tasksMatch = showOutput.match(/TasksCurrent=(\d+)/);
123
+ if (tasksMatch) {
124
+ status.tasks = tasksMatch[1];
125
+ }
126
+
127
+ // Get process ID for additional info
128
+ const pidMatch = showOutput.match(/MainPID=(\d+)/);
129
+ if (pidMatch && pidMatch[1] !== "0") {
130
+ // We could get more detailed process info here if needed
131
+ status.pid = pidMatch[1];
132
+ }
103
133
 
104
- if (cpuMatch) status.cpu = formatCPU(Number(cpuMatch[1]));
105
- if (memMatch) status.memory = formatMemory(Number(memMatch[1]));
106
- if (timeMatch) status.uptime = formatUptime(timeMatch[1]);
107
- if (tasksMatch) status.tasks = tasksMatch[1];
108
- } catch {
109
- // Metrics might not be available
134
+ } catch (metricsError: any) {
135
+ // If metrics fail, at least we have basic status
136
+ console.warn(`⚠️ Could not get metrics for ${name}: ${metricsError?.message || metricsError}`);
110
137
  }
111
138
 
112
139
  services.push(status);
@@ -145,25 +172,84 @@ async function getWindowsServices(): Promise<ServiceStatus[]> {
145
172
  }
146
173
 
147
174
  function displayServices(services: ServiceStatus[]): void {
148
- // Header
149
- console.log(`${"SERVICE".padEnd(20)} ${"STATE".padEnd(12)} ${"CPU".padEnd(8)} ${"MEMORY".padEnd(10)} ${"UPTIME".padEnd(12)} ${"TASKS".padEnd(6)} DESCRIPTION`);
150
- console.log("-".repeat(90));
175
+ if (services.length === 0) {
176
+ console.log("📋 No BS9 services found");
177
+ console.log("💡 Use 'bs9 start <file>' to create a service");
178
+ return;
179
+ }
180
+
181
+ // Header with better formatting
182
+ console.log(`${"SERVICE".padEnd(18)} ${"STATUS".padEnd(15)} ${"CPU".padEnd(10)} ${"MEMORY".padEnd(12)} ${"UPTIME".padEnd(12)} ${"TASKS".padEnd(8)} DESCRIPTION`);
183
+ console.log("─".repeat(100));
151
184
 
152
- for (const svc of services) {
153
- const state = `${svc.active}/${svc.sub}`;
185
+ // Sort services by status (running first, then by name)
186
+ const sortedServices = services.sort((a, b) => {
187
+ const aRunning = a.active === "active" && a.sub === "running";
188
+ const bRunning = b.active === "active" && b.sub === "running";
189
+ if (aRunning !== bRunning) return bRunning ? 1 : -1;
190
+ return a.name.localeCompare(b.name);
191
+ });
192
+
193
+ for (const svc of sortedServices) {
194
+ // Better status formatting with colors/indicators
195
+ let statusIndicator = "";
196
+ let status = `${svc.active}/${svc.sub}`;
197
+
198
+ if (svc.active === "active" && svc.sub === "running") {
199
+ statusIndicator = "✅";
200
+ status = "running";
201
+ } else if (svc.active === "activating" && svc.sub.includes("auto-restart")) {
202
+ statusIndicator = "🔄";
203
+ status = "restarting";
204
+ } else if (svc.active === "failed" || svc.sub === "failed") {
205
+ statusIndicator = "❌";
206
+ status = "failed";
207
+ } else if (svc.active === "inactive") {
208
+ statusIndicator = "⏸️";
209
+ status = "stopped";
210
+ } else {
211
+ statusIndicator = "⚠️";
212
+ }
213
+
214
+ const displayStatus = `${statusIndicator} ${status}`;
215
+
154
216
  console.log(
155
- `${svc.name.padEnd(20)} ${state.padEnd(12)} ${(svc.cpu || "-").padEnd(8)} ${(svc.memory || "-").padEnd(10)} ${(svc.uptime || "-").padEnd(12)} ${(svc.tasks || "-").padEnd(6)} ${svc.description}`
217
+ `${svc.name.padEnd(18)} ${displayStatus.padEnd(15)} ${(svc.cpu || "-").padEnd(10)} ${(svc.memory || "-").padEnd(12)} ${(svc.uptime || "-").padEnd(12)} ${(svc.tasks || "-").padEnd(8)} ${svc.description}`
156
218
  );
157
219
  }
158
220
 
159
- console.log("\n📊 SRE Metrics Summary:");
221
+ // Enhanced summary
222
+ console.log("\n📊 Service Summary:");
160
223
  const totalServices = services.length;
161
- const runningServices = services.filter(s => s.active === "active").length;
224
+ const runningServices = services.filter(s => s.active === "active" && s.sub === "running").length;
225
+ const failedServices = services.filter(s => s.active === "failed" || s.sub === "failed").length;
226
+ const restartingServices = services.filter(s => s.active === "activating" && s.sub.includes("auto-restart")).length;
162
227
  const totalMemory = services.reduce((sum, s) => sum + (s.memory ? parseMemory(s.memory) : 0), 0);
163
228
 
164
- console.log(` Services: ${runningServices}/${totalServices} running`);
165
- console.log(` Memory: ${formatMemory(totalMemory)}`);
166
- console.log(` Last updated: ${new Date().toISOString()}`);
229
+ console.log(` 📈 Status: ${runningServices} running, ${failedServices} failed, ${restartingServices} restarting`);
230
+ console.log(` 📦 Total: ${runningServices}/${totalServices} services running`);
231
+ console.log(` 💾 Memory: ${formatMemory(totalMemory)}`);
232
+ console.log(` 🕒 Last updated: ${new Date().toLocaleString()}`);
233
+
234
+ // Show failed services details
235
+ if (failedServices > 0) {
236
+ console.log("\n❌ Failed Services:");
237
+ const failed = services.filter(s => s.active === "failed" || s.sub === "failed");
238
+ for (const svc of failed) {
239
+ console.log(` • ${svc.name}: ${svc.active}/${svc.sub}`);
240
+ console.log(` 💡 Try: bs9 logs ${svc.name} --tail 20`);
241
+ }
242
+ }
243
+
244
+ // Show restarting services details
245
+ if (restartingServices > 0) {
246
+ console.log("\n🔄 Restarting Services:");
247
+ const restarting = services.filter(s => s.active === "activating" && s.sub.includes("auto-restart"));
248
+ for (const svc of restarting) {
249
+ console.log(` • ${svc.name}: ${svc.active}/${svc.sub}`);
250
+ console.log(` 💡 Try: bs9 logs ${svc.name} --tail 20`);
251
+ }
252
+ }
167
253
  }
168
254
 
169
255
  function formatCPU(nsec: number): string {