bs9 1.4.3 → 1.4.6

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.
@@ -0,0 +1,255 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { Command } from "commander";
4
+ import { readFileSync } from "node:fs";
5
+ import { join, dirname } from "node:path";
6
+
7
+ // Read version from package.json
8
+ const packageJsonPath = join(dirname(import.meta.path), '..', 'package.json');
9
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
10
+ const version = packageJson.version;
11
+
12
+ import { startCommand } from "../src/commands/start.js";
13
+ import { stopCommand } from "../src/commands/stop.js";
14
+ import { restartCommand } from "../src/commands/restart.js";
15
+ import { statusCommand } from "../src/commands/status.js";
16
+ import { logsCommand } from "../src/commands/logs.js";
17
+ import { monitCommand } from "../src/commands/monit.js";
18
+ import { webCommand } from "../src/commands/web.js";
19
+ import { alertCommand } from "../src/commands/alert.js";
20
+ import { exportCommand } from "../src/commands/export.js";
21
+ import { depsCommand } from "../src/commands/deps.js";
22
+ import { profileCommand } from "../src/commands/profile.js";
23
+ import { deleteCommand } from "../src/commands/delete.js";
24
+ import { saveCommand } from "../src/commands/save.js";
25
+ import { resurrectCommand } from "../src/commands/resurrect.js";
26
+ import { deployCommand } from "../src/commands/deploy.js";
27
+ import { loadbalancerCommand } from "../src/loadbalancer/manager.js";
28
+ import { windowsCommand } from "../src/windows/service.js";
29
+ import { launchdCommand } from "../src/macos/launchd.js";
30
+ import { updateCommand } from "../src/commands/update.js";
31
+ import { dbpoolCommand } from "../src/database/pool.js";
32
+ import { advancedMonitoringCommand } from "../src/monitoring/advanced.js";
33
+ import { consulCommand } from "../src/discovery/consul.js";
34
+ import { doctorCommand } from "../src/commands/doctor.js";
35
+
36
+ const program = new Command();
37
+
38
+ program
39
+ .name("bs9")
40
+ .description("BS9 (Bun Sentinel 9) — Mission-critical process manager CLI")
41
+ .version(version);
42
+
43
+ program
44
+ .command("start")
45
+ .description("Start a process with hardened systemd unit")
46
+ .argument("<file>", "Application file to start")
47
+ .option("-n, --name <name>", "Service name")
48
+ .option("-p, --port <port>", "Port number", "3000")
49
+ .option("-h, --host <host>", "Host address", "localhost")
50
+ .option("--https", "Use HTTPS protocol")
51
+ .option("-e, --env <env>", "Environment variables (can be used multiple times)", (value, previous) => [...(previous || []), value])
52
+ .option("--otel", "Enable OpenTelemetry instrumentation", true)
53
+ .option("--prometheus", "Enable Prometheus metrics", true)
54
+ .option("--build", "Build TypeScript to JavaScript before starting")
55
+ .action(startCommand);
56
+
57
+ program
58
+ .command("stop")
59
+ .description("Stop a managed service")
60
+ .argument("<name>", "Service name")
61
+ .action(stopCommand);
62
+
63
+ program
64
+ .command("restart")
65
+ .description("Restart a managed service")
66
+ .argument("<name>", "Service name")
67
+ .action(restartCommand);
68
+
69
+ program
70
+ .command("status")
71
+ .description("Show status and SRE metrics for all services")
72
+ .option("-w, --watch", "Watch mode (refresh every 2s)")
73
+ .action(statusCommand);
74
+
75
+ program
76
+ .command("logs")
77
+ .description("Show logs for a service (via journalctl)")
78
+ .argument("<name>", "Service name")
79
+ .option("-f, --follow", "Follow logs")
80
+ .option("-n, --lines <number>", "Number of lines", "50")
81
+ .action(logsCommand);
82
+
83
+ program
84
+ .command("monit")
85
+ .description("Real-time terminal dashboard for all services")
86
+ .option("-r, --refresh <seconds>", "Refresh interval in seconds", "2")
87
+ .action(monitCommand);
88
+
89
+ program
90
+ .command("web")
91
+ .description("Start web-based monitoring dashboard")
92
+ .option("-p, --port <port>", "Port for web dashboard", "8080")
93
+ .option("-d, --detach", "Run in background")
94
+ .action(webCommand);
95
+
96
+ program
97
+ .command("alert")
98
+ .description("Configure alert thresholds and webhooks")
99
+ .option("--enable", "Enable alerts")
100
+ .option("--disable", "Disable alerts")
101
+ .option("--webhook <url>", "Set webhook URL for alerts")
102
+ .option("--cpu <percentage>", "CPU threshold percentage")
103
+ .option("--memory <percentage>", "Memory threshold percentage")
104
+ .option("--errorRate <percentage>", "Error rate threshold percentage")
105
+ .option("--uptime <percentage>", "Uptime threshold percentage")
106
+ .option("--cooldown <seconds>", "Alert cooldown period in seconds")
107
+ .option("--service <name>", "Configure alerts for specific service")
108
+ .option("--list", "List current alert configuration")
109
+ .option("--test", "Test webhook connectivity")
110
+ .action(alertCommand);
111
+
112
+ program
113
+ .command("export")
114
+ .description("Export historical metrics data")
115
+ .option("-f, --format <format>", "Export format (json|csv)", "json")
116
+ .option("-h, --hours <hours>", "Hours of data to export", "24")
117
+ .option("-o, --output <file>", "Output file path")
118
+ .option("-s, --service <name>", "Export specific service metrics")
119
+ .action(exportCommand);
120
+
121
+ program
122
+ .command("deps")
123
+ .description("Visualize service dependencies")
124
+ .option("-f, --format <format>", "Output format (text|dot|json)", "text")
125
+ .option("-o, --output <file>", "Output file path")
126
+ .action(depsCommand);
127
+
128
+ program
129
+ .command("profile")
130
+ .description("Performance profiling for services")
131
+ .option("-d, --duration <seconds>", "Profiling duration", "60")
132
+ .option("-i, --interval <ms>", "Sampling interval", "1000")
133
+ .option("-s, --service <name>", "Service name to profile")
134
+ .option("--flamegraph", "Generate flame graph")
135
+ .option("--top <number>", "Show top N functions", "10")
136
+ .action(profileCommand);
137
+
138
+ program
139
+ .command("delete")
140
+ .description("Delete managed services")
141
+ .argument("[name]", "Service name (optional)")
142
+ .option("-a, --all", "Delete all services")
143
+ .option("-f, --force", "Force deletion without errors")
144
+ .option("-r, --remove", "Remove service configuration files")
145
+ .option("-t, --timeout <seconds>", "Timeout for graceful shutdown (default: 30)")
146
+ .action(deleteCommand);
147
+
148
+ program
149
+ .command("save")
150
+ .description("Save service configurations to backup")
151
+ .argument("[name]", "Service name (optional)")
152
+ .option("-a, --all", "Save all services")
153
+ .option("-f, --force", "Force save without errors")
154
+ .option("-b, --backup", "Create timestamped backup")
155
+ .action(saveCommand);
156
+
157
+ program
158
+ .command("resurrect")
159
+ .description("Resurrect services from backup")
160
+ .argument("[name]", "Service name (optional)")
161
+ .option("-a, --all", "Resurrect all services")
162
+ .option("-f, --force", "Force resurrection without errors")
163
+ .option("-c, --config <file>", "Configuration file to use")
164
+ .action(resurrectCommand);
165
+
166
+ program
167
+ .command("deploy")
168
+ .description("🚀 Deploy applications with zero-config production setup")
169
+ .argument("<file>", "Application file to deploy")
170
+ .option("-n, --name <name>", "Service name")
171
+ .option("-p, --port <port>", "Port number", "3000")
172
+ .option("-h, --host <host>", "Host address", "localhost")
173
+ .option("-e, --env <env>", "Environment variables (multiple)", (value, previous) => [...(previous || []), value])
174
+ .option("-i, --instances <number>", "Number of instances")
175
+ .option("--memory <memory>", "Memory limit")
176
+ .option("--cpu <cpu>", "CPU limit")
177
+ .option("--log-level <level>", "Log level")
178
+ .option("--log-file <file>", "Log file path")
179
+ .option("--restart <policy>", "Restart policy")
180
+ .option("--no-health", "Skip health check")
181
+ .option("--no-metrics", "Disable metrics")
182
+ .option("--no-otel", "Disable OpenTelemetry")
183
+ .option("--no-prometheus", "Disable Prometheus")
184
+ .option("--https", "Enable HTTPS")
185
+ .option("--no-linger", "Don't enable linger")
186
+ .option("-f, --force", "Force deployment")
187
+ .option("--reload", "Reload existing service")
188
+ .option("--build", "Build TypeScript")
189
+ .action(deployCommand);
190
+
191
+ program
192
+ .command("loadbalancer")
193
+ .description("Load balancer management")
194
+ .argument("<action>", "Action to perform")
195
+ .option("-p, --port <port>", "Load balancer port", "8080")
196
+ .option("-a, --algorithm <type>", "Load balancing algorithm", "round-robin")
197
+ .option("-b, --backends <list>", "Backend servers (host:port,host:port)")
198
+ .option("--health-check", "Enable health checking", true)
199
+ .option("--health-path <path>", "Health check path", "/healthz")
200
+ .option("--health-interval <ms>", "Health check interval", "10000")
201
+ .action(loadbalancerCommand);
202
+
203
+ program
204
+ .command("dbpool")
205
+ .description("Database connection pool management")
206
+ .argument("<action>", "Action to perform")
207
+ .option("--host <host>", "Database host", "localhost")
208
+ .option("--port <port>", "Database port", "5432")
209
+ .option("--database <name>", "Database name", "testdb")
210
+ .option("--username <user>", "Database username", "user")
211
+ .option("--password <pass>", "Database password", "password")
212
+ .option("--max-connections <num>", "Maximum connections", "10")
213
+ .option("--min-connections <num>", "Minimum connections", "2")
214
+ .option("--concurrency <num>", "Test concurrency", "10")
215
+ .option("--iterations <num>", "Test iterations", "100")
216
+ .action(dbpoolCommand);
217
+
218
+ program
219
+ .command("update")
220
+ .description("Update BS9 to latest version")
221
+ .option("--check", "Check for updates without installing")
222
+ .option("--force", "Force update even if already latest")
223
+ .option("--rollback", "Rollback to previous version")
224
+ .option("--version <version>", "Update to specific version")
225
+ .action(updateCommand);
226
+
227
+ program
228
+ .command("advanced")
229
+ .description("Advanced monitoring dashboard")
230
+ .option("--port <port>", "Dashboard port", "8090")
231
+ .action(advancedMonitoringCommand);
232
+
233
+ program
234
+ .command("consul")
235
+ .description("Consul service discovery")
236
+ .argument("<action>", "Action to perform")
237
+ .option("--consul-url <url>", "Consul URL", "http://localhost:8500")
238
+ .option("--name <name>", "Service name")
239
+ .option("--id <id>", "Service ID")
240
+ .option("--address <address>", "Service address")
241
+ .option("--port <port>", "Service port")
242
+ .option("--tags <tags>", "Service tags (comma-separated)")
243
+ .option("--health-check <url>", "Health check URL")
244
+ .option("--meta <json>", "Service metadata (JSON)")
245
+ .option("--service <service>", "Service name for discovery")
246
+ .action(consulCommand);
247
+
248
+ program
249
+ .command("doctor")
250
+ .description("Perform comprehensive health check")
251
+ .option("--check <type>", "Specific check to run (dependencies|configuration|platform)")
252
+ .option("-v, --verbose", "Verbose output with system information")
253
+ .action(doctorCommand);
254
+
255
+ program.parse();
package/dist/bs9.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // @bun
2
2
  // bin/bs9
3
- var bs9_default = "./bs9-bbkpq14n.";
3
+ var bs9_default = "./bs9-nfpea7ta.";
4
4
  export {
5
5
  bs9_default as default
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bs9",
3
- "version": "1.4.3",
3
+ "version": "1.4.6",
4
4
  "description": "Bun Sentinel 9 - High-performance, non-root process manager for Bun",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -237,12 +237,6 @@ function showDeploymentSummary(serviceName: string, options: DeployOptions): voi
237
237
  console.log(` bs9 delete ${serviceName}`);
238
238
  console.log(` bs9 save ${serviceName}`);
239
239
 
240
- console.log("\n🌟 KILLER FEATURES:");
241
- console.log(" ✅ Zero-config systemd setup");
242
- console.log(" ✅ Built-in health checks & restart policies");
243
- console.log(" ✅ Hot reload with environment updates");
244
- console.log(" ✅ Automatic metrics & observability");
245
- console.log(" ✅ One-command deployment");
246
-
240
+
247
241
  console.log("\n🚀 Your service is now running in production mode!");
248
242
  }
@@ -0,0 +1,332 @@
1
+ #!/usr/bin/env bun
2
+
3
+ /**
4
+ * BS9 - Bun Sentinel 9
5
+ * High-performance, non-root process manager for Bun
6
+ *
7
+ * Copyright (c) 2026 BS9 (Bun Sentinel 9)
8
+ * Licensed under the MIT License
9
+ * https://github.com/xarhang/bs9
10
+ */
11
+
12
+ import { execSync } from "node:child_process";
13
+ import { existsSync, readFileSync } from "node:fs";
14
+ import { join } from "node:path";
15
+ import { getPlatformInfo } from "../platform/detect.js";
16
+
17
+ interface DoctorOptions {
18
+ check?: string;
19
+ verbose?: boolean;
20
+ }
21
+
22
+ interface HealthCheck {
23
+ name: string;
24
+ status: "✅ PASS" | "❌ FAIL" | "⚠️ WARN";
25
+ message: string;
26
+ details?: string;
27
+ }
28
+
29
+ export async function doctorCommand(options: DoctorOptions): Promise<void> {
30
+ console.log("🏥 BS9 Health Check & Diagnostics");
31
+ console.log("=".repeat(80));
32
+
33
+ const checks: HealthCheck[] = [];
34
+ const platformInfo = getPlatformInfo();
35
+
36
+ // Basic installation checks
37
+ checks.push(checkBunInstallation());
38
+ checks.push(checkBS9Installation());
39
+ checks.push(checkPlatformDetection(platformInfo));
40
+
41
+ // Directory structure checks
42
+ checks.push(checkDirectoryStructure(platformInfo));
43
+ checks.push(checkPermissions(platformInfo));
44
+
45
+ // Service manager checks
46
+ checks.push(checkServiceManager(platformInfo));
47
+
48
+ // Network and connectivity checks
49
+ checks.push(checkNetworkConnectivity());
50
+
51
+ // Optional checks based on flags
52
+ if (options.check) {
53
+ const specificChecks = runSpecificCheck(options.check, platformInfo);
54
+ checks.push(...specificChecks);
55
+ }
56
+
57
+ if (options.verbose) {
58
+ checks.push(checkSystemResources());
59
+ checks.push(checkDependencies());
60
+ }
61
+
62
+ // Display results
63
+ displayResults(checks);
64
+
65
+ // Summary
66
+ const passed = checks.filter(c => c.status === "✅ PASS").length;
67
+ const failed = checks.filter(c => c.status === "❌ FAIL").length;
68
+ const warnings = checks.filter(c => c.status === "⚠️ WARN").length;
69
+
70
+ console.log("\n" + "=".repeat(80));
71
+ console.log(`📊 Health Check Summary:`);
72
+ console.log(` ✅ Passed: ${passed}`);
73
+ console.log(` ❌ Failed: ${failed}`);
74
+ console.log(` ⚠️ Warnings: ${warnings}`);
75
+ console.log(` 📈 Total: ${checks.length}`);
76
+
77
+ if (failed > 0) {
78
+ console.log(`\n❌ Health check FAILED with ${failed} error(s)`);
79
+ console.log("💡 Run 'bs9 doctor --verbose' for more details");
80
+ process.exit(1);
81
+ } else {
82
+ console.log(`\n✅ Health check PASSED - BS9 is ready to use!`);
83
+ }
84
+ }
85
+
86
+ function checkBunInstallation(): HealthCheck {
87
+ try {
88
+ const version = execSync("bun --version", { encoding: "utf-8" }).trim();
89
+ return {
90
+ name: "Bun Installation",
91
+ status: "✅ PASS",
92
+ message: `Bun v${version} installed`,
93
+ details: `Runtime: ${version}`
94
+ };
95
+ } catch {
96
+ return {
97
+ name: "Bun Installation",
98
+ status: "❌ FAIL",
99
+ message: "Bun is not installed or not in PATH",
100
+ details: "Install Bun from https://bun.sh"
101
+ };
102
+ }
103
+ }
104
+
105
+ function checkBS9Installation(): HealthCheck {
106
+ try {
107
+ const version = execSync("bs9 --version", { encoding: "utf-8" }).trim();
108
+ return {
109
+ name: "BS9 Installation",
110
+ status: "✅ PASS",
111
+ message: `BS9 ${version} installed`,
112
+ details: `CLI: ${version}`
113
+ };
114
+ } catch {
115
+ return {
116
+ name: "BS9 Installation",
117
+ status: "❌ FAIL",
118
+ message: "BS9 is not installed or not in PATH",
119
+ details: "Run 'npm install -g bs9' or install from source"
120
+ };
121
+ }
122
+ }
123
+
124
+ function checkPlatformDetection(platformInfo: any): HealthCheck {
125
+ return {
126
+ name: "Platform Detection",
127
+ status: "✅ PASS",
128
+ message: `Detected ${platformInfo.platform}`,
129
+ details: `OS: ${platformInfo.platform}, Service Manager: ${platformInfo.serviceManager}`
130
+ };
131
+ }
132
+
133
+ function checkDirectoryStructure(platformInfo: any): HealthCheck {
134
+ const requiredDirs = [
135
+ platformInfo.configDir,
136
+ platformInfo.logDir,
137
+ platformInfo.serviceDir
138
+ ];
139
+
140
+ const missingDirs = requiredDirs.filter(dir => !existsSync(dir));
141
+
142
+ if (missingDirs.length === 0) {
143
+ return {
144
+ name: "Directory Structure",
145
+ status: "✅ PASS",
146
+ message: "All required directories exist",
147
+ details: `Config: ${platformInfo.configDir}`
148
+ };
149
+ } else {
150
+ return {
151
+ name: "Directory Structure",
152
+ status: "❌ FAIL",
153
+ message: `Missing ${missingDirs.length} directories`,
154
+ details: `Missing: ${missingDirs.join(", ")}`
155
+ };
156
+ }
157
+ }
158
+
159
+ function checkPermissions(platformInfo: any): HealthCheck {
160
+ try {
161
+ // Test write permissions
162
+ const testFile = join(platformInfo.configDir, ".bs9-test");
163
+ execSync(`touch "${testFile}"`, { stdio: "ignore" });
164
+ execSync(`rm "${testFile}"`, { stdio: "ignore" });
165
+
166
+ return {
167
+ name: "File Permissions",
168
+ status: "✅ PASS",
169
+ message: "Write permissions OK",
170
+ details: `Can write to ${platformInfo.configDir}`
171
+ };
172
+ } catch {
173
+ return {
174
+ name: "File Permissions",
175
+ status: "❌ FAIL",
176
+ message: "Insufficient file permissions",
177
+ details: `Cannot write to ${platformInfo.configDir}`
178
+ };
179
+ }
180
+ }
181
+
182
+ function checkServiceManager(platformInfo: any): HealthCheck {
183
+ try {
184
+ switch (platformInfo.platform) {
185
+ case "linux":
186
+ execSync("systemctl --user --version", { stdio: "ignore" });
187
+ return {
188
+ name: "Service Manager",
189
+ status: "✅ PASS",
190
+ message: "systemd user services available",
191
+ details: "systemd user mode is working"
192
+ };
193
+
194
+ case "darwin":
195
+ execSync("launchctl list", { stdio: "ignore" });
196
+ return {
197
+ name: "Service Manager",
198
+ status: "✅ PASS",
199
+ message: "launchd available",
200
+ details: "macOS launchd is working"
201
+ };
202
+
203
+ case "win32":
204
+ execSync("sc query", { stdio: "ignore" });
205
+ return {
206
+ name: "Service Manager",
207
+ status: "✅ PASS",
208
+ message: "Windows Services available",
209
+ details: "Windows Service Manager is working"
210
+ };
211
+
212
+ default:
213
+ return {
214
+ name: "Service Manager",
215
+ status: "⚠️ WARN",
216
+ message: "Unsupported platform",
217
+ details: `Platform ${platformInfo.platform} may have limited support`
218
+ };
219
+ }
220
+ } catch {
221
+ return {
222
+ name: "Service Manager",
223
+ status: "❌ FAIL",
224
+ message: "Service manager not available",
225
+ details: "Cannot access system service manager"
226
+ };
227
+ }
228
+ }
229
+
230
+ function checkNetworkConnectivity(): HealthCheck {
231
+ try {
232
+ // Test basic network connectivity
233
+ execSync("curl -s --connect-timeout 3 http://httpbin.org/ip", { stdio: "ignore" });
234
+ return {
235
+ name: "Network Connectivity",
236
+ status: "✅ PASS",
237
+ message: "Network connectivity OK",
238
+ details: "Can reach external services"
239
+ };
240
+ } catch {
241
+ return {
242
+ name: "Network Connectivity",
243
+ status: "⚠️ WARN",
244
+ message: "Limited network connectivity",
245
+ details: "Cannot reach external services (may be offline)"
246
+ };
247
+ }
248
+ }
249
+
250
+ function checkSystemResources(): HealthCheck {
251
+ try {
252
+ const memory = execSync("free -h", { encoding: "utf-8" });
253
+ const disk = execSync("df -h .", { encoding: "utf-8" });
254
+
255
+ return {
256
+ name: "System Resources",
257
+ status: "✅ PASS",
258
+ message: "System resources OK",
259
+ details: `Memory and disk space available`
260
+ };
261
+ } catch {
262
+ return {
263
+ name: "System Resources",
264
+ status: "⚠️ WARN",
265
+ message: "Cannot check system resources",
266
+ details: "Resource monitoring not available"
267
+ };
268
+ }
269
+ }
270
+
271
+ function checkDependencies(): HealthCheck {
272
+ try {
273
+ const packageJson = JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8"));
274
+ const deps = Object.keys(packageJson.dependencies || {});
275
+
276
+ return {
277
+ name: "Dependencies",
278
+ status: "✅ PASS",
279
+ message: `${deps.length} dependencies found`,
280
+ details: `Dependencies: ${deps.slice(0, 3).join(", ")}${deps.length > 3 ? "..." : ""}`
281
+ };
282
+ } catch {
283
+ return {
284
+ name: "Dependencies",
285
+ status: "⚠️ WARN",
286
+ message: "No package.json found",
287
+ details: "Not in a Node.js project directory"
288
+ };
289
+ }
290
+ }
291
+
292
+ function runSpecificCheck(check: string, platformInfo: any): HealthCheck[] {
293
+ const checks: HealthCheck[] = [];
294
+
295
+ switch (check.toLowerCase()) {
296
+ case "dependencies":
297
+ checks.push(checkDependencies());
298
+ break;
299
+
300
+ case "configuration":
301
+ checks.push(checkDirectoryStructure(platformInfo));
302
+ checks.push(checkPermissions(platformInfo));
303
+ break;
304
+
305
+ case "platform":
306
+ checks.push(checkPlatformDetection(platformInfo));
307
+ checks.push(checkServiceManager(platformInfo));
308
+ break;
309
+
310
+ default:
311
+ checks.push({
312
+ name: "Specific Check",
313
+ status: "❌ FAIL",
314
+ message: `Unknown check: ${check}`,
315
+ details: "Available checks: dependencies, configuration, platform"
316
+ });
317
+ }
318
+
319
+ return checks;
320
+ }
321
+
322
+ function displayResults(checks: HealthCheck[]): void {
323
+ console.log("\n🔍 Health Check Results:");
324
+ console.log("-".repeat(80));
325
+
326
+ for (const check of checks) {
327
+ console.log(`${check.status} ${check.name}: ${check.message}`);
328
+ if (check.details) {
329
+ console.log(` 📋 ${check.details}`);
330
+ }
331
+ }
332
+ }