bs9 1.3.6 → 1.3.8

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/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # BS9 (Bun Sentinel 9) šŸš€
2
2
 
3
3
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
4
- [![Version](https://img.shields.io/badge/version-1.3.5-blue.svg)](https://github.com/xarhang/bs9)
4
+ [![Version](https://img.shields.io/badge/version-1.3.7-blue.svg)](https://github.com/xarhang/bs9)
5
5
  [![Security](https://img.shields.io/badge/security-Enterprise-green.svg)](SECURITY.md)
6
6
  [![Production Ready](https://img.shields.io/badge/production-Ready-brightgreen.svg)](PRODUCTION.md)
7
7
  [![Cross-Platform](https://img.shields.io/badge/platform-Linux%20%7C%20macOS%20%7C%20Windows-lightgrey.svg)](https://github.com/bs9/bs9)
@@ -98,6 +98,20 @@ bs9 alert --test # Test webhook
98
98
  bs9 export --format json --hours 24 # Export metrics
99
99
  bs9 export --service myapp --format csv --hours 24
100
100
  bs9 export --service myapp --format csv
101
+
102
+ # Service management
103
+ bs9 delete myapp # Delete specific service
104
+ bs9 delete myapp --remove # Delete and remove config files
105
+ bs9 delete --all # Delete all services
106
+ bs9 delete --all --force # Force delete all services
107
+ bs9 delete myapp --timeout 60 # Custom graceful shutdown timeout
108
+
109
+ # Backup and restore
110
+ bs9 save myapp # Save service configuration
111
+ bs9 save --all # Save all services
112
+ bs9 save myapp --backup # Save with timestamped backup
113
+ bs9 resurrect myapp # Restore from backup
114
+ bs9 resurrect --all # Restore all services
101
115
  ```
102
116
 
103
117
  ---
@@ -123,6 +137,22 @@ bs9 export --service myapp --format csv
123
137
  - **Cooldown Period**: Prevent alert spam
124
138
  - **Alert Testing**: Webhook connectivity validation
125
139
 
140
+ ### šŸ—‘ļø Service Deletion & Cleanup
141
+ - **Individual Service Deletion**: Delete specific services by name
142
+ - **Bulk Deletion**: Remove all BS9 services at once
143
+ - **Configuration Cleanup**: Remove service configuration files
144
+ - **Force Deletion**: Ignore errors during deletion
145
+ - **Graceful Shutdown**: Configurable timeout for clean termination
146
+ - **Cross-Platform Support**: Works on Linux, macOS, and Windows
147
+
148
+ ### šŸ’¾ Backup & Restore System
149
+ - **Service Configuration Backup**: Save service configurations to JSON
150
+ - **Bulk Backup**: Save all services at once
151
+ - **Timestamped Backups**: Create multiple backup versions
152
+ - **Service Resurrection**: Restore services from backup
153
+ - **Configuration Management**: Manage service configurations
154
+ - **Disaster Recovery**: Quick service restoration
155
+
126
156
  ### 🐳 Container & Orchestration
127
157
  - **Docker Support**: Complete Dockerfile and docker-compose setup
128
158
  - **Kubernetes**: Full K8s deployment with ServiceMonitor
@@ -303,6 +333,9 @@ kubectl get pods -n bs9-system
303
333
  | - | `bs9 web` | Web-based dashboard |
304
334
  | - | `bs9 alert` | Alert system with webhooks |
305
335
  | - | `bs9 export` | Historical metrics |
336
+ | - | `bs9 delete` | Service deletion and cleanup |
337
+ | - | `bs9 save` | Service configuration backup |
338
+ | - | `bs9 resurrect` | Service restoration from backup |
306
339
 
307
340
  ---
308
341
 
@@ -453,6 +486,9 @@ BS9/
453
486
  │ │ ā”œā”€ā”€ monit.ts # Terminal dashboard
454
487
  │ │ ā”œā”€ā”€ web.ts # Web dashboard
455
488
  │ │ ā”œā”€ā”€ alert.ts # Alert management
489
+ │ │ ā”œā”€ā”€ delete.ts # Service deletion
490
+ │ │ ā”œā”€ā”€ save.ts # Service backup
491
+ │ │ ā”œā”€ā”€ resurrect.ts # Service restoration
456
492
  │ │ └── export.ts # Data export
457
493
  │ ā”œā”€ā”€ web/ # Web dashboard
458
494
  │ │ └── dashboard.ts # Web server
package/bin/bs9 CHANGED
@@ -20,6 +20,9 @@ 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";
24
+ import { saveCommand } from "../src/commands/save.js";
25
+ import { resurrectCommand } from "../src/commands/resurrect.js";
23
26
  import { loadbalancerCommand } from "../src/loadbalancer/manager.js";
24
27
  import { windowsCommand } from "../src/windows/service.js";
25
28
  import { launchdCommand } from "../src/macos/launchd.js";
@@ -126,9 +129,38 @@ program
126
129
  .option("-d, --duration <seconds>", "Profiling duration", "60")
127
130
  .option("-i, --interval <ms>", "Sampling interval", "1000")
128
131
  .option("-s, --service <name>", "Service name to profile")
129
- .option("-o, --output <file>", "Output file path")
132
+ .option("--flamegraph", "Generate flame graph")
133
+ .option("--top <number>", "Show top N functions", "10")
130
134
  .action(profileCommand);
131
135
 
136
+ program
137
+ .command("delete")
138
+ .description("Delete managed services")
139
+ .argument("[name]", "Service name (optional)")
140
+ .option("-a, --all", "Delete all services")
141
+ .option("-f, --force", "Force deletion without errors")
142
+ .option("-r, --remove", "Remove service configuration files")
143
+ .option("-t, --timeout <seconds>", "Timeout for graceful shutdown (default: 30)")
144
+ .action(deleteCommand);
145
+
146
+ program
147
+ .command("save")
148
+ .description("Save service configurations to backup")
149
+ .argument("[name]", "Service name (optional)")
150
+ .option("-a, --all", "Save all services")
151
+ .option("-f, --force", "Force save without errors")
152
+ .option("-b, --backup", "Create timestamped backup")
153
+ .action(saveCommand);
154
+
155
+ program
156
+ .command("resurrect")
157
+ .description("Resurrect services from backup")
158
+ .argument("[name]", "Service name (optional)")
159
+ .option("-a, --all", "Resurrect all services")
160
+ .option("-f, --force", "Force resurrection without errors")
161
+ .option("-c, --config <file>", "Configuration file to use")
162
+ .action(resurrectCommand);
163
+
132
164
  program
133
165
  .command("loadbalancer")
134
166
  .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();
@@ -0,0 +1,221 @@
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 { loadbalancerCommand } from "../src/loadbalancer/manager.js";
27
+ import { windowsCommand } from "../src/windows/service.js";
28
+ import { launchdCommand } from "../src/macos/launchd.js";
29
+ import { updateCommand } from "../src/commands/update.js";
30
+ import { dbpoolCommand } from "../src/database/pool.js";
31
+ import { advancedMonitoringCommand } from "../src/monitoring/advanced.js";
32
+ import { consulCommand } from "../src/discovery/consul.js";
33
+
34
+ const program = new Command();
35
+
36
+ program
37
+ .name("bs9")
38
+ .description("BS9 (Bun Sentinel 9) — Mission-critical process manager CLI")
39
+ .version(version);
40
+
41
+ program
42
+ .command("start")
43
+ .description("Start a process with hardened systemd unit")
44
+ .argument("<file>", "Application file to start")
45
+ .option("-n, --name <name>", "Service name")
46
+ .option("-p, --port <port>", "Port number", "3000")
47
+ .option("-h, --host <host>", "Host address", "localhost")
48
+ .option("--https", "Use HTTPS protocol")
49
+ .option("-e, --env <env>", "Environment variables (can be used multiple times)", (value, previous) => [...(previous || []), value])
50
+ .option("--otel", "Enable OpenTelemetry instrumentation", true)
51
+ .option("--prometheus", "Enable Prometheus metrics", true)
52
+ .option("--build", "Build TypeScript to JavaScript before starting")
53
+ .action(startCommand);
54
+
55
+ program
56
+ .command("stop")
57
+ .description("Stop a managed service")
58
+ .argument("<name>", "Service name")
59
+ .action(stopCommand);
60
+
61
+ program
62
+ .command("restart")
63
+ .description("Restart a managed service")
64
+ .argument("<name>", "Service name")
65
+ .action(restartCommand);
66
+
67
+ program
68
+ .command("status")
69
+ .description("Show status and SRE metrics for all services")
70
+ .option("-w, --watch", "Watch mode (refresh every 2s)")
71
+ .action(statusCommand);
72
+
73
+ program
74
+ .command("logs")
75
+ .description("Show logs for a service (via journalctl)")
76
+ .argument("<name>", "Service name")
77
+ .option("-f, --follow", "Follow logs")
78
+ .option("-n, --lines <number>", "Number of lines", "50")
79
+ .action(logsCommand);
80
+
81
+ program
82
+ .command("monit")
83
+ .description("Real-time terminal dashboard for all services")
84
+ .option("-r, --refresh <seconds>", "Refresh interval in seconds", "2")
85
+ .action(monitCommand);
86
+
87
+ program
88
+ .command("web")
89
+ .description("Start web-based monitoring dashboard")
90
+ .option("-p, --port <port>", "Port for web dashboard", "8080")
91
+ .option("-d, --detach", "Run in background")
92
+ .action(webCommand);
93
+
94
+ program
95
+ .command("alert")
96
+ .description("Configure alert thresholds and webhooks")
97
+ .option("--enable", "Enable alerts")
98
+ .option("--disable", "Disable alerts")
99
+ .option("--webhook <url>", "Set webhook URL for alerts")
100
+ .option("--cpu <percentage>", "CPU threshold percentage")
101
+ .option("--memory <percentage>", "Memory threshold percentage")
102
+ .option("--errorRate <percentage>", "Error rate threshold percentage")
103
+ .option("--uptime <percentage>", "Uptime threshold percentage")
104
+ .option("--cooldown <seconds>", "Alert cooldown period in seconds")
105
+ .option("--service <name>", "Configure alerts for specific service")
106
+ .option("--list", "List current alert configuration")
107
+ .option("--test", "Test webhook connectivity")
108
+ .action(alertCommand);
109
+
110
+ program
111
+ .command("export")
112
+ .description("Export historical metrics data")
113
+ .option("-f, --format <format>", "Export format (json|csv)", "json")
114
+ .option("-h, --hours <hours>", "Hours of data to export", "24")
115
+ .option("-o, --output <file>", "Output file path")
116
+ .option("-s, --service <name>", "Export specific service metrics")
117
+ .action(exportCommand);
118
+
119
+ program
120
+ .command("deps")
121
+ .description("Visualize service dependencies")
122
+ .option("-f, --format <format>", "Output format (text|dot|json)", "text")
123
+ .option("-o, --output <file>", "Output file path")
124
+ .action(depsCommand);
125
+
126
+ program
127
+ .command("profile")
128
+ .description("Performance profiling for services")
129
+ .option("-d, --duration <seconds>", "Profiling duration", "60")
130
+ .option("-i, --interval <ms>", "Sampling interval", "1000")
131
+ .option("-s, --service <name>", "Service name to profile")
132
+ .option("--flamegraph", "Generate flame graph")
133
+ .option("--top <number>", "Show top N functions", "10")
134
+ .action(profileCommand);
135
+
136
+ program
137
+ .command("delete")
138
+ .description("Delete managed services")
139
+ .argument("[name]", "Service name (optional)")
140
+ .option("-a, --all", "Delete all services")
141
+ .option("-f, --force", "Force deletion without errors")
142
+ .option("-r, --remove", "Remove service configuration files")
143
+ .option("-t, --timeout <seconds>", "Timeout for graceful shutdown (default: 30)")
144
+ .action(deleteCommand);
145
+
146
+ program
147
+ .command("save")
148
+ .description("Save service configurations to backup")
149
+ .argument("[name]", "Service name (optional)")
150
+ .option("-a, --all", "Save all services")
151
+ .option("-f, --force", "Force save without errors")
152
+ .option("-b, --backup", "Create timestamped backup")
153
+ .action(saveCommand);
154
+
155
+ program
156
+ .command("resurrect")
157
+ .description("Resurrect services from backup")
158
+ .argument("[name]", "Service name (optional)")
159
+ .option("-a, --all", "Resurrect all services")
160
+ .option("-f, --force", "Force resurrection without errors")
161
+ .option("-c, --config <file>", "Configuration file to use")
162
+ .action(resurrectCommand);
163
+
164
+ program
165
+ .command("loadbalancer")
166
+ .description("Load balancer management")
167
+ .argument("<action>", "Action to perform")
168
+ .option("-p, --port <port>", "Load balancer port", "8080")
169
+ .option("-a, --algorithm <type>", "Load balancing algorithm", "round-robin")
170
+ .option("-b, --backends <list>", "Backend servers (host:port,host:port)")
171
+ .option("--health-check", "Enable health checking", true)
172
+ .option("--health-path <path>", "Health check path", "/healthz")
173
+ .option("--health-interval <ms>", "Health check interval", "10000")
174
+ .action(loadbalancerCommand);
175
+
176
+ program
177
+ .command("dbpool")
178
+ .description("Database connection pool management")
179
+ .argument("<action>", "Action to perform")
180
+ .option("--host <host>", "Database host", "localhost")
181
+ .option("--port <port>", "Database port", "5432")
182
+ .option("--database <name>", "Database name", "testdb")
183
+ .option("--username <user>", "Database username", "user")
184
+ .option("--password <pass>", "Database password", "password")
185
+ .option("--max-connections <num>", "Maximum connections", "10")
186
+ .option("--min-connections <num>", "Minimum connections", "2")
187
+ .option("--concurrency <num>", "Test concurrency", "10")
188
+ .option("--iterations <num>", "Test iterations", "100")
189
+ .action(dbpoolCommand);
190
+
191
+ program
192
+ .command("update")
193
+ .description("Update BS9 to latest version")
194
+ .option("--check", "Check for updates without installing")
195
+ .option("--force", "Force update even if already latest")
196
+ .option("--rollback", "Rollback to previous version")
197
+ .option("--version <version>", "Update to specific version")
198
+ .action(updateCommand);
199
+
200
+ program
201
+ .command("advanced")
202
+ .description("Advanced monitoring dashboard")
203
+ .option("--port <port>", "Dashboard port", "8090")
204
+ .action(advancedMonitoringCommand);
205
+
206
+ program
207
+ .command("consul")
208
+ .description("Consul service discovery")
209
+ .argument("<action>", "Action to perform")
210
+ .option("--consul-url <url>", "Consul URL", "http://localhost:8500")
211
+ .option("--name <name>", "Service name")
212
+ .option("--id <id>", "Service ID")
213
+ .option("--address <address>", "Service address")
214
+ .option("--port <port>", "Service port")
215
+ .option("--tags <tags>", "Service tags (comma-separated)")
216
+ .option("--health-check <url>", "Health check URL")
217
+ .option("--meta <json>", "Service metadata (JSON)")
218
+ .option("--service <service>", "Service name for discovery")
219
+ .action(consulCommand);
220
+
221
+ 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-xvqzb6v3.";
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.8",
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; // Skip the status symbol, capture service name
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
+ }
@@ -0,0 +1,171 @@
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 ResurrectOptions {
17
+ all?: boolean;
18
+ force?: boolean;
19
+ config?: string;
20
+ }
21
+
22
+ // Security: Service name validation
23
+ function isValidServiceName(name: string): boolean {
24
+ // Only allow alphanumeric, hyphens, underscores, and dots
25
+ // Prevent command injection and path traversal
26
+ const validPattern = /^[a-zA-Z0-9._-]+$/;
27
+ return validPattern.test(name) && name.length <= 64 && !name.includes('..') && !name.includes('/');
28
+ }
29
+
30
+ export async function resurrectCommand(name: string, options: ResurrectOptions): Promise<void> {
31
+ const platformInfo = getPlatformInfo();
32
+
33
+ // Handle resurrect all services
34
+ if (options.all) {
35
+ await resurrectAllServices(platformInfo, options);
36
+ return;
37
+ }
38
+
39
+ // Security: Validate service name
40
+ if (!isValidServiceName(name)) {
41
+ console.error(`āŒ Security: Invalid service name: ${name}`);
42
+ process.exit(1);
43
+ }
44
+
45
+ try {
46
+ if (platformInfo.isLinux) {
47
+ // Security: Use shell escaping to prevent injection
48
+ const escapedName = name.replace(/[^a-zA-Z0-9._-]/g, '');
49
+
50
+ // Check if service exists in backup
51
+ const backupDir = join(platformInfo.configDir, 'backups');
52
+ const backupFile = join(backupDir, `${escapedName}.json`);
53
+
54
+ if (!require('node:fs').existsSync(backupFile)) {
55
+ console.error(`āŒ No backup found for service '${name}'`);
56
+ process.exit(1);
57
+ }
58
+
59
+ // Load backup configuration
60
+ const backupConfig = JSON.parse(require('node:fs').readFileSync(backupFile, 'utf8'));
61
+
62
+ // Restore service using backup configuration
63
+ const { startCommand } = await import("./start.js");
64
+ await startCommand(backupConfig.file, {
65
+ name: backupConfig.name,
66
+ port: backupConfig.port?.toString(),
67
+ host: backupConfig.host,
68
+ env: backupConfig.env,
69
+ otel: backupConfig.otel,
70
+ prometheus: backupConfig.prometheus,
71
+ build: backupConfig.build,
72
+ https: backupConfig.https
73
+ });
74
+
75
+ console.log(`āœ… Service '${name}' resurrected successfully from backup`);
76
+
77
+ } else if (platformInfo.isMacOS) {
78
+ const { launchdCommand } = await import("../macos/launchd.js");
79
+ await launchdCommand('resurrect', { name: `bs9.${name}` });
80
+
81
+ if (options.config) {
82
+ const plistFile = join(platformInfo.serviceDir, `bs9.${name}.plist`);
83
+ try {
84
+ require('node:fs').writeFileSync(plistFile, options.config);
85
+ console.log(`šŸ“ Configuration restored: ${plistFile}`);
86
+ } catch (error) {
87
+ console.error(`āŒ Failed to restore configuration: ${error}`);
88
+ }
89
+ }
90
+
91
+ console.log(`āœ… Service '${name}' resurrected successfully`);
92
+
93
+ } else if (platformInfo.isWindows) {
94
+ const { windowsCommand } = await import("../windows/service.js");
95
+ await windowsCommand('resurrect', { name: `BS9_${name}` });
96
+
97
+ console.log(`āœ… Service '${name}' resurrected successfully`);
98
+ }
99
+ } catch (err) {
100
+ console.error(`āŒ Failed to resurrect service '${name}': ${err}`);
101
+ if (!options.force) {
102
+ process.exit(1);
103
+ }
104
+ }
105
+ }
106
+
107
+ async function resurrectAllServices(platformInfo: any, options: ResurrectOptions): Promise<void> {
108
+ try {
109
+ console.log("šŸ”„ Resurrecting all BS9 services from backup...");
110
+
111
+ if (platformInfo.isLinux) {
112
+ const backupDir = join(platformInfo.configDir, 'backups');
113
+
114
+ if (!require('node:fs').existsSync(backupDir)) {
115
+ console.log("ā„¹ļø No backup directory found");
116
+ return;
117
+ }
118
+
119
+ // Get all backup files
120
+ const backupFiles = require('node:fs').readdirSync(backupDir)
121
+ .filter((file: string) => file.endsWith('.json'));
122
+
123
+ if (backupFiles.length === 0) {
124
+ console.log("ā„¹ļø No backup files found to resurrect");
125
+ return;
126
+ }
127
+
128
+ console.log(`Found ${backupFiles.length} backup files to restore...`);
129
+
130
+ for (const backupFile of backupFiles) {
131
+ try {
132
+ const serviceName = backupFile.replace('.json', '');
133
+ const backupPath = join(backupDir, backupFile);
134
+ const backupConfig = JSON.parse(require('node:fs').readFileSync(backupPath, 'utf8'));
135
+
136
+ // Restore service using backup configuration
137
+ const { startCommand } = await import("./start.js");
138
+ await startCommand(backupConfig.file, {
139
+ name: backupConfig.name,
140
+ port: backupConfig.port?.toString(),
141
+ host: backupConfig.host,
142
+ env: backupConfig.env,
143
+ otel: backupConfig.otel,
144
+ prometheus: backupConfig.prometheus,
145
+ build: backupConfig.build,
146
+ https: backupConfig.https
147
+ });
148
+
149
+ console.log(` āœ… Resurrected service: ${serviceName}`);
150
+ } catch (error) {
151
+ console.error(` āš ļø Failed to resurrect service '${backupFile}': ${error}`);
152
+ }
153
+ }
154
+
155
+ } else if (platformInfo.isMacOS) {
156
+ console.log("šŸ“ To resurrect all services on macOS, you need to manually restore the plist files from:");
157
+ console.log(` ${join(platformInfo.configDir, 'backups')}/*.plist`);
158
+ console.log(" And then run: launchctl load ~/Library/LaunchAgents/bs9.*.plist");
159
+ } else if (platformInfo.isWindows) {
160
+ console.log("šŸ“ To resurrect all services on Windows, use PowerShell:");
161
+ console.log(" Get-ChildItem -Path \"${join(platformInfo.configDir, 'backups')}\" | ForEach-Object { Restore-Service $_.Name }");
162
+ }
163
+
164
+ console.log(`āœ… All BS9 services resurrection process completed`);
165
+ } catch (err) {
166
+ console.error(`āŒ Failed to resurrect all services: ${err}`);
167
+ if (!options.force) {
168
+ process.exit(1);
169
+ }
170
+ }
171
+ }
@@ -0,0 +1,249 @@
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 SaveOptions {
17
+ all?: boolean;
18
+ force?: boolean;
19
+ backup?: boolean;
20
+ }
21
+
22
+ // Security: Service name validation
23
+ function isValidServiceName(name: string): boolean {
24
+ // Only allow alphanumeric, hyphens, underscores, and dots
25
+ // Prevent command injection and path traversal
26
+ const validPattern = /^[a-zA-Z0-9._-]+$/;
27
+ return validPattern.test(name) && name.length <= 64 && !name.includes('..') && !name.includes('/');
28
+ }
29
+
30
+ export async function saveCommand(name: string, options: SaveOptions): Promise<void> {
31
+ const platformInfo = getPlatformInfo();
32
+
33
+ // Handle save all services
34
+ if (options.all) {
35
+ await saveAllServices(platformInfo, options);
36
+ return;
37
+ }
38
+
39
+ // Security: Validate service name
40
+ if (!isValidServiceName(name)) {
41
+ console.error(`āŒ Security: Invalid service name: ${name}`);
42
+ process.exit(1);
43
+ }
44
+
45
+ try {
46
+ if (platformInfo.isLinux) {
47
+ // Security: Use shell escaping to prevent injection
48
+ const escapedName = name.replace(/[^a-zA-Z0-9._-]/g, '');
49
+
50
+ // Get service status and configuration
51
+ const statusOutput = execSync(`systemctl --user show "${escapedName}"`, { encoding: "utf-8" });
52
+ const serviceFile = join(platformInfo.serviceDir, `${escapedName}.service`);
53
+
54
+ if (!require('node:fs').existsSync(serviceFile)) {
55
+ console.error(`āŒ Service configuration not found for '${name}'`);
56
+ process.exit(1);
57
+ }
58
+
59
+ // Read service configuration
60
+ const serviceConfig = require('node:fs').readFileSync(serviceFile, 'utf8');
61
+
62
+ // Parse service configuration to extract startup parameters
63
+ const config = parseServiceConfig(serviceConfig, statusOutput);
64
+
65
+ // Create backup directory if it doesn't exist
66
+ const backupDir = join(platformInfo.configDir, 'backups');
67
+ require('node:fs').mkdirSync(backupDir, { recursive: true });
68
+
69
+ // Save configuration to backup
70
+ const backupFile = join(backupDir, `${escapedName}.json`);
71
+ const backupData = {
72
+ name: name,
73
+ file: extractFileFromConfig(serviceConfig),
74
+ port: config.port,
75
+ host: config.host,
76
+ env: config.env,
77
+ otel: config.otel,
78
+ prometheus: config.prometheus,
79
+ build: config.build,
80
+ https: config.https,
81
+ savedAt: new Date().toISOString(),
82
+ platform: platformInfo.platform
83
+ };
84
+
85
+ require('node:fs').writeFileSync(backupFile, JSON.stringify(backupData, null, 2));
86
+
87
+ console.log(`šŸ’¾ Service '${name}' configuration saved to: ${backupFile}`);
88
+
89
+ if (options.backup) {
90
+ // Create additional backup with timestamp
91
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
92
+ const timestampedBackup = join(backupDir, `${escapedName}-${timestamp}.json`);
93
+ require('node:fs').writeFileSync(timestampedBackup, JSON.stringify(backupData, null, 2));
94
+ console.log(`šŸ“¦ Additional backup created: ${timestampedBackup}`);
95
+ }
96
+
97
+ } else if (platformInfo.isMacOS) {
98
+ const { launchdCommand } = await import("../macos/launchd.js");
99
+ await launchdCommand('save', { name: `bs9.${name}` });
100
+
101
+ console.log(`šŸ’¾ Service '${name}' configuration saved`);
102
+
103
+ } else if (platformInfo.isWindows) {
104
+ const { windowsCommand } = await import("../windows/service.js");
105
+ await windowsCommand('save', { name: `BS9_${name}` });
106
+
107
+ console.log(`šŸ’¾ Service '${name}' configuration saved`);
108
+ }
109
+ } catch (err) {
110
+ console.error(`āŒ Failed to save service '${name}': ${err}`);
111
+ if (!options.force) {
112
+ process.exit(1);
113
+ }
114
+ }
115
+ }
116
+
117
+ async function saveAllServices(platformInfo: any, options: SaveOptions): Promise<void> {
118
+ try {
119
+ console.log("šŸ’¾ Saving all BS9 service configurations...");
120
+
121
+ if (platformInfo.isLinux) {
122
+ // Get all BS9 services
123
+ const listOutput = execSync("systemctl --user list-units --type=service --no-pager --no-legend", { encoding: "utf-8" });
124
+ const lines = listOutput.split("\n").filter(line => line.includes(".service"));
125
+
126
+ const bs9Services: string[] = [];
127
+
128
+ for (const line of lines) {
129
+ const match = line.match(/^(?:\s*([ā—\sā—‹]))?\s*([^\s]+)\.service\s+([^\s]+)\s+([^\s]+)\s+(.+)$/);
130
+ if (match) {
131
+ const [, , serviceName] = match; // Skip the status symbol, capture service name
132
+
133
+ // Only process BS9 services
134
+ if (match[5].includes("Bun Service:") || match[5].includes("BS9 Service:")) {
135
+ bs9Services.push(serviceName);
136
+ }
137
+ }
138
+ }
139
+
140
+ if (bs9Services.length === 0) {
141
+ console.log("ā„¹ļø No BS9 services found to save");
142
+ return;
143
+ }
144
+
145
+ console.log(`Found ${bs9Services.length} BS9 services to save...`);
146
+
147
+ // Create backup directory
148
+ const backupDir = join(platformInfo.configDir, 'backups');
149
+ require('node:fs').mkdirSync(backupDir, { recursive: true });
150
+
151
+ for (const serviceName of bs9Services) {
152
+ try {
153
+ const serviceFile = join(platformInfo.serviceDir, `${serviceName}.service`);
154
+
155
+ if (require('node:fs').existsSync(serviceFile)) {
156
+ const serviceConfig = require('node:fs').readFileSync(serviceFile, 'utf8');
157
+ const statusOutput = execSync(`systemctl --user show "${serviceName}"`, { encoding: "utf-8" });
158
+ const config = parseServiceConfig(serviceConfig, statusOutput);
159
+
160
+ const backupFile = join(backupDir, `${serviceName}.json`);
161
+ const backupData = {
162
+ name: serviceName,
163
+ file: extractFileFromConfig(serviceConfig),
164
+ port: config.port,
165
+ host: config.host,
166
+ env: config.env,
167
+ otel: config.otel,
168
+ prometheus: config.prometheus,
169
+ build: config.build,
170
+ https: config.https,
171
+ savedAt: new Date().toISOString(),
172
+ platform: platformInfo.platform
173
+ };
174
+
175
+ require('node:fs').writeFileSync(backupFile, JSON.stringify(backupData, null, 2));
176
+ console.log(` šŸ’¾ Saved service: ${serviceName}`);
177
+ }
178
+ } catch (error) {
179
+ console.error(` āš ļø Failed to save service '${serviceName}': ${error}`);
180
+ }
181
+ }
182
+
183
+ } else if (platformInfo.isMacOS) {
184
+ console.log("šŸ“ To save all services on macOS, you need to manually backup the plist files from:");
185
+ console.log(` ${platformInfo.serviceDir}/bs9.*.plist`);
186
+ } else if (platformInfo.isWindows) {
187
+ console.log("šŸ“ To save all services on Windows, use PowerShell:");
188
+ console.log(" Get-Service -Name \"BS9_*\" | ForEach-Object { Export-ServiceConfiguration $_.Name }");
189
+ }
190
+
191
+ console.log(`āœ… All BS9 services save process completed`);
192
+ } catch (err) {
193
+ console.error(`āŒ Failed to save all services: ${err}`);
194
+ if (!options.force) {
195
+ process.exit(1);
196
+ }
197
+ }
198
+ }
199
+
200
+ // Helper function to parse service configuration
201
+ function parseServiceConfig(serviceConfig: string, statusOutput: string): any {
202
+ const config: any = {};
203
+
204
+ // Extract port from service config
205
+ const portMatch = serviceConfig.match(/--port[=\s]+(\d+)/);
206
+ if (portMatch) {
207
+ config.port = parseInt(portMatch[1]);
208
+ }
209
+
210
+ // Extract host from service config
211
+ const hostMatch = serviceConfig.match(/--host[=\s]+([^\s]+)/);
212
+ if (hostMatch) {
213
+ config.host = hostMatch[1];
214
+ }
215
+
216
+ // Extract environment variables
217
+ const envMatches = serviceConfig.match(/--env[=\s]+([^\s]+)/g);
218
+ if (envMatches) {
219
+ config.env = envMatches.map((env: string) => env.replace(/--env[=\s]+/, ''));
220
+ }
221
+
222
+ // Extract OpenTelemetry flag
223
+ config.otel = serviceConfig.includes('--otel') || serviceConfig.includes('--opentelemetry');
224
+
225
+ // Extract Prometheus flag
226
+ config.prometheus = serviceConfig.includes('--prometheus');
227
+
228
+ // Extract build flag
229
+ config.build = serviceConfig.includes('--build');
230
+
231
+ // Extract HTTPS flag
232
+ config.https = serviceConfig.includes('--https');
233
+
234
+ return config;
235
+ }
236
+
237
+ // Helper function to extract file path from service config
238
+ function extractFileFromConfig(serviceConfig: string): string {
239
+ const execMatch = serviceConfig.match(/ExecStart=([^\n]+)/);
240
+ if (execMatch) {
241
+ const execLine = execMatch[1].trim();
242
+ // Extract the file path from the exec command
243
+ const fileMatch = execLine.match(/bun\s+([^\s]+)/);
244
+ if (fileMatch) {
245
+ return fileMatch[1];
246
+ }
247
+ }
248
+ return '';
249
+ }