bs9 1.0.0 → 1.1.0
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 +97 -9
- package/bin/bs9 +58 -7
- package/dist/bs9-064xs9r9. +148 -0
- package/dist/bs9-0gqcrp5t. +144 -0
- package/dist/bs9-nv7nseny. +197 -0
- package/dist/bs9-r6b9zpw0. +156 -0
- package/dist/bs9.js +2 -0
- package/package.json +3 -4
- package/src/commands/deps.ts +295 -0
- package/src/commands/profile.ts +338 -0
- package/src/commands/restart.ts +28 -3
- package/src/commands/start.ts +201 -21
- package/src/commands/status.ts +154 -109
- package/src/commands/stop.ts +31 -3
- package/src/commands/web.ts +29 -3
- package/src/database/pool.ts +335 -0
- package/src/loadbalancer/manager.ts +481 -0
- package/src/macos/launchd.ts +402 -0
- package/src/platform/detect.ts +137 -0
- package/src/windows/service.ts +391 -0
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
4
|
+
import { existsSync, writeFileSync, mkdirSync } from "node:fs";
|
|
5
|
+
import { join, dirname } from "node:path";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
|
|
8
|
+
interface LaunchdServiceConfig {
|
|
9
|
+
label: string;
|
|
10
|
+
programArguments: string[];
|
|
11
|
+
workingDirectory: string;
|
|
12
|
+
environmentVariables: Record<string, string>;
|
|
13
|
+
runAtLoad: boolean;
|
|
14
|
+
keepAlive: boolean;
|
|
15
|
+
standardOutPath?: string;
|
|
16
|
+
standardErrorPath?: string;
|
|
17
|
+
startInterval?: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface LaunchdServiceStatus {
|
|
21
|
+
label: string;
|
|
22
|
+
pid?: number;
|
|
23
|
+
status: 'running' | 'stopped' | 'loaded' | 'unloaded';
|
|
24
|
+
lastExitStatus?: number;
|
|
25
|
+
exitTime?: Date;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
class LaunchdServiceManager {
|
|
29
|
+
private launchAgentsDir: string;
|
|
30
|
+
private configPath: string;
|
|
31
|
+
|
|
32
|
+
constructor() {
|
|
33
|
+
this.launchAgentsDir = join(homedir(), 'Library', 'LaunchAgents');
|
|
34
|
+
this.configPath = join(homedir(), '.bs9', 'launchd-services.json');
|
|
35
|
+
this.ensureDirectories();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private ensureDirectories(): void {
|
|
39
|
+
if (!existsSync(this.launchAgentsDir)) {
|
|
40
|
+
mkdirSync(this.launchAgentsDir, { recursive: true });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const configDir = dirname(this.configPath);
|
|
44
|
+
if (!existsSync(configDir)) {
|
|
45
|
+
mkdirSync(configDir, { recursive: true });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
private loadConfigs(): Record<string, LaunchdServiceConfig> {
|
|
50
|
+
try {
|
|
51
|
+
if (existsSync(this.configPath)) {
|
|
52
|
+
const content = require('fs').readFileSync(this.configPath, 'utf-8');
|
|
53
|
+
return JSON.parse(content);
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.warn('Failed to load launchd configs:', error);
|
|
57
|
+
}
|
|
58
|
+
return {};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private saveConfigs(configs: Record<string, LaunchdServiceConfig>): void {
|
|
62
|
+
try {
|
|
63
|
+
writeFileSync(this.configPath, JSON.stringify(configs, null, 2));
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error('Failed to save launchd configs:', error);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private generatePlist(config: LaunchdServiceConfig): string {
|
|
70
|
+
const plistContent = {
|
|
71
|
+
Label: config.label,
|
|
72
|
+
ProgramArguments: config.programArguments,
|
|
73
|
+
WorkingDirectory: config.workingDirectory,
|
|
74
|
+
EnvironmentVariables: config.environmentVariables,
|
|
75
|
+
RunAtLoad: config.runAtLoad,
|
|
76
|
+
KeepAlive: config.keepAlive,
|
|
77
|
+
StandardOutPath: config.standardOutPath,
|
|
78
|
+
StandardErrorPath: config.standardErrorPath,
|
|
79
|
+
StartInterval: config.startInterval
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Remove undefined values
|
|
83
|
+
Object.keys(plistContent).forEach(key => {
|
|
84
|
+
if ((plistContent as any)[key] === undefined) {
|
|
85
|
+
delete (plistContent as any)[key];
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
90
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
91
|
+
<plist version="1.0">
|
|
92
|
+
<dict>
|
|
93
|
+
${Object.entries(plistContent).map(([key, value]) => {
|
|
94
|
+
if (typeof value === 'boolean') {
|
|
95
|
+
return ` <key>${key}</key>\n <${value ? 'true' : 'false'}/>`;
|
|
96
|
+
} else if (typeof value === 'string') {
|
|
97
|
+
return ` <key>${key}</key>\n <string>${value}</string>`;
|
|
98
|
+
} else if (Array.isArray(value)) {
|
|
99
|
+
return ` <key>${key}</key>\n <array>\n${value.map(item => ` <string>${item}</string>`).join('\n')}\n </array>`;
|
|
100
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
101
|
+
return ` <key>${key}</key>\n <dict>\n${Object.entries(value).map(([k, v]) => ` <key>${k}</key>\n <string>${v}</string>`).join('\n')}\n </dict>`;
|
|
102
|
+
}
|
|
103
|
+
return '';
|
|
104
|
+
}).join('\n')}
|
|
105
|
+
</dict>
|
|
106
|
+
</plist>`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async createService(config: LaunchdServiceConfig): Promise<void> {
|
|
110
|
+
const configs = this.loadConfigs();
|
|
111
|
+
configs[config.label] = config;
|
|
112
|
+
this.saveConfigs(configs);
|
|
113
|
+
|
|
114
|
+
// Generate plist file
|
|
115
|
+
const plistPath = join(this.launchAgentsDir, `${config.label}.plist`);
|
|
116
|
+
writeFileSync(plistPath, this.generatePlist(config));
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
// Load the service
|
|
120
|
+
execSync(`launchctl load "${plistPath}"`, { stdio: 'inherit' });
|
|
121
|
+
console.log(`✅ Launchd service '${config.label}' created and loaded successfully`);
|
|
122
|
+
} catch (error) {
|
|
123
|
+
console.error(`❌ Failed to create launchd service: ${error}`);
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async startService(label: string): Promise<void> {
|
|
129
|
+
try {
|
|
130
|
+
execSync(`launchctl start "${label}"`, { stdio: 'inherit' });
|
|
131
|
+
console.log(`✅ Launchd service '${label}' started successfully`);
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.error(`❌ Failed to start launchd service: ${error}`);
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async stopService(label: string): Promise<void> {
|
|
139
|
+
try {
|
|
140
|
+
execSync(`launchctl stop "${label}"`, { stdio: 'inherit' });
|
|
141
|
+
console.log(`✅ Launchd service '${label}' stopped successfully`);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
console.error(`❌ Failed to stop launchd service: ${error}`);
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async unloadService(label: string): Promise<void> {
|
|
149
|
+
const plistPath = join(this.launchAgentsDir, `${label}.plist`);
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
// Stop service first
|
|
153
|
+
try {
|
|
154
|
+
await this.stopService(label);
|
|
155
|
+
} catch {
|
|
156
|
+
// Service might not be running
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Unload service
|
|
160
|
+
execSync(`launchctl unload "${plistPath}"`, { stdio: 'inherit' });
|
|
161
|
+
|
|
162
|
+
// Remove plist file
|
|
163
|
+
require('fs').unlinkSync(plistPath);
|
|
164
|
+
|
|
165
|
+
// Remove from config
|
|
166
|
+
const configs = this.loadConfigs();
|
|
167
|
+
delete configs[label];
|
|
168
|
+
this.saveConfigs(configs);
|
|
169
|
+
|
|
170
|
+
console.log(`✅ Launchd service '${label}' unloaded and deleted successfully`);
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.error(`❌ Failed to unload launchd service: ${error}`);
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async getServiceStatus(label: string): Promise<LaunchdServiceStatus | null> {
|
|
178
|
+
try {
|
|
179
|
+
const output = execSync(`launchctl list "${label}"`, { encoding: 'utf-8' });
|
|
180
|
+
|
|
181
|
+
const lines = output.split('\n');
|
|
182
|
+
const dataLine = lines.find(line => line.includes(label));
|
|
183
|
+
|
|
184
|
+
if (dataLine) {
|
|
185
|
+
const parts = dataLine.trim().split(/\s+/);
|
|
186
|
+
const status: LaunchdServiceStatus = {
|
|
187
|
+
label: label,
|
|
188
|
+
status: 'loaded'
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
if (parts[0] !== '-') {
|
|
192
|
+
status.pid = parseInt(parts[0]);
|
|
193
|
+
status.status = 'running';
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (parts[1] !== '-') {
|
|
197
|
+
status.lastExitStatus = parseInt(parts[1]);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return status;
|
|
201
|
+
}
|
|
202
|
+
} catch (error) {
|
|
203
|
+
// Service might not exist
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async listServices(): Promise<LaunchdServiceStatus[]> {
|
|
210
|
+
try {
|
|
211
|
+
const configs = this.loadConfigs();
|
|
212
|
+
const services: LaunchdServiceStatus[] = [];
|
|
213
|
+
|
|
214
|
+
for (const label of Object.keys(configs)) {
|
|
215
|
+
const status = await this.getServiceStatus(label);
|
|
216
|
+
if (status) {
|
|
217
|
+
services.push(status);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return services;
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.error('Failed to list launchd services:', error);
|
|
224
|
+
return [];
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async enableAutoStart(label: string): Promise<void> {
|
|
229
|
+
const configs = this.loadConfigs();
|
|
230
|
+
const config = configs[label];
|
|
231
|
+
|
|
232
|
+
if (!config) {
|
|
233
|
+
throw new Error(`Service '${label}' not found`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
config.runAtLoad = true;
|
|
237
|
+
config.keepAlive = true;
|
|
238
|
+
this.saveConfigs(configs);
|
|
239
|
+
|
|
240
|
+
// Update plist file
|
|
241
|
+
const plistPath = join(this.launchAgentsDir, `${label}.plist`);
|
|
242
|
+
writeFileSync(plistPath, this.generatePlist(config));
|
|
243
|
+
|
|
244
|
+
// Reload service
|
|
245
|
+
try {
|
|
246
|
+
execSync(`launchctl unload "${plistPath}"`, { stdio: 'inherit' });
|
|
247
|
+
execSync(`launchctl load "${plistPath}"`, { stdio: 'inherit' });
|
|
248
|
+
console.log(`✅ Launchd service '${label}' set to auto-start`);
|
|
249
|
+
} catch (error) {
|
|
250
|
+
console.error(`❌ Failed to configure auto-start: ${error}`);
|
|
251
|
+
throw error;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async disableAutoStart(label: string): Promise<void> {
|
|
256
|
+
const configs = this.loadConfigs();
|
|
257
|
+
const config = configs[label];
|
|
258
|
+
|
|
259
|
+
if (!config) {
|
|
260
|
+
throw new Error(`Service '${label}' not found`);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
config.runAtLoad = false;
|
|
264
|
+
config.keepAlive = false;
|
|
265
|
+
this.saveConfigs(configs);
|
|
266
|
+
|
|
267
|
+
// Update plist file
|
|
268
|
+
const plistPath = join(this.launchAgentsDir, `${label}.plist`);
|
|
269
|
+
writeFileSync(plistPath, this.generatePlist(config));
|
|
270
|
+
|
|
271
|
+
// Reload service
|
|
272
|
+
try {
|
|
273
|
+
execSync(`launchctl unload "${plistPath}"`, { stdio: 'inherit' });
|
|
274
|
+
execSync(`launchctl load "${plistPath}"`, { stdio: 'inherit' });
|
|
275
|
+
console.log(`✅ Launchd service '${label}' set to manual start`);
|
|
276
|
+
} catch (error) {
|
|
277
|
+
console.error(`❌ Failed to configure auto-start: ${error}`);
|
|
278
|
+
throw error;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export async function launchdCommand(action: string, options: any): Promise<void> {
|
|
284
|
+
console.log('🍎 BS9 macOS Launchd Service Management');
|
|
285
|
+
console.log('='.repeat(80));
|
|
286
|
+
|
|
287
|
+
const manager = new LaunchdServiceManager();
|
|
288
|
+
|
|
289
|
+
try {
|
|
290
|
+
switch (action) {
|
|
291
|
+
case 'create':
|
|
292
|
+
if (!options.name || !options.file) {
|
|
293
|
+
console.error('❌ --name and --file are required for create action');
|
|
294
|
+
process.exit(1);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const config: LaunchdServiceConfig = {
|
|
298
|
+
label: options.name,
|
|
299
|
+
programArguments: [options.file, ...(options.args || [])],
|
|
300
|
+
workingDirectory: options.workingDir || process.cwd(),
|
|
301
|
+
environmentVariables: options.env ? JSON.parse(options.env) : {},
|
|
302
|
+
runAtLoad: options.autoStart !== false,
|
|
303
|
+
keepAlive: options.keepAlive !== false,
|
|
304
|
+
standardOutPath: options.logOut || join(homedir(), '.bs9', 'logs', `${options.name}.out.log`),
|
|
305
|
+
standardErrorPath: options.logErr || join(homedir(), '.bs9', 'logs', `${options.name}.err.log`)
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
await manager.createService(config);
|
|
309
|
+
break;
|
|
310
|
+
|
|
311
|
+
case 'start':
|
|
312
|
+
if (!options.name) {
|
|
313
|
+
console.error('❌ --name is required for start action');
|
|
314
|
+
process.exit(1);
|
|
315
|
+
}
|
|
316
|
+
await manager.startService(options.name);
|
|
317
|
+
break;
|
|
318
|
+
|
|
319
|
+
case 'stop':
|
|
320
|
+
if (!options.name) {
|
|
321
|
+
console.error('❌ --name is required for stop action');
|
|
322
|
+
process.exit(1);
|
|
323
|
+
}
|
|
324
|
+
await manager.stopService(options.name);
|
|
325
|
+
break;
|
|
326
|
+
|
|
327
|
+
case 'restart':
|
|
328
|
+
if (!options.name) {
|
|
329
|
+
console.error('❌ --name is required for restart action');
|
|
330
|
+
process.exit(1);
|
|
331
|
+
}
|
|
332
|
+
await manager.stopService(options.name);
|
|
333
|
+
await manager.startService(options.name);
|
|
334
|
+
break;
|
|
335
|
+
|
|
336
|
+
case 'unload':
|
|
337
|
+
if (!options.name) {
|
|
338
|
+
console.error('❌ --name is required for unload action');
|
|
339
|
+
process.exit(1);
|
|
340
|
+
}
|
|
341
|
+
await manager.unloadService(options.name);
|
|
342
|
+
break;
|
|
343
|
+
|
|
344
|
+
case 'status':
|
|
345
|
+
if (options.name) {
|
|
346
|
+
const status = await manager.getServiceStatus(options.name);
|
|
347
|
+
if (status) {
|
|
348
|
+
console.log(`📊 Service Status: ${status.label}`);
|
|
349
|
+
console.log(` Status: ${status.status}`);
|
|
350
|
+
if (status.pid) console.log(` PID: ${status.pid}`);
|
|
351
|
+
if (status.lastExitStatus !== undefined) console.log(` Last Exit Status: ${status.lastExitStatus}`);
|
|
352
|
+
} else {
|
|
353
|
+
console.log(`❌ Service '${options.name}' not found`);
|
|
354
|
+
}
|
|
355
|
+
} else {
|
|
356
|
+
const services = await manager.listServices();
|
|
357
|
+
console.log('📋 BS9 macOS Services:');
|
|
358
|
+
console.log('-'.repeat(80));
|
|
359
|
+
console.log('LABEL'.padEnd(30) + 'STATUS'.padEnd(15) + 'PID'.padEnd(10) + 'EXIT STATUS');
|
|
360
|
+
console.log('-'.repeat(80));
|
|
361
|
+
|
|
362
|
+
for (const service of services) {
|
|
363
|
+
console.log(
|
|
364
|
+
service.label.padEnd(30) +
|
|
365
|
+
service.status.padEnd(15) +
|
|
366
|
+
(service.pid?.toString() || '-').padEnd(10) +
|
|
367
|
+
(service.lastExitStatus?.toString() || '-')
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
if (services.length === 0) {
|
|
372
|
+
console.log('No BS9 macOS services found.');
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
break;
|
|
376
|
+
|
|
377
|
+
case 'enable':
|
|
378
|
+
if (!options.name) {
|
|
379
|
+
console.error('❌ --name is required for enable action');
|
|
380
|
+
process.exit(1);
|
|
381
|
+
}
|
|
382
|
+
await manager.enableAutoStart(options.name);
|
|
383
|
+
break;
|
|
384
|
+
|
|
385
|
+
case 'disable':
|
|
386
|
+
if (!options.name) {
|
|
387
|
+
console.error('❌ --name is required for disable action');
|
|
388
|
+
process.exit(1);
|
|
389
|
+
}
|
|
390
|
+
await manager.disableAutoStart(options.name);
|
|
391
|
+
break;
|
|
392
|
+
|
|
393
|
+
default:
|
|
394
|
+
console.error(`❌ Unknown action: ${action}`);
|
|
395
|
+
console.log('Available actions: create, start, stop, restart, unload, status, enable, disable');
|
|
396
|
+
process.exit(1);
|
|
397
|
+
}
|
|
398
|
+
} catch (error) {
|
|
399
|
+
console.error(`❌ Failed to ${action} macOS service: ${error}`);
|
|
400
|
+
process.exit(1);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { platform } from "node:os";
|
|
4
|
+
|
|
5
|
+
export type Platform = 'linux' | 'darwin' | 'win32';
|
|
6
|
+
|
|
7
|
+
export interface PlatformInfo {
|
|
8
|
+
platform: Platform;
|
|
9
|
+
isLinux: boolean;
|
|
10
|
+
isMacOS: boolean;
|
|
11
|
+
isWindows: boolean;
|
|
12
|
+
serviceManager: 'systemd' | 'launchd' | 'windows-service';
|
|
13
|
+
configDir: string;
|
|
14
|
+
logDir: string;
|
|
15
|
+
serviceDir: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getPlatformInfo(): PlatformInfo {
|
|
19
|
+
const currentPlatform = platform() as Platform;
|
|
20
|
+
|
|
21
|
+
const baseInfo: PlatformInfo = {
|
|
22
|
+
platform: currentPlatform,
|
|
23
|
+
isLinux: currentPlatform === 'linux',
|
|
24
|
+
isMacOS: currentPlatform === 'darwin',
|
|
25
|
+
isWindows: currentPlatform === 'win32',
|
|
26
|
+
serviceManager: 'systemd',
|
|
27
|
+
configDir: '',
|
|
28
|
+
logDir: '',
|
|
29
|
+
serviceDir: ''
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
switch (currentPlatform) {
|
|
33
|
+
case 'linux':
|
|
34
|
+
baseInfo.serviceManager = 'systemd';
|
|
35
|
+
baseInfo.configDir = `${process.env.HOME}/.config/bs9`;
|
|
36
|
+
baseInfo.logDir = `${process.env.HOME}/.local/share/bs9/logs`;
|
|
37
|
+
baseInfo.serviceDir = `${process.env.HOME}/.config/systemd/user`;
|
|
38
|
+
break;
|
|
39
|
+
|
|
40
|
+
case 'darwin':
|
|
41
|
+
baseInfo.serviceManager = 'launchd';
|
|
42
|
+
baseInfo.configDir = `${process.env.HOME}/.bs9`;
|
|
43
|
+
baseInfo.logDir = `${process.env.HOME}/.bs9/logs`;
|
|
44
|
+
baseInfo.serviceDir = `${process.env.HOME}/Library/LaunchAgents`;
|
|
45
|
+
break;
|
|
46
|
+
|
|
47
|
+
case 'win32':
|
|
48
|
+
baseInfo.serviceManager = 'windows-service';
|
|
49
|
+
baseInfo.configDir = `${process.env.USERPROFILE}/.bs9`;
|
|
50
|
+
baseInfo.logDir = `${process.env.USERPROFILE}/.bs9/logs`;
|
|
51
|
+
baseInfo.serviceDir = `${process.env.USERPROFILE}/.bs9/services`;
|
|
52
|
+
break;
|
|
53
|
+
|
|
54
|
+
default:
|
|
55
|
+
throw new Error(`Unsupported platform: ${currentPlatform}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return baseInfo;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function isSupportedPlatform(): boolean {
|
|
62
|
+
const supportedPlatforms: Platform[] = ['linux', 'darwin', 'win32'];
|
|
63
|
+
return supportedPlatforms.includes(platform() as Platform);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function getPlatformSpecificCommands(): string[] {
|
|
67
|
+
const currentPlatform = platform() as Platform;
|
|
68
|
+
|
|
69
|
+
switch (currentPlatform) {
|
|
70
|
+
case 'linux':
|
|
71
|
+
return ['start', 'stop', 'restart', 'status', 'logs', 'monit', 'web', 'alert', 'export', 'deps', 'profile', 'loadbalancer', 'dbpool'];
|
|
72
|
+
|
|
73
|
+
case 'darwin':
|
|
74
|
+
return ['start', 'stop', 'restart', 'status', 'logs', 'monit', 'web', 'alert', 'export', 'deps', 'profile', 'loadbalancer', 'dbpool', 'macos'];
|
|
75
|
+
|
|
76
|
+
case 'win32':
|
|
77
|
+
return ['start', 'stop', 'restart', 'status', 'logs', 'monit', 'web', 'alert', 'export', 'deps', 'profile', 'loadbalancer', 'dbpool', 'windows'];
|
|
78
|
+
|
|
79
|
+
default:
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function getPlatformHelp(): string {
|
|
85
|
+
const currentPlatform = platform() as Platform;
|
|
86
|
+
|
|
87
|
+
switch (currentPlatform) {
|
|
88
|
+
case 'linux':
|
|
89
|
+
return `
|
|
90
|
+
🐧 Linux Platform Features:
|
|
91
|
+
• Systemd-based service management
|
|
92
|
+
• User-mode service execution
|
|
93
|
+
• Advanced security hardening
|
|
94
|
+
• Resource limits and sandboxing
|
|
95
|
+
|
|
96
|
+
Available Commands:
|
|
97
|
+
${getPlatformSpecificCommands().join(', ')}
|
|
98
|
+
`;
|
|
99
|
+
|
|
100
|
+
case 'darwin':
|
|
101
|
+
return `
|
|
102
|
+
🍎 macOS Platform Features:
|
|
103
|
+
• Launchd service management
|
|
104
|
+
• Native macOS integration
|
|
105
|
+
• Automatic service recovery
|
|
106
|
+
• Standard macOS logging
|
|
107
|
+
|
|
108
|
+
Available Commands:
|
|
109
|
+
${getPlatformSpecificCommands().join(', ')}
|
|
110
|
+
|
|
111
|
+
macOS-specific:
|
|
112
|
+
• bs9 macos create - Create launchd service
|
|
113
|
+
• bs9 macos start - Start launchd service
|
|
114
|
+
• bs9 macos stop - Stop launchd service
|
|
115
|
+
`;
|
|
116
|
+
|
|
117
|
+
case 'win32':
|
|
118
|
+
return `
|
|
119
|
+
🪟 Windows Platform Features:
|
|
120
|
+
• Windows service management
|
|
121
|
+
• PowerShell-based automation
|
|
122
|
+
• Event log integration
|
|
123
|
+
• Service recovery policies
|
|
124
|
+
|
|
125
|
+
Available Commands:
|
|
126
|
+
${getPlatformSpecificCommands().join(', ')}
|
|
127
|
+
|
|
128
|
+
Windows-specific:
|
|
129
|
+
• bs9 windows create - Create Windows service
|
|
130
|
+
• bs9 windows start - Start Windows service
|
|
131
|
+
• bs9 windows stop - Stop Windows service
|
|
132
|
+
`;
|
|
133
|
+
|
|
134
|
+
default:
|
|
135
|
+
return `❌ Platform ${currentPlatform} is not supported`;
|
|
136
|
+
}
|
|
137
|
+
}
|