bm2 1.0.35 → 1.0.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -12
- package/package.json +1 -1
- package/src/daemon-v1.ts +244 -0
- package/src/daemon.ts +238 -195
- package/src/index.ts +856 -824
- package/src/types.ts +1 -0
- package/src/utils.ts +7 -7
package/src/daemon.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* License: GPL-3.0-only
|
|
14
14
|
* Author: Zak <zak@maxxpainn.com>
|
|
15
15
|
*/
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
import { ProcessManager } from "./process-manager";
|
|
18
18
|
import { Dashboard } from "./dashboard";
|
|
19
19
|
import { ModuleManager } from "./module-manager";
|
|
@@ -24,221 +24,264 @@ import {
|
|
|
24
24
|
METRICS_PORT,
|
|
25
25
|
} from "./constants";
|
|
26
26
|
import { ensureDirs } from "./utils";
|
|
27
|
-
import { unlinkSync, existsSync } from "fs";
|
|
28
27
|
import type { DaemonMessage, DaemonResponse } from "./types";
|
|
29
|
-
import type {
|
|
28
|
+
import type { Server } from "bun";
|
|
30
29
|
|
|
31
|
-
ensureDirs();
|
|
32
30
|
|
|
33
|
-
|
|
34
|
-
const pm = new ProcessManager();
|
|
35
|
-
const dashboard = new Dashboard(pm);
|
|
36
|
-
const moduleManager = new ModuleManager(pm);
|
|
31
|
+
export default class Daemon {
|
|
37
32
|
|
|
38
|
-
|
|
33
|
+
initialized: boolean = false;
|
|
39
34
|
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
server: Server<any> | null = null;
|
|
36
|
+
pm: ProcessManager | null = null;
|
|
37
|
+
dashboard: Dashboard | null = null;
|
|
38
|
+
moduleManager: ModuleManager | null = null;
|
|
39
|
+
metricsInterval: NodeJS.Timeout | null = null;
|
|
40
|
+
args = process.argv.slice(2);
|
|
42
41
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
debugMode: boolean = false;
|
|
43
|
+
daemonEnabled: boolean = true;
|
|
44
|
+
|
|
45
|
+
// ── Bound once so Bun.serve always has the right `this` ──────────────────
|
|
46
|
+
private boundFetch = (req: Request) => this.handleServerRequests(req);
|
|
47
|
+
|
|
48
|
+
getServerOpts = () => ({
|
|
49
|
+
unix: DAEMON_SOCKET,
|
|
50
|
+
fetch: this.boundFetch,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
async initialize(_daemonEnabled: boolean = true) {
|
|
55
|
+
|
|
56
|
+
await ensureDirs();
|
|
47
57
|
|
|
48
|
-
|
|
49
|
-
|
|
58
|
+
this.daemonEnabled = _daemonEnabled;
|
|
59
|
+
this.pm = new ProcessManager();
|
|
60
|
+
this.dashboard = new Dashboard(this.pm);
|
|
61
|
+
this.moduleManager = new ModuleManager(this.pm);
|
|
50
62
|
|
|
51
|
-
|
|
52
|
-
|
|
63
|
+
this.args = process.argv.slice(2);
|
|
64
|
+
this.debugMode = this.args.includes("--debug");
|
|
53
65
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
66
|
+
if (_daemonEnabled) {
|
|
67
|
+
|
|
68
|
+
const sock = Bun.file(DAEMON_SOCKET);
|
|
69
|
+
|
|
70
|
+
// Clean up existing socket
|
|
71
|
+
if (await sock.exists()) {
|
|
72
|
+
try { await sock.delete(); } catch {}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Write PID file
|
|
76
|
+
await Bun.write(DAEMON_PID_FILE, String(process.pid));
|
|
77
|
+
|
|
78
|
+
}
|
|
58
79
|
|
|
80
|
+
// Load modules
|
|
81
|
+
await this.moduleManager.loadAll();
|
|
59
82
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
83
|
+
this.metricsInterval = setInterval(() => {
|
|
84
|
+
this.pm!.getMetrics();
|
|
85
|
+
}, 2000);
|
|
86
|
+
|
|
87
|
+
this.initialized = true;
|
|
88
|
+
|
|
89
|
+
} // end initialize
|
|
90
|
+
|
|
91
|
+
async handleServerRequests(req: Request): Promise<Response> {
|
|
92
|
+
|
|
93
|
+
if (req.method !== "POST") {
|
|
94
|
+
return Response.json(
|
|
95
|
+
{ type: "error", error: "Method Not Allowed", success: false },
|
|
96
|
+
{ status: 405 }
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
|
|
102
|
+
const msg = (await req.json()) as DaemonMessage;
|
|
103
|
+
const response = await this.handleMessage(msg);
|
|
104
|
+
return Response.json(response);
|
|
105
|
+
|
|
106
|
+
} catch (err: any) {
|
|
107
|
+
return Response.json(
|
|
108
|
+
{ type: "error", error: err.message, success: false },
|
|
109
|
+
{ status: 500 }
|
|
110
|
+
);
|
|
111
|
+
}
|
|
67
112
|
}
|
|
68
113
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return Response.json(
|
|
79
|
-
{ type: "error", error: err.message, success: false },
|
|
80
|
-
{ status: 500 }
|
|
81
|
-
);
|
|
114
|
+
// initialize MUST be called before startServer
|
|
115
|
+
startServer(): Server<any> {
|
|
116
|
+
|
|
117
|
+
if (!this.initialized) {
|
|
118
|
+
throw new Error("Daemon.initialize() must be called before startServer()");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
this.server = Bun.serve(this.getServerOpts());
|
|
122
|
+
return this.server;
|
|
82
123
|
}
|
|
83
|
-
};
|
|
84
124
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
fetch: handleServerRequests
|
|
88
|
-
}
|
|
125
|
+
async handleMessage(msg: DaemonMessage): Promise<DaemonResponse> {
|
|
126
|
+
try {
|
|
89
127
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
switch (msg.type) {
|
|
93
|
-
case "start": {
|
|
94
|
-
const states = await pm.start(msg.data);
|
|
95
|
-
return { type: "start", data: states, success: true, id: msg.id };
|
|
96
|
-
}
|
|
97
|
-
case "stop": {
|
|
98
|
-
const states = await pm.stop(msg.data.target);
|
|
99
|
-
return { type: "stop", data: states, success: true, id: msg.id };
|
|
100
|
-
}
|
|
101
|
-
case "restart": {
|
|
102
|
-
const states = await pm.restart(msg.data.target);
|
|
103
|
-
return { type: "restart", data: states, success: true, id: msg.id };
|
|
104
|
-
}
|
|
105
|
-
case "reload": {
|
|
106
|
-
const states = await pm.reload(msg.data.target);
|
|
107
|
-
return { type: "reload", data: states, success: true, id: msg.id };
|
|
108
|
-
}
|
|
109
|
-
case "delete": {
|
|
110
|
-
const states = await pm.del(msg.data.target);
|
|
111
|
-
return { type: "delete", data: states, success: true, id: msg.id };
|
|
112
|
-
}
|
|
113
|
-
case "scale": {
|
|
114
|
-
const states = await pm.scale(msg.data.target, msg.data.count);
|
|
115
|
-
return { type: "scale", data: states, success: true, id: msg.id };
|
|
116
|
-
}
|
|
117
|
-
case "stopAll": {
|
|
118
|
-
const states = await pm.stopAll();
|
|
119
|
-
return { type: "stopAll", data: states, success: true, id: msg.id };
|
|
120
|
-
}
|
|
121
|
-
case "restartAll": {
|
|
122
|
-
const states = await pm.restartAll();
|
|
123
|
-
return { type: "restartAll", data: states, success: true, id: msg.id };
|
|
124
|
-
}
|
|
125
|
-
case "reloadAll": {
|
|
126
|
-
const states = await pm.reloadAll();
|
|
127
|
-
return { type: "reloadAll", data: states, success: true, id: msg.id };
|
|
128
|
-
}
|
|
129
|
-
case "deleteAll": {
|
|
130
|
-
const states = await pm.deleteAll();
|
|
131
|
-
return { type: "deleteAll", data: states, success: true, id: msg.id };
|
|
132
|
-
}
|
|
133
|
-
case "list": {
|
|
134
|
-
return { type: "list", data: pm.list(), success: true, id: msg.id };
|
|
135
|
-
}
|
|
136
|
-
case "describe": {
|
|
137
|
-
return { type: "describe", data: pm.describe(msg.data.target), success: true, id: msg.id };
|
|
138
|
-
}
|
|
139
|
-
case "logs": {
|
|
140
|
-
const logs = await pm.getLogs(msg.data.target, msg.data.lines);
|
|
141
|
-
return { type: "logs", data: logs, success: true, id: msg.id };
|
|
142
|
-
}
|
|
143
|
-
case "flush": {
|
|
144
|
-
await pm.flushLogs(msg.data?.target);
|
|
145
|
-
return { type: "flush", success: true, id: msg.id };
|
|
146
|
-
}
|
|
147
|
-
case "save": {
|
|
148
|
-
await pm.save();
|
|
149
|
-
return { type: "save", success: true, id: msg.id };
|
|
150
|
-
}
|
|
151
|
-
case "resurrect": {
|
|
152
|
-
const states = await pm.resurrect();
|
|
153
|
-
return { type: "resurrect", data: states, success: true, id: msg.id };
|
|
154
|
-
}
|
|
155
|
-
case "ecosystem": {
|
|
156
|
-
const states = await pm.startEcosystem(msg.data);
|
|
157
|
-
return { type: "ecosystem", data: states, success: true, id: msg.id };
|
|
158
|
-
}
|
|
159
|
-
case "signal": {
|
|
160
|
-
await pm.sendSignal(msg.data.target, msg.data.signal);
|
|
161
|
-
return { type: "signal", success: true, id: msg.id };
|
|
162
|
-
}
|
|
163
|
-
case "reset": {
|
|
164
|
-
const states = await pm.reset(msg.data.target);
|
|
165
|
-
return { type: "reset", data: states, success: true, id: msg.id };
|
|
166
|
-
}
|
|
167
|
-
case "metrics": {
|
|
168
|
-
const metrics = await pm.getMetrics();
|
|
169
|
-
return { type: "metrics", data: metrics, success: true, id: msg.id };
|
|
170
|
-
}
|
|
171
|
-
case "metricsHistory": {
|
|
172
|
-
const history = pm.getMetricsHistory(msg.data?.seconds || 300);
|
|
173
|
-
return { type: "metricsHistory", data: history, success: true, id: msg.id };
|
|
174
|
-
}
|
|
175
|
-
case "prometheus": {
|
|
176
|
-
const prom = pm.getPrometheusMetrics();
|
|
177
|
-
return { type: "prometheus", data: prom, success: true, id: msg.id };
|
|
178
|
-
}
|
|
179
|
-
case "dashboard": {
|
|
180
|
-
const port = msg.data?.port || DASHBOARD_PORT;
|
|
181
|
-
const metricsPort = msg.data?.metricsPort || METRICS_PORT;
|
|
182
|
-
dashboard.start(port, metricsPort);
|
|
183
|
-
return { type: "dashboard", data: { port, metricsPort }, success: true, id: msg.id };
|
|
128
|
+
if (!this.initialized) {
|
|
129
|
+
await this.initialize();
|
|
184
130
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
type: "
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
131
|
+
|
|
132
|
+
const pm = this.pm!;
|
|
133
|
+
const dashboard = this.dashboard!;
|
|
134
|
+
const moduleManager = this.moduleManager!;
|
|
135
|
+
const metricsInterval = this.metricsInterval!;
|
|
136
|
+
|
|
137
|
+
switch (msg.type) {
|
|
138
|
+
case "start": {
|
|
139
|
+
const states = await pm.start(msg.data);
|
|
140
|
+
return { type: "start", data: states, success: true, id: msg.id };
|
|
141
|
+
}
|
|
142
|
+
case "stop": {
|
|
143
|
+
const states = await pm.stop(msg.data.target);
|
|
144
|
+
return { type: "stop", data: states, success: true, id: msg.id };
|
|
145
|
+
}
|
|
146
|
+
case "restart": {
|
|
147
|
+
const states = await pm.restart(msg.data.target);
|
|
148
|
+
return { type: "restart", data: states, success: true, id: msg.id };
|
|
149
|
+
}
|
|
150
|
+
case "reload": {
|
|
151
|
+
const states = await pm.reload(msg.data.target);
|
|
152
|
+
return { type: "reload", data: states, success: true, id: msg.id };
|
|
153
|
+
}
|
|
154
|
+
case "delete": {
|
|
155
|
+
const states = await pm.del(msg.data.target);
|
|
156
|
+
return { type: "delete", data: states, success: true, id: msg.id };
|
|
157
|
+
}
|
|
158
|
+
case "scale": {
|
|
159
|
+
const states = await pm.scale(msg.data.target, msg.data.count);
|
|
160
|
+
return { type: "scale", data: states, success: true, id: msg.id };
|
|
161
|
+
}
|
|
162
|
+
case "stopAll": {
|
|
163
|
+
const states = await pm.stopAll();
|
|
164
|
+
return { type: "stopAll", data: states, success: true, id: msg.id };
|
|
165
|
+
}
|
|
166
|
+
case "restartAll": {
|
|
167
|
+
const states = await pm.restartAll();
|
|
168
|
+
return { type: "restartAll", data: states, success: true, id: msg.id };
|
|
169
|
+
}
|
|
170
|
+
case "reloadAll": {
|
|
171
|
+
const states = await pm.reloadAll();
|
|
172
|
+
return { type: "reloadAll", data: states, success: true, id: msg.id };
|
|
173
|
+
}
|
|
174
|
+
case "deleteAll": {
|
|
175
|
+
const states = await pm.deleteAll();
|
|
176
|
+
return { type: "deleteAll", data: states, success: true, id: msg.id };
|
|
177
|
+
}
|
|
178
|
+
case "list": {
|
|
179
|
+
return { type: "list", data: pm.list(), success: true, id: msg.id };
|
|
180
|
+
}
|
|
181
|
+
case "describe": {
|
|
182
|
+
return { type: "describe", data: pm.describe(msg.data.target), success: true, id: msg.id };
|
|
183
|
+
}
|
|
184
|
+
case "logs": {
|
|
185
|
+
const logs = await pm.getLogs(msg.data.target, msg.data.lines);
|
|
186
|
+
return { type: "logs", data: logs, success: true, id: msg.id };
|
|
187
|
+
}
|
|
188
|
+
case "flush": {
|
|
189
|
+
await pm.flushLogs(msg.data?.target);
|
|
190
|
+
return { type: "flush", success: true, id: msg.id };
|
|
191
|
+
}
|
|
192
|
+
case "save": {
|
|
193
|
+
await pm.save();
|
|
194
|
+
return { type: "save", success: true, id: msg.id };
|
|
195
|
+
}
|
|
196
|
+
case "resurrect": {
|
|
197
|
+
const states = await pm.resurrect();
|
|
198
|
+
return { type: "resurrect", data: states, success: true, id: msg.id };
|
|
199
|
+
}
|
|
200
|
+
case "ecosystem": {
|
|
201
|
+
const states = await pm.startEcosystem(msg.data);
|
|
202
|
+
return { type: "ecosystem", data: states, success: true, id: msg.id };
|
|
203
|
+
}
|
|
204
|
+
case "signal": {
|
|
205
|
+
await pm.sendSignal(msg.data.target, msg.data.signal);
|
|
206
|
+
return { type: "signal", success: true, id: msg.id };
|
|
207
|
+
}
|
|
208
|
+
case "reset": {
|
|
209
|
+
const states = await pm.reset(msg.data.target);
|
|
210
|
+
return { type: "reset", data: states, success: true, id: msg.id };
|
|
211
|
+
}
|
|
212
|
+
case "metrics": {
|
|
213
|
+
const metrics = await pm.getMetrics();
|
|
214
|
+
return { type: "metrics", data: metrics, success: true, id: msg.id };
|
|
215
|
+
}
|
|
216
|
+
case "metricsHistory": {
|
|
217
|
+
const history = pm.getMetricsHistory(msg.data?.seconds || 300);
|
|
218
|
+
return { type: "metricsHistory", data: history, success: true, id: msg.id };
|
|
219
|
+
}
|
|
220
|
+
case "prometheus": {
|
|
221
|
+
const prom = pm.getPrometheusMetrics();
|
|
222
|
+
return { type: "prometheus", data: prom, success: true, id: msg.id };
|
|
223
|
+
}
|
|
224
|
+
case "dashboard": {
|
|
225
|
+
const port = msg.data?.port || DASHBOARD_PORT;
|
|
226
|
+
const metricsPort = msg.data?.metricsPort || METRICS_PORT;
|
|
227
|
+
dashboard.start(port, metricsPort);
|
|
228
|
+
return { type: "dashboard", data: { port, metricsPort }, success: true, id: msg.id };
|
|
229
|
+
}
|
|
230
|
+
case "dashboardStop": {
|
|
231
|
+
dashboard.stop();
|
|
232
|
+
return { type: "dashboardStop", success: true, id: msg.id };
|
|
233
|
+
}
|
|
234
|
+
case "moduleInstall": {
|
|
235
|
+
const path = await moduleManager.install(msg.data.module);
|
|
236
|
+
return { type: "moduleInstall", data: { path }, success: true, id: msg.id };
|
|
237
|
+
}
|
|
238
|
+
case "moduleUninstall": {
|
|
239
|
+
await moduleManager.uninstall(msg.data.module);
|
|
240
|
+
return { type: "moduleUninstall", success: true, id: msg.id };
|
|
241
|
+
}
|
|
242
|
+
case "moduleList": {
|
|
243
|
+
return { type: "moduleList", data: moduleManager.list(), success: true, id: msg.id };
|
|
244
|
+
}
|
|
245
|
+
case "ping": {
|
|
246
|
+
return {
|
|
247
|
+
type: "pong",
|
|
248
|
+
data: { pid: process.pid, uptime: process.uptime() },
|
|
249
|
+
success: true,
|
|
250
|
+
id: msg.id,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
case "kill": {
|
|
254
|
+
await pm.stopAll();
|
|
255
|
+
dashboard.stop();
|
|
256
|
+
clearInterval(metricsInterval);
|
|
257
|
+
setTimeout(() => process.exit(0), 200);
|
|
258
|
+
return { type: "kill", success: true, id: msg.id };
|
|
259
|
+
}
|
|
260
|
+
default:
|
|
261
|
+
return { type: "error", error: `Unknown command: ${(msg as any).type}`, success: false, id: msg.id };
|
|
219
262
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
263
|
+
|
|
264
|
+
} catch (err: Error | any) {
|
|
265
|
+
|
|
266
|
+
let error = err.message;
|
|
267
|
+
|
|
268
|
+
if (this.debugMode) {
|
|
269
|
+
error = `Message: ${err.message}\nStack: ${err.stack}`;
|
|
270
|
+
console.error(err, err.stack);
|
|
227
271
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
} catch (err: Error | any) {
|
|
232
|
-
let error = err.message;
|
|
233
|
-
if (debugMode) {
|
|
234
|
-
error = `Message: ${err.message}\nStack: ${err.stack}`
|
|
235
|
-
console.error(err, err.stack)
|
|
272
|
+
|
|
273
|
+
return { type: "error", error, success: false, id: msg.id };
|
|
274
|
+
|
|
236
275
|
}
|
|
237
|
-
return { type: "error", error, success: false, id: msg.id };
|
|
238
276
|
}
|
|
239
|
-
}
|
|
240
277
|
|
|
278
|
+
} // end class
|
|
241
279
|
|
|
242
|
-
server = Bun.serve(serverOptions);
|
|
243
280
|
|
|
244
|
-
|
|
281
|
+
// ── Entrypoint (spawned by CLI) ───────────────────────────────────────────
|
|
282
|
+
if (import.meta.main) {
|
|
283
|
+
const dm = new Daemon();
|
|
284
|
+
await dm.initialize(); // initialize first — writes PID, sets up pm/dashboard
|
|
285
|
+
const s = dm.startServer(); // then bind the socket
|
|
286
|
+
console.log(`Daemon listening on ${s.url}`);
|
|
287
|
+
}
|