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