@gachlab/devup 0.9.3 → 0.10.1
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/dist/control-plane/socket-server.d.ts +29 -0
- package/dist/control-plane/socket-server.d.ts.map +1 -1
- package/dist/index.js +105 -8
- package/dist/index.js.map +1 -1
- package/dist/orchestrator/daemon.d.ts.map +1 -1
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/hooks/useControlPlane.d.ts +6 -1
- package/dist/tui/hooks/useControlPlane.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -9,6 +9,29 @@ import type { ProcessState } from '../process/types.js';
|
|
|
9
9
|
* to the socket file already has the same uid as the devup process — no
|
|
10
10
|
* additional auth needed. Strictly local; TCP exposure is intentionally
|
|
11
11
|
* out of scope. */
|
|
12
|
+
export interface ServiceStatEntry {
|
|
13
|
+
cpu: number;
|
|
14
|
+
memMB: number;
|
|
15
|
+
}
|
|
16
|
+
export interface StatsResult {
|
|
17
|
+
services: Record<string, ServiceStatEntry>;
|
|
18
|
+
system: {
|
|
19
|
+
totalMemMB: number;
|
|
20
|
+
freeMemMB: number;
|
|
21
|
+
cpuCores: number;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export interface ProxyInfo {
|
|
25
|
+
active: boolean;
|
|
26
|
+
provider: string;
|
|
27
|
+
domain: string;
|
|
28
|
+
tls: boolean;
|
|
29
|
+
routes: Record<string, string>;
|
|
30
|
+
}
|
|
31
|
+
export interface ProjectInfo {
|
|
32
|
+
project: string;
|
|
33
|
+
profiles: Record<string, string[]>;
|
|
34
|
+
}
|
|
12
35
|
export interface RpcContext {
|
|
13
36
|
/** State of every service (read-only snapshot). */
|
|
14
37
|
states(): Map<string, ProcessState>;
|
|
@@ -23,6 +46,12 @@ export interface RpcContext {
|
|
|
23
46
|
watchLogs(svcName: string | null, onLine: (svc: string, line: string) => void): () => void;
|
|
24
47
|
/** Subscribe to service-state changes. Returns an unsubscribe function. */
|
|
25
48
|
watchStatus(onUpdate: (name: string, state: ProcessState) => void): () => void;
|
|
49
|
+
/** Per-service CPU/mem stats + system totals. */
|
|
50
|
+
getStats(): Promise<StatsResult>;
|
|
51
|
+
/** Active proxy configuration, or null when no proxy is running. */
|
|
52
|
+
getProxyInfo(): ProxyInfo | null;
|
|
53
|
+
/** Project metadata: name and profiles defined in config. */
|
|
54
|
+
getInfo(): ProjectInfo;
|
|
26
55
|
}
|
|
27
56
|
export interface SocketServerHandle {
|
|
28
57
|
server: Server;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket-server.d.ts","sourceRoot":"","sources":["../../src/control-plane/socket-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAe,MAAM,UAAU,CAAC;AAMlE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD;;;;;;;;oBAQoB;AAEpB,MAAM,WAAW,UAAU;IACzB,mDAAmD;IACnD,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACpC,iCAAiC;IACjC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,8BAA8B;IAC9B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,yFAAyF;IACzF,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5D;2CACuC;IACvC,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC3F,2EAA2E;IAC3E,WAAW,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"socket-server.d.ts","sourceRoot":"","sources":["../../src/control-plane/socket-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAe,MAAM,UAAU,CAAC;AAMlE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD;;;;;;;;oBAQoB;AAEpB,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;IAC3C,MAAM,EAAE;QACN,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,OAAO,CAAC;IACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,UAAU;IACzB,mDAAmD;IACnD,MAAM,IAAI,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACpC,iCAAiC;IACjC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,8BAA8B;IAC9B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,yFAAyF;IACzF,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5D;2CACuC;IACvC,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC3F,2EAA2E;IAC3E,WAAW,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;IAC/E,iDAAiD;IACjD,QAAQ,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;IACjC,oEAAoE;IACpE,YAAY,IAAI,SAAS,GAAG,IAAI,CAAC;IACjC,6DAA6D;IAC7D,OAAO,IAAI,WAAW,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAG7D;AAED,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,EACnB,GAAG,EAAE,UAAU,EACf,IAAI,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;CAAO,GAC1D,OAAO,CAAC,kBAAkB,CAAC,CA+C7B"}
|
package/dist/index.js
CHANGED
|
@@ -831,6 +831,7 @@ function serializeState(name, st) {
|
|
|
831
831
|
health: st.health,
|
|
832
832
|
port: st.svc.port,
|
|
833
833
|
type: st.svc.type,
|
|
834
|
+
phase: st.svc.phase,
|
|
834
835
|
errors: st.errors,
|
|
835
836
|
restarts: st.restarts,
|
|
836
837
|
pid: st.pid,
|
|
@@ -847,8 +848,12 @@ async function dispatch(method, params, ctx) {
|
|
|
847
848
|
for (const [name, st] of ctx.states()) {
|
|
848
849
|
out.push(serializeState(name, st));
|
|
849
850
|
}
|
|
850
|
-
return { services: out };
|
|
851
|
+
return { services: out, proxy: ctx.getProxyInfo() };
|
|
851
852
|
}
|
|
853
|
+
case "stats":
|
|
854
|
+
return await ctx.getStats();
|
|
855
|
+
case "info":
|
|
856
|
+
return ctx.getInfo();
|
|
852
857
|
case "restart": {
|
|
853
858
|
const svc = stringOrThrow(params["svc"] ?? params["service"], "svc");
|
|
854
859
|
await ctx.restart(svc);
|
|
@@ -951,7 +956,7 @@ function openStream(socketPath, method, params, onFrame, onError) {
|
|
|
951
956
|
import { spawn as spawn4 } from "child_process";
|
|
952
957
|
import { writeFileSync as writeFileSync2, readFileSync as readFileSync3, existsSync as existsSync10, unlinkSync as unlinkSync2, mkdirSync as mkdirSync3, createReadStream } from "fs";
|
|
953
958
|
import { join as join8 } from "path";
|
|
954
|
-
import { homedir as homedir3 } from "os";
|
|
959
|
+
import { homedir as homedir3, totalmem, freemem, cpus } from "os";
|
|
955
960
|
import { setTimeout as sleep } from "timers/promises";
|
|
956
961
|
import { createInterface as createInterface3 } from "readline";
|
|
957
962
|
|
|
@@ -1874,6 +1879,7 @@ async function daemonBody(opts) {
|
|
|
1874
1879
|
const logBus = new Broadcaster();
|
|
1875
1880
|
const stateBus = new Broadcaster();
|
|
1876
1881
|
const lazyProxies = /* @__PURE__ */ new Map();
|
|
1882
|
+
const prevCpuMap = /* @__PURE__ */ new Map();
|
|
1877
1883
|
let externals = [];
|
|
1878
1884
|
let socket = null;
|
|
1879
1885
|
let healthTimer = null;
|
|
@@ -1983,7 +1989,51 @@ async function daemonBody(opts) {
|
|
|
1983
1989
|
watchLogs: (svcName, onLine) => logBus.subscribe(({ svc, text }) => {
|
|
1984
1990
|
if (svcName === null || svc === svcName) onLine(svc, text);
|
|
1985
1991
|
}),
|
|
1986
|
-
watchStatus: (onUpdate) => stateBus.subscribe(({ name, state }) => onUpdate(name, state))
|
|
1992
|
+
watchStatus: (onUpdate) => stateBus.subscribe(({ name, state }) => onUpdate(name, state)),
|
|
1993
|
+
async getStats() {
|
|
1994
|
+
const pids = [];
|
|
1995
|
+
const pidToName = /* @__PURE__ */ new Map();
|
|
1996
|
+
for (const [name, st] of mgr.state) {
|
|
1997
|
+
if (st.pid) {
|
|
1998
|
+
pids.push(st.pid);
|
|
1999
|
+
pidToName.set(st.pid, name);
|
|
2000
|
+
}
|
|
2001
|
+
}
|
|
2002
|
+
const raw = pids.length ? await platform.getProcessStats(pids) : /* @__PURE__ */ new Map();
|
|
2003
|
+
const services2 = {};
|
|
2004
|
+
for (const [name] of mgr.state) {
|
|
2005
|
+
services2[name] = { cpu: 0, memMB: 0 };
|
|
2006
|
+
}
|
|
2007
|
+
for (const [pid, data] of raw) {
|
|
2008
|
+
const name = pidToName.get(pid);
|
|
2009
|
+
if (!name) continue;
|
|
2010
|
+
const prev = prevCpuMap.get(name) ?? { time: Date.now(), cpu: 0 };
|
|
2011
|
+
const cpu = calcCpuPercent(data.cpuSeconds, prev.cpu, prev.time);
|
|
2012
|
+
prevCpuMap.set(name, { time: Date.now(), cpu: data.cpuSeconds });
|
|
2013
|
+
services2[name] = { cpu: Math.round(cpu * 10) / 10, memMB: Math.round(data.rss / 1024 * 10) / 10 };
|
|
2014
|
+
}
|
|
2015
|
+
return {
|
|
2016
|
+
services: services2,
|
|
2017
|
+
system: {
|
|
2018
|
+
totalMemMB: Math.round(totalmem() / 1024 / 1024),
|
|
2019
|
+
freeMemMB: Math.round(freemem() / 1024 / 1024),
|
|
2020
|
+
cpuCores: cpus().length
|
|
2021
|
+
}
|
|
2022
|
+
};
|
|
2023
|
+
},
|
|
2024
|
+
getProxyInfo() {
|
|
2025
|
+
if (!proxyProvider || !proxyOpts || !cliArgs.proxy) return null;
|
|
2026
|
+
return {
|
|
2027
|
+
active: true,
|
|
2028
|
+
provider: proxyProvider.name,
|
|
2029
|
+
domain: proxyOpts.domain,
|
|
2030
|
+
tls: proxyOpts.tls,
|
|
2031
|
+
routes: proxyOpts.routes
|
|
2032
|
+
};
|
|
2033
|
+
},
|
|
2034
|
+
getInfo() {
|
|
2035
|
+
return { project: projectName, profiles: config.profiles ?? {} };
|
|
2036
|
+
}
|
|
1987
2037
|
}, { onLog: (msg) => writeDevupLog(msg) });
|
|
1988
2038
|
healthTimer = setInterval(() => {
|
|
1989
2039
|
void mgr.checkAllHealth();
|
|
@@ -3102,8 +3152,10 @@ function useLogsPause(setPaused, logsPaused, logsScrollOffset) {
|
|
|
3102
3152
|
import { useEffect as useEffect5, useRef as useRef3 } from "react";
|
|
3103
3153
|
import { createInterface as createInterface5 } from "readline";
|
|
3104
3154
|
import { createReadStream as createReadStream3, existsSync as existsSync15 } from "fs";
|
|
3105
|
-
|
|
3155
|
+
import { totalmem as totalmem2, freemem as freemem2, cpus as cpus2 } from "os";
|
|
3156
|
+
function useControlPlane(manager, projectName, logSink, pushLog, logBus, stateBus, platform, proxy, profiles) {
|
|
3106
3157
|
const handleRef = useRef3(null);
|
|
3158
|
+
const prevCpuMap = useRef3(/* @__PURE__ */ new Map());
|
|
3107
3159
|
useEffect5(() => {
|
|
3108
3160
|
if (!manager) return;
|
|
3109
3161
|
let handle = null;
|
|
@@ -3135,6 +3187,50 @@ function useControlPlane(manager, projectName, logSink, pushLog, logBus, stateBu
|
|
|
3135
3187
|
},
|
|
3136
3188
|
watchStatus: (onUpdate) => {
|
|
3137
3189
|
return stateBus.subscribe(({ name, state }) => onUpdate(name, state));
|
|
3190
|
+
},
|
|
3191
|
+
async getStats() {
|
|
3192
|
+
const pids = [];
|
|
3193
|
+
const pidToName = /* @__PURE__ */ new Map();
|
|
3194
|
+
for (const [name, st] of manager.state) {
|
|
3195
|
+
if (st.pid) {
|
|
3196
|
+
pids.push(st.pid);
|
|
3197
|
+
pidToName.set(st.pid, name);
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
const raw = pids.length ? await platform.getProcessStats(pids) : /* @__PURE__ */ new Map();
|
|
3201
|
+
const services = {};
|
|
3202
|
+
for (const [name] of manager.state) {
|
|
3203
|
+
services[name] = { cpu: 0, memMB: 0 };
|
|
3204
|
+
}
|
|
3205
|
+
for (const [pid, data] of raw) {
|
|
3206
|
+
const name = pidToName.get(pid);
|
|
3207
|
+
if (!name) continue;
|
|
3208
|
+
const prev = prevCpuMap.current.get(name) ?? { time: Date.now(), cpu: 0 };
|
|
3209
|
+
const cpu = calcCpuPercent(data.cpuSeconds, prev.cpu, prev.time);
|
|
3210
|
+
prevCpuMap.current.set(name, { time: Date.now(), cpu: data.cpuSeconds });
|
|
3211
|
+
services[name] = { cpu: Math.round(cpu * 10) / 10, memMB: Math.round(data.rss / 1024 * 10) / 10 };
|
|
3212
|
+
}
|
|
3213
|
+
return {
|
|
3214
|
+
services,
|
|
3215
|
+
system: {
|
|
3216
|
+
totalMemMB: Math.round(totalmem2() / 1024 / 1024),
|
|
3217
|
+
freeMemMB: Math.round(freemem2() / 1024 / 1024),
|
|
3218
|
+
cpuCores: cpus2().length
|
|
3219
|
+
}
|
|
3220
|
+
};
|
|
3221
|
+
},
|
|
3222
|
+
getProxyInfo() {
|
|
3223
|
+
if (!proxy) return null;
|
|
3224
|
+
return {
|
|
3225
|
+
active: true,
|
|
3226
|
+
provider: proxy.provider.name,
|
|
3227
|
+
domain: proxy.opts.domain,
|
|
3228
|
+
tls: proxy.opts.tls,
|
|
3229
|
+
routes: proxy.opts.routes
|
|
3230
|
+
};
|
|
3231
|
+
},
|
|
3232
|
+
getInfo() {
|
|
3233
|
+
return { project: projectName, profiles };
|
|
3138
3234
|
}
|
|
3139
3235
|
}, { onLog: (msg) => pushLog("devup", msg, 12) });
|
|
3140
3236
|
handleRef.current = handle;
|
|
@@ -3146,7 +3242,7 @@ function useControlPlane(manager, projectName, logSink, pushLog, logBus, stateBu
|
|
|
3146
3242
|
void handle?.close();
|
|
3147
3243
|
handleRef.current = null;
|
|
3148
3244
|
};
|
|
3149
|
-
}, [manager, projectName, logSink, pushLog, logBus, stateBus]);
|
|
3245
|
+
}, [manager, projectName, logSink, pushLog, logBus, stateBus, platform, proxy, profiles]);
|
|
3150
3246
|
return handleRef;
|
|
3151
3247
|
}
|
|
3152
3248
|
|
|
@@ -3289,7 +3385,7 @@ function StatsPanel({ states, stats, sortMode, maxNameLen, height, focused, scro
|
|
|
3289
3385
|
const statsObj = Object.fromEntries([...stats].map(([k, v]) => [k, v]));
|
|
3290
3386
|
const apis = sortServiceNames(names.filter((n) => states.get(n).svc.type === "api"), sortMode, statsObj, stObj);
|
|
3291
3387
|
const webs = sortServiceNames(names.filter((n) => states.get(n).svc.type === "web"), sortMode, statsObj, stObj);
|
|
3292
|
-
const
|
|
3388
|
+
const cpus3 = os.cpus().length;
|
|
3293
3389
|
const totalGB = (os.totalmem() / 1024 / 1024 / 1024).toFixed(1);
|
|
3294
3390
|
const usedGB = (parseFloat(totalGB) - os.freemem() / 1024 / 1024 / 1024).toFixed(1);
|
|
3295
3391
|
const load = os.loadavg()[0].toFixed(2);
|
|
@@ -3342,7 +3438,7 @@ function StatsPanel({ states, stats, sortMode, maxNameLen, height, focused, scro
|
|
|
3342
3438
|
] }),
|
|
3343
3439
|
/* @__PURE__ */ jsxs(Text, { dimColor: true, children: [
|
|
3344
3440
|
" System: ",
|
|
3345
|
-
|
|
3441
|
+
cpus3,
|
|
3346
3442
|
"c Load ",
|
|
3347
3443
|
load,
|
|
3348
3444
|
" RAM ",
|
|
@@ -3754,7 +3850,8 @@ function App({ config, services, cliArgs, platform, env, baseCwd, proxyProvider,
|
|
|
3754
3850
|
onToggleProxy: () => {
|
|
3755
3851
|
}
|
|
3756
3852
|
});
|
|
3757
|
-
const
|
|
3853
|
+
const proxyCtx = proxyProvider && proxyOpts ? { provider: proxyProvider, opts: proxyOpts } : null;
|
|
3854
|
+
const socketServer = useControlPlane(pm.manager, config.name, logSink, pm.pushLog, pm.logBus, pm.stateBus, platform, proxyCtx, config.profiles ?? {});
|
|
3758
3855
|
const shutdown = useCallback3(async () => {
|
|
3759
3856
|
lazyProxies.current.forEach((p) => p.destroy());
|
|
3760
3857
|
await socketServer.current?.close();
|