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