bs9 1.3.6 → 1.3.7

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/bin/bs9 CHANGED
@@ -20,6 +20,7 @@ import { alertCommand } from "../src/commands/alert.js";
20
20
  import { exportCommand } from "../src/commands/export.js";
21
21
  import { depsCommand } from "../src/commands/deps.js";
22
22
  import { profileCommand } from "../src/commands/profile.js";
23
+ import { deleteCommand } from "../src/commands/delete.js";
23
24
  import { loadbalancerCommand } from "../src/loadbalancer/manager.js";
24
25
  import { windowsCommand } from "../src/windows/service.js";
25
26
  import { launchdCommand } from "../src/macos/launchd.js";
@@ -126,9 +127,20 @@ program
126
127
  .option("-d, --duration <seconds>", "Profiling duration", "60")
127
128
  .option("-i, --interval <ms>", "Sampling interval", "1000")
128
129
  .option("-s, --service <name>", "Service name to profile")
129
- .option("-o, --output <file>", "Output file path")
130
+ .option("--flamegraph", "Generate flame graph")
131
+ .option("--top <number>", "Show top N functions", "10")
130
132
  .action(profileCommand);
131
133
 
134
+ program
135
+ .command("delete")
136
+ .description("Delete managed services")
137
+ .argument("[name]", "Service name (optional)")
138
+ .option("-a, --all", "Delete all services")
139
+ .option("-f, --force", "Force deletion without errors")
140
+ .option("-r, --remove", "Remove service configuration files")
141
+ .option("-t, --timeout <seconds>", "Timeout for graceful shutdown", "30")
142
+ .action(deleteCommand);
143
+
132
144
  program
133
145
  .command("loadbalancer")
134
146
  .description("Load balancer management")
@@ -0,0 +1,201 @@
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 { loadbalancerCommand } from "../src/loadbalancer/manager.js";
25
+ import { windowsCommand } from "../src/windows/service.js";
26
+ import { launchdCommand } from "../src/macos/launchd.js";
27
+ import { updateCommand } from "../src/commands/update.js";
28
+ import { dbpoolCommand } from "../src/database/pool.js";
29
+ import { advancedMonitoringCommand } from "../src/monitoring/advanced.js";
30
+ import { consulCommand } from "../src/discovery/consul.js";
31
+
32
+ const program = new Command();
33
+
34
+ program
35
+ .name("bs9")
36
+ .description("BS9 (Bun Sentinel 9) — Mission-critical process manager CLI")
37
+ .version(version);
38
+
39
+ program
40
+ .command("start")
41
+ .description("Start a process with hardened systemd unit")
42
+ .argument("<file>", "Application file to start")
43
+ .option("-n, --name <name>", "Service name")
44
+ .option("-p, --port <port>", "Port number", "3000")
45
+ .option("-h, --host <host>", "Host address", "localhost")
46
+ .option("--https", "Use HTTPS protocol")
47
+ .option("-e, --env <env>", "Environment variables (can be used multiple times)", (value, previous) => [...(previous || []), value])
48
+ .option("--otel", "Enable OpenTelemetry instrumentation", true)
49
+ .option("--prometheus", "Enable Prometheus metrics", true)
50
+ .option("--build", "Build TypeScript to JavaScript before starting")
51
+ .action(startCommand);
52
+
53
+ program
54
+ .command("stop")
55
+ .description("Stop a managed service")
56
+ .argument("<name>", "Service name")
57
+ .action(stopCommand);
58
+
59
+ program
60
+ .command("restart")
61
+ .description("Restart a managed service")
62
+ .argument("<name>", "Service name")
63
+ .action(restartCommand);
64
+
65
+ program
66
+ .command("status")
67
+ .description("Show status and SRE metrics for all services")
68
+ .option("-w, --watch", "Watch mode (refresh every 2s)")
69
+ .action(statusCommand);
70
+
71
+ program
72
+ .command("logs")
73
+ .description("Show logs for a service (via journalctl)")
74
+ .argument("<name>", "Service name")
75
+ .option("-f, --follow", "Follow logs")
76
+ .option("-n, --lines <number>", "Number of lines", "50")
77
+ .action(logsCommand);
78
+
79
+ program
80
+ .command("monit")
81
+ .description("Real-time terminal dashboard for all services")
82
+ .option("-r, --refresh <seconds>", "Refresh interval in seconds", "2")
83
+ .action(monitCommand);
84
+
85
+ program
86
+ .command("web")
87
+ .description("Start web-based monitoring dashboard")
88
+ .option("-p, --port <port>", "Port for web dashboard", "8080")
89
+ .option("-d, --detach", "Run in background")
90
+ .action(webCommand);
91
+
92
+ program
93
+ .command("alert")
94
+ .description("Configure alert thresholds and webhooks")
95
+ .option("--enable", "Enable alerts")
96
+ .option("--disable", "Disable alerts")
97
+ .option("--webhook <url>", "Set webhook URL for alerts")
98
+ .option("--cpu <percentage>", "CPU threshold percentage")
99
+ .option("--memory <percentage>", "Memory threshold percentage")
100
+ .option("--errorRate <percentage>", "Error rate threshold percentage")
101
+ .option("--uptime <percentage>", "Uptime threshold percentage")
102
+ .option("--cooldown <seconds>", "Alert cooldown period in seconds")
103
+ .option("--service <name>", "Configure alerts for specific service")
104
+ .option("--list", "List current alert configuration")
105
+ .option("--test", "Test webhook connectivity")
106
+ .action(alertCommand);
107
+
108
+ program
109
+ .command("export")
110
+ .description("Export historical metrics data")
111
+ .option("-f, --format <format>", "Export format (json|csv)", "json")
112
+ .option("-h, --hours <hours>", "Hours of data to export", "24")
113
+ .option("-o, --output <file>", "Output file path")
114
+ .option("-s, --service <name>", "Export specific service metrics")
115
+ .action(exportCommand);
116
+
117
+ program
118
+ .command("deps")
119
+ .description("Visualize service dependencies")
120
+ .option("-f, --format <format>", "Output format (text|dot|json)", "text")
121
+ .option("-o, --output <file>", "Output file path")
122
+ .action(depsCommand);
123
+
124
+ program
125
+ .command("profile")
126
+ .description("Performance profiling for services")
127
+ .option("-d, --duration <seconds>", "Profiling duration", "60")
128
+ .option("-i, --interval <ms>", "Sampling interval", "1000")
129
+ .option("-s, --service <name>", "Service name to profile")
130
+ .option("--flamegraph", "Generate flame graph")
131
+ .option("--top <number>", "Show top N functions", "10")
132
+ .action(profileCommand);
133
+
134
+ program
135
+ .command("delete")
136
+ .description("Delete managed services")
137
+ .argument("[name]", "Service name (optional)")
138
+ .option("-a, --all", "Delete all services")
139
+ .option("-f, --force", "Force deletion without errors")
140
+ .option("-r, --remove", "Remove service configuration files")
141
+ .option("-t, --timeout <seconds>", "Timeout for graceful shutdown", "30")
142
+ .action(deleteCommand);
143
+
144
+ program
145
+ .command("loadbalancer")
146
+ .description("Load balancer management")
147
+ .argument("<action>", "Action to perform")
148
+ .option("-p, --port <port>", "Load balancer port", "8080")
149
+ .option("-a, --algorithm <type>", "Load balancing algorithm", "round-robin")
150
+ .option("-b, --backends <list>", "Backend servers (host:port,host:port)")
151
+ .option("--health-check", "Enable health checking", true)
152
+ .option("--health-path <path>", "Health check path", "/healthz")
153
+ .option("--health-interval <ms>", "Health check interval", "10000")
154
+ .action(loadbalancerCommand);
155
+
156
+ program
157
+ .command("dbpool")
158
+ .description("Database connection pool management")
159
+ .argument("<action>", "Action to perform")
160
+ .option("--host <host>", "Database host", "localhost")
161
+ .option("--port <port>", "Database port", "5432")
162
+ .option("--database <name>", "Database name", "testdb")
163
+ .option("--username <user>", "Database username", "user")
164
+ .option("--password <pass>", "Database password", "password")
165
+ .option("--max-connections <num>", "Maximum connections", "10")
166
+ .option("--min-connections <num>", "Minimum connections", "2")
167
+ .option("--concurrency <num>", "Test concurrency", "10")
168
+ .option("--iterations <num>", "Test iterations", "100")
169
+ .action(dbpoolCommand);
170
+
171
+ program
172
+ .command("update")
173
+ .description("Update BS9 to latest version")
174
+ .option("--check", "Check for updates without installing")
175
+ .option("--force", "Force update even if already latest")
176
+ .option("--rollback", "Rollback to previous version")
177
+ .option("--version <version>", "Update to specific version")
178
+ .action(updateCommand);
179
+
180
+ program
181
+ .command("advanced")
182
+ .description("Advanced monitoring dashboard")
183
+ .option("--port <port>", "Dashboard port", "8090")
184
+ .action(advancedMonitoringCommand);
185
+
186
+ program
187
+ .command("consul")
188
+ .description("Consul service discovery")
189
+ .argument("<action>", "Action to perform")
190
+ .option("--consul-url <url>", "Consul URL", "http://localhost:8500")
191
+ .option("--name <name>", "Service name")
192
+ .option("--id <id>", "Service ID")
193
+ .option("--address <address>", "Service address")
194
+ .option("--port <port>", "Service port")
195
+ .option("--tags <tags>", "Service tags (comma-separated)")
196
+ .option("--health-check <url>", "Health check URL")
197
+ .option("--meta <json>", "Service metadata (JSON)")
198
+ .option("--service <service>", "Service name for discovery")
199
+ .action(consulCommand);
200
+
201
+ program.parse();
package/dist/bs9.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // @bun
2
2
  // bin/bs9
3
- var bs9_default = "./bs9-zpqd24hj.";
3
+ var bs9_default = "./bs9-6h4w11ar.";
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.3.6",
3
+ "version": "1.3.7",
4
4
  "description": "Bun Sentinel 9 - High-performance, non-root process manager for Bun",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -0,0 +1,178 @@
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 { join } from "node:path";
14
+ import { getPlatformInfo } from "../platform/detect.js";
15
+
16
+ interface DeleteOptions {
17
+ all?: boolean;
18
+ force?: boolean;
19
+ remove?: boolean;
20
+ timeout?: string;
21
+ }
22
+
23
+ // Security: Service name validation
24
+ function isValidServiceName(name: string): boolean {
25
+ // Only allow alphanumeric, hyphens, underscores, and dots
26
+ // Prevent command injection and path traversal
27
+ const validPattern = /^[a-zA-Z0-9._-]+$/;
28
+ return validPattern.test(name) && name.length <= 64 && !name.includes('..') && !name.includes('/');
29
+ }
30
+
31
+ export async function deleteCommand(name: string, options: DeleteOptions): Promise<void> {
32
+ const platformInfo = getPlatformInfo();
33
+
34
+ // Handle delete all services
35
+ if (options.all) {
36
+ await deleteAllServices(platformInfo, options);
37
+ return;
38
+ }
39
+
40
+ // Security: Validate service name
41
+ if (!isValidServiceName(name)) {
42
+ console.error(`❌ Security: Invalid service name: ${name}`);
43
+ process.exit(1);
44
+ }
45
+
46
+ try {
47
+ if (platformInfo.isLinux) {
48
+ // Security: Use shell escaping to prevent injection
49
+ const escapedName = name.replace(/[^a-zA-Z0-9._-]/g, '');
50
+
51
+ // Stop the service first
52
+ try {
53
+ execSync(`systemctl --user stop "${escapedName}"`, { stdio: "inherit" });
54
+ } catch (error) {
55
+ // Service might not be running, continue with deletion
56
+ }
57
+
58
+ // Disable the service
59
+ try {
60
+ execSync(`systemctl --user disable "${escapedName}"`, { stdio: "inherit" });
61
+ } catch (error) {
62
+ // Service might not exist, continue
63
+ }
64
+
65
+ // Remove the service file if requested
66
+ if (options.remove) {
67
+ const serviceFile = join(platformInfo.serviceDir, `${escapedName}.service`);
68
+ try {
69
+ execSync(`rm -f "${serviceFile}"`, { stdio: "inherit" });
70
+ console.log(`🗑️ Service file removed: ${serviceFile}`);
71
+ } catch (error) {
72
+ // File might not exist, continue
73
+ }
74
+ }
75
+
76
+ console.log(`🗑️ Service '${name}' deleted successfully`);
77
+ } else if (platformInfo.isMacOS) {
78
+ const { launchdCommand } = await import("../macos/launchd.js");
79
+ await launchdCommand('delete', { name: `bs9.${name}` });
80
+
81
+ if (options.remove) {
82
+ const plistFile = join(platformInfo.serviceDir, `bs9.${name}.plist`);
83
+ try {
84
+ execSync(`rm -f "${plistFile}"`, { stdio: "inherit" });
85
+ console.log(`🗑️ Service file removed: ${plistFile}`);
86
+ } catch (error) {
87
+ // File might not exist, continue
88
+ }
89
+ }
90
+ } else if (platformInfo.isWindows) {
91
+ const { windowsCommand } = await import("../windows/service.js");
92
+ await windowsCommand('delete', { name: `BS9_${name}` });
93
+
94
+ if (options.remove) {
95
+ // Windows service removal is handled by the windowsCommand
96
+ console.log(`🗑️ Windows service 'BS9_${name}' deleted`);
97
+ }
98
+ }
99
+ } catch (err) {
100
+ console.error(`❌ Failed to delete service '${name}': ${err}`);
101
+ if (!options.force) {
102
+ process.exit(1);
103
+ }
104
+ }
105
+ }
106
+
107
+ async function deleteAllServices(platformInfo: any, options: DeleteOptions): Promise<void> {
108
+ try {
109
+ console.log("🗑️ Deleting all BS9 services...");
110
+
111
+ if (platformInfo.isLinux) {
112
+ // Get all BS9 services
113
+ const listOutput = execSync("systemctl --user list-units --type=service --no-pager --no-legend", { encoding: "utf-8" });
114
+ const lines = listOutput.split("\n").filter(line => line.includes(".service"));
115
+
116
+ const bs9Services: string[] = [];
117
+
118
+ for (const line of lines) {
119
+ const match = line.match(/^(?:\s*([●\s○]))?\s*([^\s]+)\.service\s+([^\s]+)\s+([^\s]+)\s+(.+)$/);
120
+ if (match) {
121
+ const [, serviceName] = match;
122
+
123
+ // Only process BS9 services
124
+ if (match[5].includes("Bun Service:") || match[5].includes("BS9 Service:")) {
125
+ bs9Services.push(serviceName);
126
+ }
127
+ }
128
+ }
129
+
130
+ if (bs9Services.length === 0) {
131
+ console.log("ℹ️ No BS9 services found to delete");
132
+ return;
133
+ }
134
+
135
+ console.log(`Found ${bs9Services.length} BS9 services to delete...`);
136
+
137
+ for (const serviceName of bs9Services) {
138
+ try {
139
+ // Stop the service
140
+ execSync(`systemctl --user stop "${serviceName}"`, { stdio: "inherit" });
141
+
142
+ // Disable the service
143
+ execSync(`systemctl --user disable "${serviceName}"`, { stdio: "inherit" });
144
+
145
+ // Remove service file if requested
146
+ if (options.remove) {
147
+ const serviceFile = join(platformInfo.serviceDir, `${serviceName}.service`);
148
+ execSync(`rm -f "${serviceFile}"`, { stdio: "inherit" });
149
+ console.log(` 🗑️ Deleted service file: ${serviceName}`);
150
+ }
151
+
152
+ console.log(` 🗑️ Deleted service: ${serviceName}`);
153
+ } catch (error) {
154
+ console.error(` ⚠️ Failed to delete service '${serviceName}': ${error}`);
155
+ }
156
+ }
157
+
158
+ } else if (platformInfo.isMacOS) {
159
+ // For macOS, we would need to list all launchd agents
160
+ // This is more complex, so for now we'll provide a message
161
+ console.log("📝 To delete all services on macOS, you need to manually remove the plist files from:");
162
+ console.log(` ${platformInfo.serviceDir}/bs9.*.plist`);
163
+ console.log(" And then run: launchctl unload ~/Library/LaunchAgents/bs9.*.plist");
164
+ } else if (platformInfo.isWindows) {
165
+ // For Windows, we would need to query all BS9 services
166
+ // This is more complex, so for now we'll provide a message
167
+ console.log("📝 To delete all services on Windows, use PowerShell:");
168
+ console.log(" Get-Service -Name \"BS9_*\" | ForEach-Object { Stop-Service $_.Name; Remove-Service $_.Name }");
169
+ }
170
+
171
+ console.log(`✅ All BS9 services deletion process completed`);
172
+ } catch (err) {
173
+ console.error(`❌ Failed to delete all services: ${err}`);
174
+ if (!options.force) {
175
+ process.exit(1);
176
+ }
177
+ }
178
+ }