bm2 1.0.35 → 1.0.36
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 +145 -12
- package/package.json +1 -1
- package/src/daemon-v1.ts +244 -0
- package/src/daemon.ts +238 -195
- package/src/index.ts +856 -824
- package/src/types.ts +1 -0
- package/src/utils.ts +7 -7
package/src/index.ts
CHANGED
|
@@ -41,890 +41,910 @@ import type {
|
|
|
41
41
|
} from "./types";
|
|
42
42
|
import { statusColor } from "./colors";
|
|
43
43
|
import { liveWatchProcess, printProcessTable } from "./process-table";
|
|
44
|
-
import
|
|
44
|
+
import Daemon from "./daemon";
|
|
45
45
|
|
|
46
46
|
// ---------------------------------------------------------------------------
|
|
47
47
|
// Ensure directory structure exists
|
|
48
48
|
// ---------------------------------------------------------------------------
|
|
49
|
-
ensureDirs();
|
|
49
|
+
await ensureDirs();
|
|
50
50
|
|
|
51
51
|
// ---------------------------------------------------------------------------
|
|
52
|
-
//
|
|
52
|
+
// BM2CLI class
|
|
53
53
|
// ---------------------------------------------------------------------------
|
|
54
54
|
|
|
55
|
+
class BM2CLI {
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const pid = parseInt(readFileSync(DAEMON_PID_FILE, "utf-8").trim());
|
|
60
|
-
process.kill(pid, 0); // signal 0 — just check existence
|
|
61
|
-
return true;
|
|
62
|
-
} catch {
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
57
|
+
// -------------------------------------------------------------------------
|
|
58
|
+
// Daemon helpers
|
|
59
|
+
// -------------------------------------------------------------------------
|
|
66
60
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if (!(await stdout.exists())) await Bun.write(stdout, "");
|
|
77
|
-
if (!(await stderr.exists())) await Bun.write(stderr, "");
|
|
78
|
-
|
|
79
|
-
const child = Bun.spawn([bunPath, "run", daemonScript], {
|
|
80
|
-
stdout,
|
|
81
|
-
stderr,
|
|
82
|
-
stdin: "ignore",
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
// Detach so the daemon outlives the CLI
|
|
86
|
-
child.unref();
|
|
87
|
-
|
|
88
|
-
console.error(colorize("Starting daemon..", "green"));
|
|
89
|
-
|
|
90
|
-
// Wait for socket to appear
|
|
91
|
-
for (let i = 0; i < 100; i++) {
|
|
92
|
-
if (isDaemonRunning()) return;
|
|
93
|
-
|
|
94
|
-
await Bun.sleep(1_000);
|
|
95
|
-
console.error(colorize("Waiting for daemon..", "cyan"));
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (!isDaemonRunning()) {
|
|
99
|
-
throw new Error("Daemon failed to start (socket not found after 5 s)");
|
|
61
|
+
isDaemonRunning(): boolean {
|
|
62
|
+
if (!existsSync(DAEMON_PID_FILE)) return false;
|
|
63
|
+
try {
|
|
64
|
+
const pid = parseInt(readFileSync(DAEMON_PID_FILE, "utf-8").trim());
|
|
65
|
+
process.kill(pid, 0);
|
|
66
|
+
return true;
|
|
67
|
+
} catch {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
100
70
|
}
|
|
101
|
-
}
|
|
102
71
|
|
|
103
|
-
async
|
|
104
|
-
try {
|
|
105
|
-
|
|
106
|
-
if (!isDaemonRunning()) return;
|
|
72
|
+
async startDaemon(): Promise<void> {
|
|
107
73
|
|
|
108
|
-
|
|
109
|
-
const pid = Number(pidText);
|
|
74
|
+
if (this.isDaemonRunning()) return;
|
|
110
75
|
|
|
111
|
-
|
|
76
|
+
const daemonScript = join(import.meta.dir, "daemon.ts");
|
|
77
|
+
const bunPath = Bun.which("bun") || "bun";
|
|
112
78
|
|
|
113
|
-
|
|
79
|
+
const stdout = Bun.file(DAEMON_OUT_LOG_FILE);
|
|
80
|
+
const stderr = Bun.file(DAEMON_ERR_LOG_FILE);
|
|
114
81
|
|
|
115
|
-
|
|
116
|
-
await Bun.write(
|
|
117
|
-
} catch (err) {
|
|
118
|
-
console.error("Failed to stop daemon:", err);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
82
|
+
if (!(await stdout.exists())) await Bun.write(stdout, "");
|
|
83
|
+
if (!(await stderr.exists())) await Bun.write(stderr, "");
|
|
121
84
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
let res;
|
|
128
|
-
|
|
129
|
-
try {
|
|
130
|
-
|
|
131
|
-
const uri = `http://localhost/command`
|
|
132
|
-
|
|
133
|
-
res = await fetch(uri, {
|
|
134
|
-
unix: DAEMON_SOCKET,
|
|
135
|
-
method: "POST",
|
|
136
|
-
headers: {
|
|
137
|
-
"Content-Type": "application/json",
|
|
138
|
-
},
|
|
139
|
-
body: JSON.stringify(msg),
|
|
85
|
+
const child = Bun.spawn([bunPath, "run", daemonScript], {
|
|
86
|
+
stdout,
|
|
87
|
+
stderr,
|
|
88
|
+
stdin: "ignore",
|
|
140
89
|
});
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
90
|
+
|
|
91
|
+
child.unref();
|
|
92
|
+
|
|
93
|
+
console.error(colorize("Starting daemon..", "green"));
|
|
94
|
+
|
|
95
|
+
for (let i = 0; i < 100; i++) {
|
|
96
|
+
if (this.isDaemonRunning()) return;
|
|
97
|
+
await Bun.sleep(1_000);
|
|
98
|
+
console.error(colorize("Waiting for daemon..", "cyan"));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!this.isDaemonRunning()) {
|
|
102
|
+
throw new Error("Daemon failed to start (socket not found after 5 s)");
|
|
144
103
|
}
|
|
145
|
-
|
|
146
|
-
const resJson: DaemonResponse = await res.json() as DaemonResponse;
|
|
147
|
-
|
|
148
|
-
return resJson;
|
|
149
|
-
|
|
150
|
-
} catch (e: any) {
|
|
151
|
-
console.log("Results returned: " + await res?.text())
|
|
152
|
-
console.log()
|
|
153
|
-
console.log("sendToDaemon#Error:", e, e.stack)
|
|
154
|
-
return { type: "error", error: "Fetch Error", success: false }
|
|
155
104
|
}
|
|
156
|
-
|
|
157
|
-
}
|
|
158
105
|
|
|
106
|
+
async stopDaemon(): Promise<void> {
|
|
107
|
+
try {
|
|
108
|
+
if (!this.isDaemonRunning()) return;
|
|
159
109
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
110
|
+
const pidText = await Bun.file(DAEMON_PID_FILE).text();
|
|
111
|
+
const pid = Number(pidText);
|
|
112
|
+
|
|
113
|
+
process.kill(pid, "SIGTERM");
|
|
114
|
+
console.error("Daemon stopped");
|
|
115
|
+
|
|
116
|
+
await Bun.write(DAEMON_PID_FILE, "");
|
|
117
|
+
} catch (err) {
|
|
118
|
+
console.error("Failed to stop daemon:", err);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async sendToDaemon(msg: DaemonMessage): Promise<DaemonResponse> {
|
|
123
|
+
await this.startDaemon();
|
|
124
|
+
|
|
125
|
+
let res;
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
res = await fetch("http://localhost/command", {
|
|
129
|
+
unix: DAEMON_SOCKET,
|
|
130
|
+
method: "POST",
|
|
131
|
+
headers: { "Content-Type": "application/json" },
|
|
132
|
+
body: JSON.stringify(msg),
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
if (!res.ok) {
|
|
136
|
+
throw new Error(`Daemon error: ${res.status}`);
|
|
137
|
+
}
|
|
163
138
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
throw new Error(`Ecosystem file not found: ${abs}`);
|
|
139
|
+
return (await res.json()) as DaemonResponse;
|
|
140
|
+
} catch (e: any) {
|
|
141
|
+
console.log("Results returned: " + (await res?.text()));
|
|
142
|
+
console.log();
|
|
143
|
+
console.log("sendToDaemon#Error:", e, e.stack);
|
|
144
|
+
return { type: "error", error: "Fetch Error", success: false };
|
|
145
|
+
}
|
|
172
146
|
}
|
|
173
147
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
config = (mod.default || mod) as EcosystemConfig;
|
|
148
|
+
async callDaemonCmd(msg: DaemonMessage): Promise<DaemonResponse> {
|
|
149
|
+
try {
|
|
150
|
+
const d = new Daemon();
|
|
151
|
+
await d.initialize();
|
|
152
|
+
return d.handleMessage(msg);
|
|
153
|
+
} catch (e: any) {
|
|
154
|
+
console.log("callDaemonCmd:", e, e.stack);
|
|
155
|
+
return { type: "error", error: "Fetch Error", success: false };
|
|
156
|
+
}
|
|
184
157
|
}
|
|
185
|
-
|
|
186
|
-
|
|
158
|
+
|
|
159
|
+
// -------------------------------------------------------------------------
|
|
160
|
+
// Ecosystem config loader
|
|
161
|
+
// -------------------------------------------------------------------------
|
|
162
|
+
|
|
163
|
+
async loadEcosystemConfig(filePath: string): Promise<EcosystemConfig> {
|
|
187
164
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
return i;
|
|
191
|
-
})
|
|
192
|
-
|
|
193
|
-
//console.log("config===>", config.apps)
|
|
194
|
-
|
|
195
|
-
return config
|
|
196
|
-
}
|
|
165
|
+
const abs = resolve(filePath);
|
|
166
|
+
const file = Bun.file(abs);
|
|
197
167
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
168
|
+
if (!(await file.exists())) {
|
|
169
|
+
throw new Error(`Ecosystem file not found: ${abs}`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const ext = extname(abs);
|
|
173
|
+
let config: EcosystemConfig;
|
|
174
|
+
|
|
175
|
+
if (ext === ".json") {
|
|
176
|
+
config = (await file.json()) as EcosystemConfig;
|
|
177
|
+
} else {
|
|
178
|
+
const mod = await import(abs);
|
|
179
|
+
config = (mod.default || mod) as EcosystemConfig;
|
|
180
|
+
}
|
|
201
181
|
|
|
202
|
-
|
|
203
|
-
const opts: StartOptions = { script: scriptOrConfig };
|
|
182
|
+
const cwd = path.dirname(abs);
|
|
204
183
|
|
|
205
|
-
|
|
206
|
-
|
|
184
|
+
config.apps = config.apps.map((i) => {
|
|
185
|
+
if ((i.cwd || "").trim() === "") i.cwd = cwd;
|
|
186
|
+
return i;
|
|
187
|
+
});
|
|
207
188
|
|
|
208
|
-
|
|
209
|
-
|
|
189
|
+
return config;
|
|
190
|
+
}
|
|
210
191
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
break;
|
|
236
|
-
case "--watch-path":
|
|
237
|
-
if (!Array.isArray(opts.watch)) opts.watch = [];
|
|
238
|
-
(opts.watch as string[]).push(args[++i]!);
|
|
239
|
-
break;
|
|
240
|
-
case "--ignore-watch":
|
|
241
|
-
opts.ignoreWatch = args[++i]!.split(",");
|
|
242
|
-
break;
|
|
243
|
-
case "--exec-mode":
|
|
244
|
-
case "-x":
|
|
245
|
-
opts.execMode = args[++i] as "fork" | "cluster";
|
|
246
|
-
break;
|
|
247
|
-
case "--max-memory-restart":
|
|
248
|
-
opts.maxMemoryRestart = args[++i];
|
|
249
|
-
break;
|
|
250
|
-
case "--max-restarts":
|
|
251
|
-
opts.maxRestarts = parseInt(args[++i]!);
|
|
252
|
-
break;
|
|
253
|
-
case "--min-uptime":
|
|
254
|
-
opts.minUptime = parseInt(args[++i]!);
|
|
255
|
-
break;
|
|
256
|
-
case "--kill-timeout":
|
|
257
|
-
opts.killTimeout = parseInt(args[++i]!);
|
|
258
|
-
break;
|
|
259
|
-
case "--restart-delay":
|
|
260
|
-
opts.restartDelay = parseInt(args[++i]!);
|
|
261
|
-
break;
|
|
262
|
-
case "--cron":
|
|
263
|
-
case "--cron-restart":
|
|
264
|
-
opts.cron = args[++i];
|
|
265
|
-
break;
|
|
266
|
-
case "--no-autorestart":
|
|
267
|
-
opts.autorestart = false;
|
|
268
|
-
break;
|
|
269
|
-
case "--env": {
|
|
270
|
-
const envPair = args[++i]!;
|
|
271
|
-
const eqIdx = envPair.indexOf("=");
|
|
272
|
-
if (eqIdx !== -1) {
|
|
273
|
-
if (!opts.env) opts.env = {};
|
|
274
|
-
opts.env[envPair.substring(0, eqIdx)] = envPair.substring(eqIdx + 1);
|
|
275
|
-
}
|
|
276
|
-
break;
|
|
277
|
-
}
|
|
278
|
-
case "--log":
|
|
279
|
-
case "--output":
|
|
280
|
-
case "-o":
|
|
281
|
-
opts.outFile = args[++i];
|
|
282
|
-
break;
|
|
283
|
-
case "--error":
|
|
284
|
-
case "-e":
|
|
285
|
-
opts.errorFile = args[++i];
|
|
286
|
-
break;
|
|
287
|
-
case "--merge-logs":
|
|
288
|
-
opts.mergeLogs = true;
|
|
289
|
-
break;
|
|
290
|
-
case "--log-date-format":
|
|
291
|
-
opts.logDateFormat = args[++i];
|
|
292
|
-
break;
|
|
293
|
-
case "--log-max-size":
|
|
294
|
-
opts.logMaxSize = args[++i];
|
|
295
|
-
break;
|
|
296
|
-
case "--log-retain":
|
|
297
|
-
opts.logRetain = parseInt(args[++i]!);
|
|
298
|
-
break;
|
|
299
|
-
case "--log-compress":
|
|
300
|
-
opts.logCompress = true;
|
|
301
|
-
break;
|
|
302
|
-
case "--port":
|
|
303
|
-
case "-p":
|
|
304
|
-
opts.port = parseInt(args[++i]!);
|
|
305
|
-
break;
|
|
306
|
-
case "--health-check-url":
|
|
307
|
-
opts.healthCheckUrl = args[++i];
|
|
308
|
-
break;
|
|
309
|
-
case "--health-check-interval":
|
|
310
|
-
opts.healthCheckInterval = parseInt(args[++i]!);
|
|
311
|
-
break;
|
|
312
|
-
case "--health-check-timeout":
|
|
313
|
-
opts.healthCheckTimeout = parseInt(args[++i]!);
|
|
314
|
-
break;
|
|
315
|
-
case "--health-check-max-fails":
|
|
316
|
-
opts.healthCheckMaxFails = parseInt(args[++i]!);
|
|
317
|
-
break;
|
|
318
|
-
case "--wait-ready":
|
|
319
|
-
opts.waitReady = true;
|
|
320
|
-
break;
|
|
321
|
-
case "--listen-timeout":
|
|
322
|
-
opts.listenTimeout = parseInt(args[++i]!);
|
|
323
|
-
break;
|
|
324
|
-
case "--namespace":
|
|
325
|
-
opts.namespace = args[++i];
|
|
326
|
-
break;
|
|
327
|
-
case "--source-map-support":
|
|
328
|
-
opts.sourceMapSupport = true;
|
|
329
|
-
break;
|
|
330
|
-
case "--":
|
|
331
|
-
// Everything after -- is passed as script args
|
|
192
|
+
// -------------------------------------------------------------------------
|
|
193
|
+
// CLI flag parser
|
|
194
|
+
//
|
|
195
|
+
// Flags may appear in ANY order relative to the script path, e.g.:
|
|
196
|
+
// bm2 start app.ts --no-daemon --name api
|
|
197
|
+
// bm2 start --no-daemon app.ts --name api
|
|
198
|
+
// bm2 start --name api --no-daemon app.ts
|
|
199
|
+
//
|
|
200
|
+
// The first non-flag token encountered is treated as the script path.
|
|
201
|
+
// Every subsequent non-flag token (or tokens after --) becomes an arg.
|
|
202
|
+
// -------------------------------------------------------------------------
|
|
203
|
+
|
|
204
|
+
parseStartFlags(args: string[]): StartOptions {
|
|
205
|
+
const opts: StartOptions = { script: "", noDaemon: false };
|
|
206
|
+
|
|
207
|
+
let i = 0;
|
|
208
|
+
let scriptResolved = false;
|
|
209
|
+
const positionalArgs: string[] = [];
|
|
210
|
+
|
|
211
|
+
while (i < args.length) {
|
|
212
|
+
const arg = args[i]!;
|
|
213
|
+
|
|
214
|
+
// End-of-flags sentinel — everything after is passed to the script
|
|
215
|
+
if (arg === "--") {
|
|
332
216
|
positionalArgs.push(...args.slice(i + 1));
|
|
333
|
-
i = args.length;
|
|
334
217
|
break;
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
switch (arg) {
|
|
221
|
+
case "--name":
|
|
222
|
+
case "-n":
|
|
223
|
+
opts.name = args[++i];
|
|
224
|
+
break;
|
|
225
|
+
case "--instances":
|
|
226
|
+
case "-i":
|
|
227
|
+
opts.instances = parseInt(args[++i]!) || 1;
|
|
228
|
+
break;
|
|
229
|
+
case "--cwd":
|
|
230
|
+
opts.cwd = args[++i];
|
|
231
|
+
break;
|
|
232
|
+
case "--interpreter":
|
|
233
|
+
opts.interpreter = args[++i];
|
|
234
|
+
break;
|
|
235
|
+
case "--interpreter-args":
|
|
236
|
+
opts.interpreterArgs = args[++i]!.split(" ");
|
|
237
|
+
break;
|
|
238
|
+
case "--node-args":
|
|
239
|
+
opts.nodeArgs = args[++i]!.split(" ");
|
|
240
|
+
break;
|
|
241
|
+
case "--watch":
|
|
242
|
+
case "-w":
|
|
243
|
+
opts.watch = true;
|
|
244
|
+
break;
|
|
245
|
+
case "--watch-path":
|
|
246
|
+
if (!Array.isArray(opts.watch)) opts.watch = [];
|
|
247
|
+
(opts.watch as string[]).push(args[++i]!);
|
|
248
|
+
break;
|
|
249
|
+
case "--ignore-watch":
|
|
250
|
+
opts.ignoreWatch = args[++i]!.split(",");
|
|
251
|
+
break;
|
|
252
|
+
case "--exec-mode":
|
|
253
|
+
case "-x":
|
|
254
|
+
opts.execMode = args[++i] as "fork" | "cluster";
|
|
255
|
+
break;
|
|
256
|
+
case "--max-memory-restart":
|
|
257
|
+
opts.maxMemoryRestart = args[++i];
|
|
258
|
+
break;
|
|
259
|
+
case "--max-restarts":
|
|
260
|
+
opts.maxRestarts = parseInt(args[++i]!);
|
|
261
|
+
break;
|
|
262
|
+
case "--min-uptime":
|
|
263
|
+
opts.minUptime = parseInt(args[++i]!);
|
|
264
|
+
break;
|
|
265
|
+
case "--kill-timeout":
|
|
266
|
+
opts.killTimeout = parseInt(args[++i]!);
|
|
267
|
+
break;
|
|
268
|
+
case "--restart-delay":
|
|
269
|
+
opts.restartDelay = parseInt(args[++i]!);
|
|
270
|
+
break;
|
|
271
|
+
case "--cron":
|
|
272
|
+
case "--cron-restart":
|
|
273
|
+
opts.cron = args[++i];
|
|
274
|
+
break;
|
|
275
|
+
case "--no-autorestart":
|
|
276
|
+
opts.autorestart = false;
|
|
277
|
+
break;
|
|
278
|
+
case "--no-daemon":
|
|
279
|
+
case "-d":
|
|
280
|
+
opts.noDaemon = true;
|
|
281
|
+
break;
|
|
282
|
+
case "--env": {
|
|
283
|
+
const envPair = args[++i]!;
|
|
284
|
+
const eqIdx = envPair.indexOf("=");
|
|
285
|
+
if (eqIdx !== -1) {
|
|
286
|
+
if (!opts.env) opts.env = {};
|
|
287
|
+
opts.env[envPair.substring(0, eqIdx)] = envPair.substring(eqIdx + 1);
|
|
288
|
+
}
|
|
289
|
+
break;
|
|
338
290
|
}
|
|
339
|
-
|
|
291
|
+
case "--log":
|
|
292
|
+
case "--output":
|
|
293
|
+
case "-o":
|
|
294
|
+
opts.outFile = args[++i];
|
|
295
|
+
break;
|
|
296
|
+
case "--error":
|
|
297
|
+
case "-e":
|
|
298
|
+
opts.errorFile = args[++i];
|
|
299
|
+
break;
|
|
300
|
+
case "--merge-logs":
|
|
301
|
+
opts.mergeLogs = true;
|
|
302
|
+
break;
|
|
303
|
+
case "--log-date-format":
|
|
304
|
+
opts.logDateFormat = args[++i];
|
|
305
|
+
break;
|
|
306
|
+
case "--log-max-size":
|
|
307
|
+
opts.logMaxSize = args[++i];
|
|
308
|
+
break;
|
|
309
|
+
case "--log-retain":
|
|
310
|
+
opts.logRetain = parseInt(args[++i]!);
|
|
311
|
+
break;
|
|
312
|
+
case "--log-compress":
|
|
313
|
+
opts.logCompress = true;
|
|
314
|
+
break;
|
|
315
|
+
case "--port":
|
|
316
|
+
case "-p":
|
|
317
|
+
opts.port = parseInt(args[++i]!);
|
|
318
|
+
break;
|
|
319
|
+
case "--health-check-url":
|
|
320
|
+
opts.healthCheckUrl = args[++i];
|
|
321
|
+
break;
|
|
322
|
+
case "--health-check-interval":
|
|
323
|
+
opts.healthCheckInterval = parseInt(args[++i]!);
|
|
324
|
+
break;
|
|
325
|
+
case "--health-check-timeout":
|
|
326
|
+
opts.healthCheckTimeout = parseInt(args[++i]!);
|
|
327
|
+
break;
|
|
328
|
+
case "--health-check-max-fails":
|
|
329
|
+
opts.healthCheckMaxFails = parseInt(args[++i]!);
|
|
330
|
+
break;
|
|
331
|
+
case "--wait-ready":
|
|
332
|
+
opts.waitReady = true;
|
|
333
|
+
break;
|
|
334
|
+
case "--listen-timeout":
|
|
335
|
+
opts.listenTimeout = parseInt(args[++i]!);
|
|
336
|
+
break;
|
|
337
|
+
case "--namespace":
|
|
338
|
+
opts.namespace = args[++i];
|
|
339
|
+
break;
|
|
340
|
+
case "--source-map-support":
|
|
341
|
+
opts.sourceMapSupport = true;
|
|
342
|
+
break;
|
|
343
|
+
default:
|
|
344
|
+
if (arg.startsWith("-")) {
|
|
345
|
+
// Unknown flag — warn and skip
|
|
346
|
+
console.warn(colorize(`[bm2] Unknown flag ignored: ${arg}`, "dim"));
|
|
347
|
+
} else {
|
|
348
|
+
// Positional token: first one is the script, rest are script args
|
|
349
|
+
if (!scriptResolved) {
|
|
350
|
+
opts.script = arg;
|
|
351
|
+
scriptResolved = true;
|
|
352
|
+
} else {
|
|
353
|
+
positionalArgs.push(arg);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
i++;
|
|
340
360
|
}
|
|
341
|
-
i++;
|
|
342
|
-
}
|
|
343
361
|
|
|
344
|
-
|
|
345
|
-
|
|
362
|
+
if (positionalArgs.length > 0) opts.args = positionalArgs;
|
|
363
|
+
|
|
364
|
+
return opts;
|
|
346
365
|
}
|
|
347
366
|
|
|
348
|
-
|
|
349
|
-
|
|
367
|
+
// -------------------------------------------------------------------------
|
|
368
|
+
// Commands
|
|
369
|
+
// -------------------------------------------------------------------------
|
|
350
370
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
371
|
+
async cmdStart(args: string[]) {
|
|
372
|
+
if (args.length === 0) {
|
|
373
|
+
console.error(colorize("Usage: bm2 start <script|config> [options]", "red"));
|
|
374
|
+
process.exit(1);
|
|
375
|
+
}
|
|
354
376
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
377
|
+
// Peek at the first non-flag argument to decide if it's an ecosystem file.
|
|
378
|
+
// We do this before full parsing so we can branch early without consuming args.
|
|
379
|
+
const firstPositional = args.find((a) => !a.startsWith("-"));
|
|
380
|
+
|
|
381
|
+
if (!firstPositional) {
|
|
382
|
+
console.error(colorize("Usage: bm2 start <script|config> [options]", "red"));
|
|
383
|
+
process.exit(1);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const ext = extname(firstPositional);
|
|
387
|
+
|
|
388
|
+
if (
|
|
389
|
+
ext === ".json" ||
|
|
390
|
+
firstPositional.includes("ecosystem") ||
|
|
391
|
+
firstPositional.includes("bm2.config") ||
|
|
392
|
+
firstPositional.includes("pm2.config")
|
|
393
|
+
) {
|
|
394
|
+
const config = await this.loadEcosystemConfig(firstPositional);
|
|
395
|
+
const res = await this.sendToDaemon({ type: "ecosystem", data: config });
|
|
396
|
+
if (!res.success) {
|
|
397
|
+
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
398
|
+
process.exit(1);
|
|
399
|
+
}
|
|
400
|
+
printProcessTable(res.data);
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Parse all args — parseStartFlags finds the script itself
|
|
405
|
+
const opts = this.parseStartFlags(args);
|
|
406
|
+
|
|
407
|
+
if (!opts.script) {
|
|
408
|
+
console.error(colorize("Error: no script specified", "red"));
|
|
409
|
+
process.exit(1);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
opts.script = resolve(opts.script);
|
|
413
|
+
if (!opts.cwd) opts.cwd = path.dirname(opts.script);
|
|
414
|
+
|
|
415
|
+
const noDaemon = opts.noDaemon;
|
|
416
|
+
|
|
417
|
+
const res = noDaemon
|
|
418
|
+
? await this.callDaemonCmd({ type: "start", data: opts })
|
|
419
|
+
: await this.sendToDaemon({ type: "start", data: opts });
|
|
363
420
|
|
|
364
|
-
const ext = extname(scriptOrConfig);
|
|
365
|
-
|
|
366
|
-
// Ecosystem file
|
|
367
|
-
if (
|
|
368
|
-
ext === ".json" ||
|
|
369
|
-
scriptOrConfig.includes("ecosystem") ||
|
|
370
|
-
scriptOrConfig.includes("bm2.config") ||
|
|
371
|
-
scriptOrConfig.includes("pm2.config")
|
|
372
|
-
) {
|
|
373
|
-
const config = await loadEcosystemConfig(scriptOrConfig);
|
|
374
|
-
const res = await sendToDaemon({ type: "ecosystem", data: config });
|
|
375
421
|
if (!res.success) {
|
|
376
422
|
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
377
423
|
process.exit(1);
|
|
378
424
|
}
|
|
425
|
+
|
|
379
426
|
printProcessTable(res.data);
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
382
427
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
const cwd = path.dirname(opts.script);
|
|
388
|
-
|
|
389
|
-
opts.cwd = cwd;
|
|
390
|
-
|
|
391
|
-
const res = await sendToDaemon({ type: "start", data: opts });
|
|
392
|
-
|
|
393
|
-
if (!res.success) {
|
|
394
|
-
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
395
|
-
process.exit(1);
|
|
428
|
+
if (noDaemon) {
|
|
429
|
+
await new Promise(() => {});
|
|
430
|
+
}
|
|
396
431
|
}
|
|
397
|
-
printProcessTable(res.data);
|
|
398
|
-
}
|
|
399
432
|
|
|
400
|
-
async
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
433
|
+
async cmdStop(args: string[]) {
|
|
434
|
+
const target = args[0] || "all";
|
|
435
|
+
const type = target === "all" ? "stopAll" : "stop";
|
|
436
|
+
const data = target === "all" ? undefined : { target };
|
|
404
437
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
438
|
+
const res = await this.sendToDaemon({ type, data });
|
|
439
|
+
if (!res.success) {
|
|
440
|
+
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
441
|
+
process.exit(1);
|
|
442
|
+
}
|
|
443
|
+
printProcessTable(res.data);
|
|
409
444
|
}
|
|
410
|
-
printProcessTable(res.data);
|
|
411
|
-
}
|
|
412
445
|
|
|
413
|
-
async
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
446
|
+
async cmdRestart(args: string[]) {
|
|
447
|
+
const target = args[0] || "all";
|
|
448
|
+
const type = target === "all" ? "restartAll" : "restart";
|
|
449
|
+
const data = target === "all" ? undefined : { target };
|
|
417
450
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
451
|
+
const res = await this.sendToDaemon({ type, data });
|
|
452
|
+
if (!res.success) {
|
|
453
|
+
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
454
|
+
process.exit(1);
|
|
455
|
+
}
|
|
456
|
+
printProcessTable(res.data);
|
|
422
457
|
}
|
|
423
|
-
printProcessTable(res.data);
|
|
424
|
-
}
|
|
425
458
|
|
|
426
|
-
async
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
459
|
+
async cmdReload(args: string[]) {
|
|
460
|
+
const target = args[0] || "all";
|
|
461
|
+
const type = target === "all" ? "reloadAll" : "reload";
|
|
462
|
+
const data = target === "all" ? undefined : { target };
|
|
430
463
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
464
|
+
const res = await this.sendToDaemon({ type, data });
|
|
465
|
+
if (!res.success) {
|
|
466
|
+
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
467
|
+
process.exit(1);
|
|
468
|
+
}
|
|
469
|
+
printProcessTable(res.data);
|
|
435
470
|
}
|
|
436
|
-
printProcessTable(res.data);
|
|
437
|
-
}
|
|
438
471
|
|
|
439
|
-
async
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
472
|
+
async cmdDelete(args: string[]) {
|
|
473
|
+
const target = args[0] || "all";
|
|
474
|
+
const type = target === "all" ? "deleteAll" : "delete";
|
|
475
|
+
const data = target === "all" ? undefined : { target };
|
|
443
476
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
console.log(colorize("✓ Deleted", "green"));
|
|
451
|
-
printProcessTable(res.data);
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
async function cmdList(args: string[]) {
|
|
455
|
-
const res = await sendToDaemon({ type: "list" });
|
|
456
|
-
if (!res.success) {
|
|
457
|
-
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
458
|
-
process.exit(1);
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
let liveMode = false;
|
|
462
|
-
|
|
463
|
-
for (let arg of args) {
|
|
464
|
-
switch (arg) {
|
|
465
|
-
case "--live":
|
|
466
|
-
liveMode = true;
|
|
467
|
-
break;
|
|
468
|
-
default:
|
|
477
|
+
const res = await this.sendToDaemon({ type, data });
|
|
478
|
+
if (!res.success) {
|
|
479
|
+
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
480
|
+
process?.exit(1);
|
|
469
481
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
if (liveMode) {
|
|
473
|
-
liveWatchProcess(res.data)
|
|
474
|
-
} else {
|
|
482
|
+
|
|
483
|
+
console.log(colorize("✓ Deleted", "green"));
|
|
475
484
|
printProcessTable(res.data);
|
|
476
485
|
}
|
|
477
|
-
}
|
|
478
486
|
|
|
479
|
-
async
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
487
|
+
async cmdList(args: string[]) {
|
|
488
|
+
const res = await this.sendToDaemon({ type: "list" });
|
|
489
|
+
if (!res.success) {
|
|
490
|
+
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
491
|
+
process.exit(1);
|
|
492
|
+
}
|
|
485
493
|
|
|
486
|
-
|
|
487
|
-
if (!res.success) {
|
|
488
|
-
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
489
|
-
process.exit(1);
|
|
490
|
-
}
|
|
494
|
+
const liveMode = args.includes("--live");
|
|
491
495
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
console.log(` Exec mode : ${p.bm2_env.execMode}`);
|
|
498
|
-
console.log(` Instances : ${p.bm2_env.instances}`);
|
|
499
|
-
console.log(` Namespace : ${p.namespace || "default"}`);
|
|
500
|
-
console.log(` Script : ${p.bm2_env.script}`);
|
|
501
|
-
console.log(` CWD : ${p.bm2_env.cwd}`);
|
|
502
|
-
console.log(` Args : ${p.bm2_env.args.join(" ") || "(none)"}`);
|
|
503
|
-
console.log(` Interpreter : ${p.bm2_env.interpreter || "bun"}`);
|
|
504
|
-
console.log(` Restarts : ${p.bm2_env.restart_time}`);
|
|
505
|
-
console.log(` Unstable : ${p.bm2_env.unstable_restarts}`);
|
|
506
|
-
console.log(
|
|
507
|
-
` Uptime : ${
|
|
508
|
-
p.status === "online" ? formatUptime(Date.now() - p.bm2_env.pm_uptime) : "N/A"
|
|
509
|
-
}`
|
|
510
|
-
);
|
|
511
|
-
console.log(` Created at : ${new Date(p.bm2_env.created_at).toISOString()}`);
|
|
512
|
-
console.log(` CPU : ${p.monit.cpu.toFixed(1)}%`);
|
|
513
|
-
console.log(` Memory : ${formatBytes(p.monit.memory)}`);
|
|
514
|
-
if (p.monit.handles !== undefined)
|
|
515
|
-
console.log(` Handles : ${p.monit.handles}`);
|
|
516
|
-
if (p.monit.eventLoopLatency !== undefined)
|
|
517
|
-
console.log(` EL Latency : ${p.monit.eventLoopLatency.toFixed(2)} ms`);
|
|
518
|
-
console.log(` Watch : ${p.bm2_env.watch}`);
|
|
519
|
-
console.log(` Autorestart : ${p.bm2_env.autorestart}`);
|
|
520
|
-
console.log(` Max restarts : ${p.bm2_env.maxRestarts}`);
|
|
521
|
-
console.log(` Kill timeout : ${p.bm2_env.killTimeout} ms`);
|
|
522
|
-
if (p.bm2_env.healthCheckUrl)
|
|
523
|
-
console.log(` Health URL : ${p.bm2_env.healthCheckUrl}`);
|
|
524
|
-
if (p.bm2_env.cronRestart)
|
|
525
|
-
console.log(` Cron restart : ${p.bm2_env.cronRestart}`);
|
|
526
|
-
if (p.bm2_env.port)
|
|
527
|
-
console.log(` Port : ${p.bm2_env.port}`);
|
|
528
|
-
console.log();
|
|
496
|
+
if (liveMode) {
|
|
497
|
+
liveWatchProcess(res.data);
|
|
498
|
+
} else {
|
|
499
|
+
printProcessTable(res.data);
|
|
500
|
+
}
|
|
529
501
|
}
|
|
530
|
-
}
|
|
531
502
|
|
|
532
|
-
async
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
503
|
+
async cmdDescribe(args: string[]) {
|
|
504
|
+
const target = args[0];
|
|
505
|
+
if (!target) {
|
|
506
|
+
console.error(colorize("Usage: bm2 describe <id|name>", "red"));
|
|
507
|
+
process.exit(1);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
const res = await this.sendToDaemon({ type: "describe", data: { target } });
|
|
511
|
+
if (!res.success) {
|
|
512
|
+
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
513
|
+
process.exit(1);
|
|
514
|
+
}
|
|
539
515
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
516
|
+
const processes: ProcessState[] = res.data;
|
|
517
|
+
for (const p of processes) {
|
|
518
|
+
console.log(colorize(`\n─── ${p.name} (id: ${p.pm_id}) ───`, "bold"));
|
|
519
|
+
console.log(` Status : ${colorize(p.status, statusColor(p.status))}`);
|
|
520
|
+
console.log(` PID : ${p.pid || "N/A"}`);
|
|
521
|
+
console.log(` Exec mode : ${p.bm2_env.execMode}`);
|
|
522
|
+
console.log(` Instances : ${p.bm2_env.instances}`);
|
|
523
|
+
console.log(` Namespace : ${p.namespace || "default"}`);
|
|
524
|
+
console.log(` Script : ${p.bm2_env.script}`);
|
|
525
|
+
console.log(` CWD : ${p.bm2_env.cwd}`);
|
|
526
|
+
console.log(` Args : ${p.bm2_env.args.join(" ") || "(none)"}`);
|
|
527
|
+
console.log(` Interpreter : ${p.bm2_env.interpreter || "bun"}`);
|
|
528
|
+
console.log(` Restarts : ${p.bm2_env.restart_time}`);
|
|
529
|
+
console.log(` Unstable : ${p.bm2_env.unstable_restarts}`);
|
|
530
|
+
console.log(
|
|
531
|
+
` Uptime : ${
|
|
532
|
+
p.status === "online" ? formatUptime(Date.now() - p.bm2_env.pm_uptime) : "N/A"
|
|
533
|
+
}`
|
|
534
|
+
);
|
|
535
|
+
console.log(` Created at : ${new Date(p.bm2_env.created_at).toISOString()}`);
|
|
536
|
+
console.log(` CPU : ${p.monit.cpu.toFixed(1)}%`);
|
|
537
|
+
console.log(` Memory : ${formatBytes(p.monit.memory)}`);
|
|
538
|
+
if (p.monit.handles !== undefined) console.log(` Handles : ${p.monit.handles}`);
|
|
539
|
+
if (p.monit.eventLoopLatency !== undefined)
|
|
540
|
+
console.log(` EL Latency : ${p.monit.eventLoopLatency.toFixed(2)} ms`);
|
|
541
|
+
console.log(` Watch : ${p.bm2_env.watch}`);
|
|
542
|
+
console.log(` Autorestart : ${p.bm2_env.autorestart}`);
|
|
543
|
+
console.log(` Max restarts : ${p.bm2_env.maxRestarts}`);
|
|
544
|
+
console.log(` Kill timeout : ${p.bm2_env.killTimeout} ms`);
|
|
545
|
+
if (p.bm2_env.healthCheckUrl) console.log(` Health URL : ${p.bm2_env.healthCheckUrl}`);
|
|
546
|
+
if (p.bm2_env.cronRestart) console.log(` Cron restart : ${p.bm2_env.cronRestart}`);
|
|
547
|
+
if (p.bm2_env.port) console.log(` Port : ${p.bm2_env.port}`);
|
|
548
|
+
console.log();
|
|
549
|
+
}
|
|
544
550
|
}
|
|
545
551
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
552
|
+
async cmdLogs(args: string[]) {
|
|
553
|
+
const target = args[0] || "all";
|
|
554
|
+
let lines = 20;
|
|
555
|
+
const linesIdx = args.indexOf("--lines");
|
|
556
|
+
if (linesIdx !== -1 && args[linesIdx + 1]) {
|
|
557
|
+
lines = parseInt(args[linesIdx + 1]!);
|
|
551
558
|
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
559
|
+
|
|
560
|
+
const res = await this.sendToDaemon({ type: "logs", data: { target, lines } });
|
|
561
|
+
if (!res.success) {
|
|
562
|
+
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
563
|
+
process.exit(1);
|
|
555
564
|
}
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
565
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
566
|
+
for (const log of res.data) {
|
|
567
|
+
console.log(colorize(`\n─── ${log.name} (id: ${log.id}) ───`, "bold"));
|
|
568
|
+
if (log.out) {
|
|
569
|
+
console.log(colorize("--- stdout ---", "dim"));
|
|
570
|
+
console.log(log.out);
|
|
571
|
+
}
|
|
572
|
+
if (log.err) {
|
|
573
|
+
console.log(colorize("--- stderr ---", "red"));
|
|
574
|
+
console.log(log.err);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
565
577
|
}
|
|
566
|
-
console.log(colorize("✓ Logs flushed", "green"));
|
|
567
|
-
}
|
|
568
578
|
|
|
569
|
-
async
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
579
|
+
async cmdFlush(args: string[]) {
|
|
580
|
+
const target = args[0];
|
|
581
|
+
const res = await this.sendToDaemon({ type: "flush", data: target ? { target } : undefined });
|
|
582
|
+
if (!res.success) {
|
|
583
|
+
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
584
|
+
process.exit(1);
|
|
585
|
+
}
|
|
586
|
+
console.log(colorize("✓ Logs flushed", "green"));
|
|
575
587
|
}
|
|
576
588
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
}
|
|
589
|
+
async cmdScale(args: string[]) {
|
|
590
|
+
const target = args[0];
|
|
591
|
+
const count = parseInt(args[1]!);
|
|
592
|
+
if (!target || isNaN(count)) {
|
|
593
|
+
console.error(colorize("Usage: bm2 scale <name|id> <count>", "red"));
|
|
594
|
+
process.exit(1);
|
|
595
|
+
}
|
|
584
596
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
597
|
+
const res = await this.sendToDaemon({ type: "scale", data: { target, count } });
|
|
598
|
+
if (!res.success) {
|
|
599
|
+
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
600
|
+
process.exit(1);
|
|
601
|
+
}
|
|
602
|
+
printProcessTable(res.data);
|
|
590
603
|
}
|
|
591
|
-
console.log(colorize("✓ Process list saved", "green"));
|
|
592
|
-
}
|
|
593
604
|
|
|
594
|
-
async
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
605
|
+
async cmdSave() {
|
|
606
|
+
const res = await this.sendToDaemon({ type: "save" });
|
|
607
|
+
if (!res.success) {
|
|
608
|
+
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
609
|
+
process.exit(1);
|
|
610
|
+
}
|
|
611
|
+
console.log(colorize("✓ Process list saved", "green"));
|
|
599
612
|
}
|
|
600
|
-
printProcessTable(res.data);
|
|
601
|
-
}
|
|
602
613
|
|
|
603
|
-
async
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
614
|
+
async cmdResurrect() {
|
|
615
|
+
const res = await this.sendToDaemon({ type: "resurrect" });
|
|
616
|
+
if (!res.success) {
|
|
617
|
+
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
618
|
+
process.exit(1);
|
|
619
|
+
}
|
|
620
|
+
printProcessTable(res.data);
|
|
609
621
|
}
|
|
610
622
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
}
|
|
623
|
+
async cmdSignal(args: string[]) {
|
|
624
|
+
const signal = args[0];
|
|
625
|
+
const target = args[1];
|
|
626
|
+
if (!signal || !target) {
|
|
627
|
+
console.error(colorize("Usage: bm2 sendSignal <signal> <id|name>", "red"));
|
|
628
|
+
process.exit(1);
|
|
629
|
+
}
|
|
618
630
|
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
631
|
+
const res = await this.sendToDaemon({ type: "signal", data: { target, signal } });
|
|
632
|
+
if (!res.success) {
|
|
633
|
+
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
634
|
+
process.exit(1);
|
|
635
|
+
}
|
|
636
|
+
console.log(colorize(`✓ Signal ${signal} sent to ${target}`, "green"));
|
|
625
637
|
}
|
|
626
|
-
console.log(colorize("✓ Restart counters reset", "green"));
|
|
627
|
-
printProcessTable(res.data);
|
|
628
|
-
}
|
|
629
638
|
|
|
630
|
-
async
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
639
|
+
async cmdReset(args: string[]) {
|
|
640
|
+
const target = args[0] || "all";
|
|
641
|
+
const res = await this.sendToDaemon({ type: "reset", data: { target } });
|
|
642
|
+
if (!res.success) {
|
|
643
|
+
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
644
|
+
process.exit(1);
|
|
645
|
+
}
|
|
646
|
+
console.log(colorize("✓ Restart counters reset", "green"));
|
|
647
|
+
printProcessTable(res.data);
|
|
635
648
|
}
|
|
636
649
|
|
|
637
|
-
|
|
638
|
-
|
|
650
|
+
async cmdMonit() {
|
|
651
|
+
const res = await this.sendToDaemon({ type: "metrics" });
|
|
652
|
+
if (!res.success) {
|
|
653
|
+
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
654
|
+
process.exit(1);
|
|
655
|
+
}
|
|
639
656
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
console.log(` CPUs : ${snapshot.system.cpuCount}`);
|
|
643
|
-
console.log(` Memory : ${formatBytes(snapshot.system.totalMemory - snapshot.system.freeMemory)} / ${formatBytes(snapshot.system.totalMemory)}`);
|
|
644
|
-
console.log(` Load avg : ${snapshot.system.loadAvg.map((l: number) => l.toFixed(2)).join(", ")}`);
|
|
645
|
-
console.log();
|
|
657
|
+
const snapshot = res.data;
|
|
658
|
+
console.log(colorize("\n⚡ BM2 Monitor\n", "bold"));
|
|
646
659
|
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
660
|
+
console.log(colorize("System:", "cyan"));
|
|
661
|
+
console.log(` Platform : ${snapshot.system.platform}`);
|
|
662
|
+
console.log(` CPUs : ${snapshot.system.cpuCount}`);
|
|
650
663
|
console.log(
|
|
651
|
-
`
|
|
664
|
+
` Memory : ${formatBytes(
|
|
665
|
+
snapshot.system.totalMemory - snapshot.system.freeMemory
|
|
666
|
+
)} / ${formatBytes(snapshot.system.totalMemory)}`
|
|
652
667
|
);
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
}
|
|
668
|
+
console.log(` Load avg : ${snapshot.system.loadAvg.map((l: number) => l.toFixed(2)).join(", ")}`);
|
|
669
|
+
console.log();
|
|
656
670
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
671
|
+
console.log(colorize("Processes:", "cyan"));
|
|
672
|
+
for (const p of snapshot.processes) {
|
|
673
|
+
const statusStr = colorize(padRight(p.status, 14), statusColor(p.status));
|
|
674
|
+
console.log(
|
|
675
|
+
` ${padRight(String(p.id), 4)} ${padRight(p.name, 20)} ${statusStr} CPU: ${padRight(
|
|
676
|
+
p.cpu.toFixed(1) + "%",
|
|
677
|
+
8
|
|
678
|
+
)} MEM: ${padRight(formatBytes(p.memory), 10)} ↺ ${p.restarts}`
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
console.log();
|
|
682
|
+
}
|
|
660
683
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
if (mIdx !== -1 && args[mIdx + 1]) metricsPort = parseInt(args[mIdx + 1]!);
|
|
684
|
+
async cmdDashboard(args: string[]) {
|
|
685
|
+
let port = DASHBOARD_PORT;
|
|
686
|
+
let metricsPort = METRICS_PORT;
|
|
665
687
|
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
}
|
|
671
|
-
console.log(colorize(`✓ Dashboard running at http://localhost:${res.data.port}`, "green"));
|
|
672
|
-
console.log(colorize(` Prometheus metrics at http://localhost:${res.data.metricsPort}/metrics`, "dim"));
|
|
673
|
-
}
|
|
688
|
+
const portIdx = args.indexOf("--port");
|
|
689
|
+
if (portIdx !== -1 && args[portIdx + 1]) port = parseInt(args[portIdx + 1]!);
|
|
690
|
+
const mIdx = args.indexOf("--metrics-port");
|
|
691
|
+
if (mIdx !== -1 && args[mIdx + 1]) metricsPort = parseInt(args[mIdx + 1]!);
|
|
674
692
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
693
|
+
const res = await this.sendToDaemon({ type: "dashboard", data: { port, metricsPort } });
|
|
694
|
+
if (!res.success) {
|
|
695
|
+
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
696
|
+
process.exit(1);
|
|
697
|
+
}
|
|
698
|
+
console.log(colorize(`✓ Dashboard running at http://localhost:${res.data.port}`, "green"));
|
|
699
|
+
console.log(
|
|
700
|
+
colorize(` Prometheus metrics at http://localhost:${res.data.metricsPort}/metrics`, "dim")
|
|
701
|
+
);
|
|
680
702
|
}
|
|
681
|
-
console.log(colorize("✓ Dashboard stopped", "green"));
|
|
682
|
-
}
|
|
683
703
|
|
|
684
|
-
async
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
console.log(` PID : ${res.data.pid}`);
|
|
690
|
-
console.log(` Uptime : ${formatUptime(res.data.uptime * 1000)}`);
|
|
691
|
-
} else {
|
|
692
|
-
console.log(colorize("✗ Daemon responded with error", "red"));
|
|
704
|
+
async cmdDashboardStop() {
|
|
705
|
+
const res = await this.sendToDaemon({ type: "dashboardStop" });
|
|
706
|
+
if (!res.success) {
|
|
707
|
+
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
708
|
+
process.exit(1);
|
|
693
709
|
}
|
|
694
|
-
|
|
695
|
-
console.log(colorize("✗ Daemon is not running", "red"));
|
|
710
|
+
console.log(colorize("✓ Dashboard stopped", "green"));
|
|
696
711
|
}
|
|
697
|
-
}
|
|
698
712
|
|
|
699
|
-
async
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
713
|
+
async cmdPing() {
|
|
714
|
+
try {
|
|
715
|
+
const res = await this.sendToDaemon({ type: "ping" });
|
|
716
|
+
if (res.success) {
|
|
717
|
+
console.log(colorize("✓ Daemon is alive", "green"));
|
|
718
|
+
console.log(` PID : ${res.data.pid}`);
|
|
719
|
+
console.log(` Uptime : ${formatUptime(res.data.uptime * 1000)}`);
|
|
720
|
+
} else {
|
|
721
|
+
console.log(colorize("✗ Daemon responded with error", "red"));
|
|
722
|
+
}
|
|
723
|
+
} catch {
|
|
724
|
+
console.log(colorize("✗ Daemon is not running", "red"));
|
|
725
|
+
}
|
|
704
726
|
}
|
|
705
727
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
} catch {}
|
|
713
|
-
|
|
714
|
-
console.log(colorize("✓ Daemon killed", "green"));
|
|
715
|
-
}
|
|
728
|
+
async cmdKill() {
|
|
729
|
+
try {
|
|
730
|
+
await this.sendToDaemon({ type: "kill" });
|
|
731
|
+
} catch {
|
|
732
|
+
// Expected — daemon exits
|
|
733
|
+
}
|
|
716
734
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
735
|
+
try {
|
|
736
|
+
if (existsSync(DAEMON_SOCKET)) unlinkSync(DAEMON_SOCKET);
|
|
737
|
+
} catch {}
|
|
738
|
+
try {
|
|
739
|
+
if (existsSync(DAEMON_PID_FILE)) unlinkSync(DAEMON_PID_FILE);
|
|
740
|
+
} catch {}
|
|
720
741
|
|
|
721
|
-
|
|
722
|
-
console.error(colorize("Usage: bm2 deploy <config> <environment> [setup]", "red"));
|
|
723
|
-
process.exit(1);
|
|
742
|
+
console.log(colorize("✓ Daemon killed", "green"));
|
|
724
743
|
}
|
|
725
744
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
console.error(colorize(`Deploy environment "${environment}" not found in config`, "red"));
|
|
730
|
-
process.exit(1);
|
|
731
|
-
}
|
|
745
|
+
async cmdDeploy(args: string[]) {
|
|
746
|
+
const configFile = args[0];
|
|
747
|
+
const environment = args[1];
|
|
732
748
|
|
|
733
|
-
|
|
734
|
-
|
|
749
|
+
if (!configFile || !environment) {
|
|
750
|
+
console.error(colorize("Usage: bm2 deploy <config> <environment> [setup]", "red"));
|
|
751
|
+
process.exit(1);
|
|
752
|
+
}
|
|
735
753
|
|
|
736
|
-
|
|
737
|
-
await deployer.setup(deployConfig);
|
|
738
|
-
} else {
|
|
739
|
-
await deployer.deploy(deployConfig, args[2]);
|
|
740
|
-
}
|
|
741
|
-
}
|
|
754
|
+
const config = await this.loadEcosystemConfig(configFile);
|
|
742
755
|
|
|
743
|
-
|
|
744
|
-
|
|
756
|
+
if (!config.deploy || !config.deploy[environment]) {
|
|
757
|
+
console.error(colorize(`Deploy environment "${environment}" not found in config`, "red"));
|
|
758
|
+
process.exit(1);
|
|
759
|
+
}
|
|
745
760
|
|
|
746
|
-
|
|
747
|
-
const
|
|
748
|
-
console.log(result);
|
|
749
|
-
return;
|
|
750
|
-
}
|
|
761
|
+
const deployConfig = config.deploy[environment]!;
|
|
762
|
+
const deployer = new DeployManager();
|
|
751
763
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
764
|
+
if (args[2] === "setup") {
|
|
765
|
+
await deployer.setup(deployConfig);
|
|
766
|
+
} else {
|
|
767
|
+
await deployer.deploy(deployConfig, args[2]);
|
|
768
|
+
}
|
|
756
769
|
}
|
|
757
770
|
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
console.log(content);
|
|
761
|
-
}
|
|
771
|
+
async cmdStartup(args: string[]) {
|
|
772
|
+
const startup = new StartupManager();
|
|
762
773
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
switch (subCmd) {
|
|
768
|
-
case "set": {
|
|
769
|
-
const name = args[1];
|
|
770
|
-
const key = args[2];
|
|
771
|
-
const value = args[3];
|
|
772
|
-
if (!name || !key || value === undefined) {
|
|
773
|
-
console.error(colorize("Usage: bm2 env set <name> <key> <value>", "red"));
|
|
774
|
-
process.exit(1);
|
|
775
|
-
}
|
|
776
|
-
await envMgr.setEnv(name, key, value);
|
|
777
|
-
console.log(colorize(`✓ Set ${key}=${value} for ${name}`, "green"));
|
|
778
|
-
break;
|
|
774
|
+
if (args[0] === "remove" || args[0] === "uninstall") {
|
|
775
|
+
console.log(await startup.uninstall());
|
|
776
|
+
return;
|
|
779
777
|
}
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
process.exit(1);
|
|
785
|
-
}
|
|
786
|
-
const env = await envMgr.getEnv(name);
|
|
787
|
-
for (const [k, v] of Object.entries(env)) {
|
|
788
|
-
console.log(`${colorize(k, "cyan")}=${v}`);
|
|
789
|
-
}
|
|
790
|
-
break;
|
|
778
|
+
|
|
779
|
+
if (args[0] === "install") {
|
|
780
|
+
console.log(await startup.install());
|
|
781
|
+
return;
|
|
791
782
|
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
783
|
+
|
|
784
|
+
console.log(await startup.generate(args[0]));
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
async cmdEnv(args: string[]) {
|
|
788
|
+
const envMgr = new EnvManager();
|
|
789
|
+
const subCmd = args[0];
|
|
790
|
+
|
|
791
|
+
switch (subCmd) {
|
|
792
|
+
case "set": {
|
|
793
|
+
const name = args[1];
|
|
794
|
+
const key = args[2];
|
|
795
|
+
const value = args[3];
|
|
796
|
+
if (!name || !key || value === undefined) {
|
|
797
|
+
console.error(colorize("Usage: bm2 env set <name> <key> <value>", "red"));
|
|
798
|
+
process.exit(1);
|
|
799
|
+
}
|
|
800
|
+
await envMgr.setEnv(name, key, value);
|
|
801
|
+
console.log(colorize(`✓ Set ${key}=${value} for ${name}`, "green"));
|
|
802
|
+
break;
|
|
799
803
|
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
console.log(colorize(`\n${name}:`, "bold"));
|
|
804
|
+
case "get": {
|
|
805
|
+
const name = args[1];
|
|
806
|
+
if (!name) {
|
|
807
|
+
console.error(colorize("Usage: bm2 env get <name>", "red"));
|
|
808
|
+
process.exit(1);
|
|
809
|
+
}
|
|
810
|
+
const env = await envMgr.getEnv(name);
|
|
808
811
|
for (const [k, v] of Object.entries(env)) {
|
|
809
|
-
console.log(
|
|
812
|
+
console.log(`${colorize(k, "cyan")}=${v}`);
|
|
813
|
+
}
|
|
814
|
+
break;
|
|
815
|
+
}
|
|
816
|
+
case "delete":
|
|
817
|
+
case "rm": {
|
|
818
|
+
const name = args[1];
|
|
819
|
+
const key = args[2];
|
|
820
|
+
if (!name) {
|
|
821
|
+
console.error(colorize("Usage: bm2 env delete <name> [key]", "red"));
|
|
822
|
+
process.exit(1);
|
|
810
823
|
}
|
|
824
|
+
await envMgr.deleteEnv(name, key);
|
|
825
|
+
console.log(colorize("✓ Deleted", "green"));
|
|
826
|
+
break;
|
|
827
|
+
}
|
|
828
|
+
case "list": {
|
|
829
|
+
const all = await envMgr.getEnvs();
|
|
830
|
+
for (const [name, env] of Object.entries(all)) {
|
|
831
|
+
console.log(colorize(`\n${name}:`, "bold"));
|
|
832
|
+
for (const [k, v] of Object.entries(env)) {
|
|
833
|
+
console.log(` ${colorize(k, "cyan")}=${v}`);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
break;
|
|
811
837
|
}
|
|
812
|
-
|
|
838
|
+
default:
|
|
839
|
+
console.error(colorize("Usage: bm2 env <set|get|delete|list> ...", "red"));
|
|
840
|
+
process.exit(1);
|
|
813
841
|
}
|
|
814
|
-
default:
|
|
815
|
-
console.error(colorize("Usage: bm2 env <set|get|delete|list> ...", "red"));
|
|
816
|
-
process.exit(1);
|
|
817
842
|
}
|
|
818
|
-
}
|
|
819
843
|
|
|
820
|
-
async
|
|
821
|
-
|
|
844
|
+
async cmdModule(args: string[]) {
|
|
845
|
+
const subCmd = args[0];
|
|
822
846
|
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
847
|
+
switch (subCmd) {
|
|
848
|
+
case "install": {
|
|
849
|
+
const mod = args[1];
|
|
850
|
+
if (!mod) {
|
|
851
|
+
console.error(colorize("Usage: bm2 module install <name|url|path>", "red"));
|
|
852
|
+
process.exit(1);
|
|
853
|
+
}
|
|
854
|
+
const res = await this.sendToDaemon({ type: "moduleInstall", data: { module: mod } });
|
|
855
|
+
if (!res.success) {
|
|
856
|
+
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
857
|
+
process.exit(1);
|
|
858
|
+
}
|
|
859
|
+
console.log(colorize(`✓ Module installed at ${res.data.path}`, "green"));
|
|
860
|
+
break;
|
|
829
861
|
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
862
|
+
case "uninstall":
|
|
863
|
+
case "remove": {
|
|
864
|
+
const mod = args[1];
|
|
865
|
+
if (!mod) {
|
|
866
|
+
console.error(colorize("Usage: bm2 module uninstall <name>", "red"));
|
|
867
|
+
process.exit(1);
|
|
868
|
+
}
|
|
869
|
+
const res = await this.sendToDaemon({ type: "moduleUninstall", data: { module: mod } });
|
|
870
|
+
if (!res.success) {
|
|
871
|
+
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
872
|
+
process.exit(1);
|
|
873
|
+
}
|
|
874
|
+
console.log(colorize("✓ Module uninstalled", "green"));
|
|
875
|
+
break;
|
|
834
876
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
877
|
+
case "list":
|
|
878
|
+
case "ls": {
|
|
879
|
+
const res = await this.sendToDaemon({ type: "moduleList" });
|
|
880
|
+
if (!res.success) {
|
|
881
|
+
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
882
|
+
process.exit(1);
|
|
883
|
+
}
|
|
884
|
+
if (res.data.length === 0) {
|
|
885
|
+
console.log(colorize("No modules installed", "dim"));
|
|
886
|
+
} else {
|
|
887
|
+
for (const m of res.data) {
|
|
888
|
+
console.log(` ${colorize(m.name, "cyan")} @ ${m.version}`);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
break;
|
|
844
892
|
}
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
893
|
+
default:
|
|
894
|
+
console.error(colorize("Usage: bm2 module <install|uninstall|list> ...", "red"));
|
|
848
895
|
process.exit(1);
|
|
849
|
-
}
|
|
850
|
-
console.log(colorize("✓ Module uninstalled", "green"));
|
|
851
|
-
break;
|
|
852
896
|
}
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
console.log(colorize("No modules installed", "dim"));
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
async cmdDaemon(args: string[]) {
|
|
900
|
+
const subCmd = args[0];
|
|
901
|
+
|
|
902
|
+
const daemonStatus = () => {
|
|
903
|
+
if (this.isDaemonRunning()) {
|
|
904
|
+
console.log(colorize("running", "green"));
|
|
862
905
|
} else {
|
|
863
|
-
|
|
864
|
-
console.log(` ${colorize(m.name, "cyan")} @ ${m.version}`);
|
|
865
|
-
}
|
|
906
|
+
console.error(colorize("stopped", "red"));
|
|
866
907
|
}
|
|
867
|
-
break;
|
|
868
|
-
}
|
|
869
|
-
default:
|
|
870
|
-
console.error(colorize("Usage: bm2 module <install|uninstall|list> ...", "red"));
|
|
871
908
|
process.exit(1);
|
|
872
|
-
|
|
873
|
-
}
|
|
909
|
+
};
|
|
874
910
|
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
911
|
+
switch (subCmd) {
|
|
912
|
+
case "status":
|
|
913
|
+
daemonStatus();
|
|
914
|
+
break;
|
|
915
|
+
case "start":
|
|
916
|
+
await this.startDaemon();
|
|
917
|
+
process.exit(0);
|
|
918
|
+
break;
|
|
919
|
+
case "stop":
|
|
920
|
+
await this.stopDaemon();
|
|
921
|
+
process.exit(0);
|
|
922
|
+
break;
|
|
923
|
+
case "reload":
|
|
924
|
+
await this.stopDaemon();
|
|
925
|
+
await this.startDaemon();
|
|
926
|
+
process.exit(0);
|
|
927
|
+
break;
|
|
928
|
+
default:
|
|
929
|
+
console.error(colorize("Usage: bm2 daemon <status|start|stop|reload>", "red"));
|
|
930
|
+
process.exit(1);
|
|
883
931
|
}
|
|
884
|
-
|
|
885
|
-
process.exit(1);
|
|
886
932
|
}
|
|
887
|
-
|
|
888
|
-
const subCmd = args[0];
|
|
889
|
-
|
|
890
|
-
switch (subCmd) {
|
|
891
|
-
case "status":
|
|
892
|
-
daemonStatus();
|
|
893
|
-
break;
|
|
894
|
-
case "start":
|
|
895
|
-
await startDaemon();
|
|
896
|
-
process.exit(1);
|
|
897
|
-
break
|
|
898
|
-
case "stop":
|
|
899
|
-
await stopDaemon();
|
|
900
|
-
process.exit(1);
|
|
901
|
-
break;
|
|
902
|
-
case "reload":
|
|
903
|
-
await stopDaemon();
|
|
904
|
-
await startDaemon();
|
|
905
|
-
process.exit(1);
|
|
906
|
-
break;
|
|
907
|
-
default:
|
|
908
|
-
console.error(colorize("Usage: bm2 daemon <status|start|stop|reload>", "red"));
|
|
909
|
-
process.exit(1);
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
}
|
|
913
933
|
|
|
914
|
-
async
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
934
|
+
async cmdPrometheus() {
|
|
935
|
+
const res = await this.sendToDaemon({ type: "prometheus" });
|
|
936
|
+
if (!res.success) {
|
|
937
|
+
console.error(colorize(`Error: ${res.error}`, "red"));
|
|
938
|
+
process.exit(1);
|
|
939
|
+
}
|
|
940
|
+
console.log(res.data);
|
|
919
941
|
}
|
|
920
|
-
console.log(res.data);
|
|
921
|
-
}
|
|
922
942
|
|
|
923
|
-
//
|
|
924
|
-
// Help
|
|
925
|
-
//
|
|
943
|
+
// -------------------------------------------------------------------------
|
|
944
|
+
// Help
|
|
945
|
+
// -------------------------------------------------------------------------
|
|
926
946
|
|
|
927
|
-
|
|
947
|
+
printHelp() {
|
|
928
948
|
console.log(`
|
|
929
949
|
${colorize("BM2", "bold")} ${colorize(`v${VERSION}`, "dim")} — Bun Process Manager
|
|
930
950
|
|
|
@@ -973,10 +993,10 @@ function printHelp() {
|
|
|
973
993
|
${colorize("Daemon:", "cyan")}
|
|
974
994
|
daemon status Returns the status of the daemon
|
|
975
995
|
daemon start Starts the daemon
|
|
976
|
-
daemon
|
|
996
|
+
daemon stop Stops the daemon
|
|
977
997
|
daemon reload Reloads the daemon
|
|
978
998
|
|
|
979
|
-
${colorize("
|
|
999
|
+
${colorize("Other:", "cyan")}
|
|
980
1000
|
ping Check if daemon is alive
|
|
981
1001
|
kill Kill the daemon and all processes
|
|
982
1002
|
sendSignal <sig> <id|name> Send OS signal to process
|
|
@@ -984,7 +1004,7 @@ function printHelp() {
|
|
|
984
1004
|
${colorize("Start Options:", "dim")}
|
|
985
1005
|
--name, -n <name> Process name
|
|
986
1006
|
--instances, -i <N> Number of instances (cluster)
|
|
987
|
-
--exec-mode, -x <mode>
|
|
1007
|
+
--exec-mode, -x <mode> fork or cluster
|
|
988
1008
|
--watch, -w Watch for file changes
|
|
989
1009
|
--cwd <path> Working directory
|
|
990
1010
|
--interpreter <bin> Custom interpreter
|
|
@@ -995,6 +1015,7 @@ function printHelp() {
|
|
|
995
1015
|
--port, -p <port> Base port for cluster
|
|
996
1016
|
--env <KEY=VALUE> Set environment variable
|
|
997
1017
|
--no-autorestart Disable auto-restart
|
|
1018
|
+
--no-daemon, -d Run without daemon (blocks)
|
|
998
1019
|
--log, -o <file> Custom stdout log path
|
|
999
1020
|
--error, -e <file> Custom stderr log path
|
|
1000
1021
|
--namespace <ns> Namespace grouping
|
|
@@ -1005,6 +1026,8 @@ function printHelp() {
|
|
|
1005
1026
|
${colorize("Examples:", "dim")}
|
|
1006
1027
|
bm2 start app.ts
|
|
1007
1028
|
bm2 start server.ts --name api -i 4 --watch
|
|
1029
|
+
bm2 start --no-daemon app.ts
|
|
1030
|
+
bm2 start --name api --no-daemon server.ts
|
|
1008
1031
|
bm2 start ecosystem.config.ts
|
|
1009
1032
|
bm2 restart api
|
|
1010
1033
|
bm2 scale api 8
|
|
@@ -1012,117 +1035,126 @@ function printHelp() {
|
|
|
1012
1035
|
bm2 monit
|
|
1013
1036
|
bm2 save && bm2 resurrect
|
|
1014
1037
|
`);
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
const command =
|
|
1023
|
-
const commandArgs =
|
|
1024
|
-
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
// -------------------------------------------------------------------------
|
|
1041
|
+
// Main dispatch
|
|
1042
|
+
// -------------------------------------------------------------------------
|
|
1043
|
+
|
|
1044
|
+
async run(argv: string[]) {
|
|
1045
|
+
const command = argv[0];
|
|
1046
|
+
const commandArgs = argv.slice(1);
|
|
1047
|
+
|
|
1025
1048
|
switch (command) {
|
|
1026
|
-
|
|
1027
|
-
await cmdStart(commandArgs);
|
|
1049
|
+
case "start":
|
|
1050
|
+
await this.cmdStart(commandArgs);
|
|
1028
1051
|
break;
|
|
1029
|
-
|
|
1030
|
-
await cmdStop(commandArgs);
|
|
1052
|
+
case "stop":
|
|
1053
|
+
await this.cmdStop(commandArgs);
|
|
1031
1054
|
break;
|
|
1032
|
-
|
|
1033
|
-
await cmdRestart(commandArgs);
|
|
1055
|
+
case "restart":
|
|
1056
|
+
await this.cmdRestart(commandArgs);
|
|
1034
1057
|
break;
|
|
1035
|
-
|
|
1036
|
-
await cmdReload(commandArgs);
|
|
1058
|
+
case "reload":
|
|
1059
|
+
await this.cmdReload(commandArgs);
|
|
1037
1060
|
break;
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
await cmdDelete(commandArgs);
|
|
1061
|
+
case "delete":
|
|
1062
|
+
case "del":
|
|
1063
|
+
case "rm":
|
|
1064
|
+
await this.cmdDelete(commandArgs);
|
|
1042
1065
|
break;
|
|
1043
|
-
|
|
1044
|
-
await cmdScale(commandArgs);
|
|
1066
|
+
case "scale":
|
|
1067
|
+
await this.cmdScale(commandArgs);
|
|
1045
1068
|
break;
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
await cmdList(commandArgs);
|
|
1069
|
+
case "list":
|
|
1070
|
+
case "ls":
|
|
1071
|
+
case "status":
|
|
1072
|
+
await this.cmdList(commandArgs);
|
|
1050
1073
|
break;
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
await cmdDescribe(commandArgs);
|
|
1074
|
+
case "describe":
|
|
1075
|
+
case "show":
|
|
1076
|
+
case "info":
|
|
1077
|
+
await this.cmdDescribe(commandArgs);
|
|
1055
1078
|
break;
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
await cmdLogs(commandArgs);
|
|
1079
|
+
case "logs":
|
|
1080
|
+
case "log":
|
|
1081
|
+
await this.cmdLogs(commandArgs);
|
|
1059
1082
|
break;
|
|
1060
|
-
|
|
1061
|
-
await cmdFlush(commandArgs);
|
|
1083
|
+
case "flush":
|
|
1084
|
+
await this.cmdFlush(commandArgs);
|
|
1062
1085
|
break;
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
await cmdMonit();
|
|
1086
|
+
case "monit":
|
|
1087
|
+
case "monitor":
|
|
1088
|
+
await this.cmdMonit();
|
|
1066
1089
|
break;
|
|
1067
|
-
|
|
1090
|
+
case "dashboard":
|
|
1068
1091
|
if (commandArgs[0] === "stop") {
|
|
1069
|
-
|
|
1092
|
+
await this.cmdDashboardStop();
|
|
1070
1093
|
} else {
|
|
1071
|
-
|
|
1094
|
+
await this.cmdDashboard(commandArgs);
|
|
1072
1095
|
}
|
|
1073
1096
|
break;
|
|
1074
|
-
|
|
1075
|
-
await cmdPrometheus();
|
|
1097
|
+
case "prometheus":
|
|
1098
|
+
await this.cmdPrometheus();
|
|
1076
1099
|
break;
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
await cmdSave();
|
|
1100
|
+
case "save":
|
|
1101
|
+
case "dump":
|
|
1102
|
+
await this.cmdSave();
|
|
1080
1103
|
break;
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
await cmdResurrect();
|
|
1104
|
+
case "resurrect":
|
|
1105
|
+
case "restore":
|
|
1106
|
+
await this.cmdResurrect();
|
|
1084
1107
|
break;
|
|
1085
|
-
|
|
1086
|
-
await cmdReset(commandArgs);
|
|
1108
|
+
case "reset":
|
|
1109
|
+
await this.cmdReset(commandArgs);
|
|
1087
1110
|
break;
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
await cmdSignal(commandArgs);
|
|
1111
|
+
case "sendSignal":
|
|
1112
|
+
case "signal":
|
|
1113
|
+
await this.cmdSignal(commandArgs);
|
|
1091
1114
|
break;
|
|
1092
|
-
|
|
1093
|
-
await cmdPing();
|
|
1115
|
+
case "ping":
|
|
1116
|
+
await this.cmdPing();
|
|
1094
1117
|
break;
|
|
1095
|
-
|
|
1096
|
-
await cmdKill();
|
|
1118
|
+
case "kill":
|
|
1119
|
+
await this.cmdKill();
|
|
1097
1120
|
break;
|
|
1098
|
-
|
|
1099
|
-
await cmdDeploy(commandArgs);
|
|
1121
|
+
case "deploy":
|
|
1122
|
+
await this.cmdDeploy(commandArgs);
|
|
1100
1123
|
break;
|
|
1101
|
-
|
|
1102
|
-
await cmdStartup(commandArgs);
|
|
1124
|
+
case "startup":
|
|
1125
|
+
await this.cmdStartup(commandArgs);
|
|
1103
1126
|
break;
|
|
1104
|
-
|
|
1105
|
-
await cmdEnv(commandArgs);
|
|
1127
|
+
case "env":
|
|
1128
|
+
await this.cmdEnv(commandArgs);
|
|
1106
1129
|
break;
|
|
1107
|
-
|
|
1108
|
-
await cmdModule(commandArgs);
|
|
1130
|
+
case "module":
|
|
1131
|
+
await this.cmdModule(commandArgs);
|
|
1109
1132
|
break;
|
|
1110
|
-
|
|
1111
|
-
await cmdDaemon(commandArgs);
|
|
1133
|
+
case "daemon":
|
|
1134
|
+
await this.cmdDaemon(commandArgs);
|
|
1112
1135
|
break;
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1136
|
+
case "version":
|
|
1137
|
+
case "-v":
|
|
1138
|
+
case "--version":
|
|
1116
1139
|
console.log(`${APP_NAME} v${VERSION}`);
|
|
1117
1140
|
break;
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
printHelp();
|
|
1141
|
+
case "help":
|
|
1142
|
+
case "-h":
|
|
1143
|
+
case "--help":
|
|
1144
|
+
case undefined:
|
|
1145
|
+
this.printHelp();
|
|
1123
1146
|
break;
|
|
1124
|
-
|
|
1147
|
+
default:
|
|
1125
1148
|
console.error(colorize(`Unknown command: ${command}`, "red"));
|
|
1126
1149
|
console.error(`Run ${colorize("bm2 --help", "cyan")} for usage information.`);
|
|
1127
1150
|
process.exit(1);
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1128
1153
|
}
|
|
1154
|
+
|
|
1155
|
+
// ---------------------------------------------------------------------------
|
|
1156
|
+
// Entrypoint
|
|
1157
|
+
// ---------------------------------------------------------------------------
|
|
1158
|
+
|
|
1159
|
+
const cli = new BM2CLI();
|
|
1160
|
+
await cli.run(process.argv.slice(2));
|