@profullstack/hqtui-demo 0.1.1 → 0.1.3
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 +51 -8
- package/dist/main.js +34 -11
- package/dist/main.js.map +1 -1
- package/dist/screens/index.js +4 -0
- package/dist/screens/index.js.map +1 -1
- package/dist/screens/network.js +86 -0
- package/dist/screens/network.js.map +1 -0
- package/dist/screens/services.js +121 -0
- package/dist/screens/services.js.map +1 -0
- package/dist/screens/sessions.js +80 -0
- package/dist/screens/sessions.js.map +1 -0
- package/dist/screens/traffic.js +183 -0
- package/dist/screens/traffic.js.map +1 -0
- package/dist/simulation.js +6 -0
- package/dist/simulation.js.map +1 -1
- package/dist/state.js +4 -1
- package/dist/state.js.map +1 -1
- package/dist/system/common.js +2 -0
- package/dist/system/common.js.map +1 -1
- package/dist/system/linux-telemetry.js +420 -0
- package/dist/system/linux-telemetry.js.map +1 -0
- package/dist/system/linux-traffic.js +282 -0
- package/dist/system/linux-traffic.js.map +1 -0
- package/dist/system/linux.js +90 -26
- package/dist/system/linux.js.map +1 -1
- package/dist/system/simulated-telemetry.js +267 -0
- package/dist/system/simulated-telemetry.js.map +1 -0
- package/dist/system/telemetry.js +44 -0
- package/dist/system/telemetry.js.map +1 -0
- package/package.json +4 -3
- package/src/main.ts +26 -11
- package/src/screens/index.ts +4 -0
- package/src/screens/network.ts +91 -0
- package/src/screens/services.ts +136 -0
- package/src/screens/sessions.ts +85 -0
- package/src/screens/traffic.ts +199 -0
- package/src/simulation.ts +12 -0
- package/src/state.ts +7 -2
- package/src/system/common.ts +2 -0
- package/src/system/linux-telemetry.ts +431 -0
- package/src/system/linux-traffic.ts +375 -0
- package/src/system/linux.ts +95 -21
- package/src/system/simulated-telemetry.ts +280 -0
- package/src/system/telemetry.ts +263 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import type { Telemetry } from "./telemetry.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Simulated telemetry so `--sim` populates every screen, and so CI snapshots
|
|
5
|
+
* of the traffic and session views are deterministic.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const USERS = ["anthony", "deploy", "ci", "root", "postgres"];
|
|
9
|
+
const HOSTS = ["10.0.4.17", "203.0.113.42", "198.51.100.9", "192.168.1.42", "172.16.0.8"];
|
|
10
|
+
const PATHS = ["/", "/api/health", "/api/users", "/assets/app.js", "/docs", "/api/ws", "/login", "/api/metrics"];
|
|
11
|
+
const AGENTS = ["nginx", "sshd", "systemd", "cron", "kernel", "dockerd"];
|
|
12
|
+
const UNITS = [
|
|
13
|
+
["nginx", "active", "running", "A high performance web server"],
|
|
14
|
+
["postgresql", "active", "running", "PostgreSQL RDBMS"],
|
|
15
|
+
["redis-server", "active", "running", "Advanced key-value store"],
|
|
16
|
+
["ssh", "active", "running", "OpenBSD Secure Shell server"],
|
|
17
|
+
["cron", "active", "running", "Regular background program processing"],
|
|
18
|
+
["docker", "active", "running", "Docker Application Container Engine"],
|
|
19
|
+
["systemd-resolved", "active", "running", "Network Name Resolution"],
|
|
20
|
+
["backup", "failed", "failed", "Nightly backup job"],
|
|
21
|
+
["telemetry-agent", "inactive", "dead", "Vendor telemetry collector"],
|
|
22
|
+
];
|
|
23
|
+
const PROTOCOLS = ["HTTPS", "HTTP", "SSH", "Postgres", "Redis", "DNS", "SMTP", "ephemeral"];
|
|
24
|
+
|
|
25
|
+
function push(history: number[], value: number, limit = 240): void {
|
|
26
|
+
history.push(value);
|
|
27
|
+
if (history.length > limit) history.shift();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function clock(offsetSeconds = 0): string {
|
|
31
|
+
return new Date(Date.now() - offsetSeconds * 1000).toTimeString().slice(0, 8);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Fill in every telemetry section with plausible, drifting values. */
|
|
35
|
+
export function updateTelemetry(t: Telemetry, random: () => number, tick: number, cpu: number): void {
|
|
36
|
+
// --- sessions -------------------------------------------------------------
|
|
37
|
+
if (t.sessions.length === 0) {
|
|
38
|
+
t.sessions = [
|
|
39
|
+
{ user: "anthony", tty: "pts/0", from: "10.0.4.17", loginAt: "09:14", idle: ".", what: "bun run dev" },
|
|
40
|
+
{ user: "deploy", tty: "pts/1", from: "203.0.113.42", loginAt: "11:02", idle: "00:04", what: "tail -f app.log" },
|
|
41
|
+
{ user: "anthony", tty: "pts/2", from: "10.0.4.17", loginAt: "11:40", idle: "00:12", what: "psql" },
|
|
42
|
+
];
|
|
43
|
+
t.logins = Array.from({ length: 18 }, (_, i) => ({
|
|
44
|
+
user: USERS[i % USERS.length],
|
|
45
|
+
tty: `pts/${i % 4}`,
|
|
46
|
+
from: HOSTS[i % HOSTS.length],
|
|
47
|
+
when: `Aug ${20 + (i % 9)} ${String(7 + (i % 12)).padStart(2, "0")}:${String((i * 7) % 60).padStart(2, "0")}`,
|
|
48
|
+
status: i < 3 ? "still" : "ok",
|
|
49
|
+
}));
|
|
50
|
+
t.failedLogins = Array.from({ length: 6 }, (_, i) => ({
|
|
51
|
+
user: ["root", "admin", "test", "oracle", "ubuntu", "git"][i],
|
|
52
|
+
tty: "ssh",
|
|
53
|
+
from: `185.${100 + i}.${20 + i * 3}.${9 + i}`,
|
|
54
|
+
when: `Aug 30 0${i}:${String((i * 11) % 60).padStart(2, "0")}`,
|
|
55
|
+
status: "failed",
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
push(t.sessionHistory, t.sessions.length + (random() < 0.1 ? 1 : 0));
|
|
59
|
+
|
|
60
|
+
// --- sockets and protocols ------------------------------------------------
|
|
61
|
+
const connectionCount = Math.round(40 + cpu * 60 + random() * 15);
|
|
62
|
+
t.protocols = PROTOCOLS.map((protocol, i) => {
|
|
63
|
+
const weight = [0.42, 0.12, 0.06, 0.1, 0.07, 0.08, 0.03, 0.12][i];
|
|
64
|
+
const total = Math.max(0, Math.round(connectionCount * weight * (0.7 + random() * 0.6)));
|
|
65
|
+
const inbound = protocol === "HTTPS" || protocol === "HTTP" || protocol === "SSH"
|
|
66
|
+
? Math.round(total * 0.75)
|
|
67
|
+
: Math.round(total * 0.15);
|
|
68
|
+
return { protocol, inbound, outbound: total - inbound, total };
|
|
69
|
+
}).sort((a, b) => b.total - a.total);
|
|
70
|
+
|
|
71
|
+
t.inboundConnections = t.protocols.reduce((a, p) => a + p.inbound, 0);
|
|
72
|
+
t.outboundConnections = t.protocols.reduce((a, p) => a + p.outbound, 0);
|
|
73
|
+
push(t.connectionHistory, t.inboundConnections + t.outboundConnections);
|
|
74
|
+
|
|
75
|
+
t.remotes = HOSTS.map((host, i) => ({
|
|
76
|
+
host,
|
|
77
|
+
connections: Math.max(1, Math.round((8 - i) * (0.6 + random()))),
|
|
78
|
+
protocols: [PROTOCOLS[i % PROTOCOLS.length], PROTOCOLS[(i + 3) % PROTOCOLS.length]].join(", "),
|
|
79
|
+
})).sort((a, b) => b.connections - a.connections);
|
|
80
|
+
|
|
81
|
+
t.connections = t.remotes.slice(0, 12).map((remote, i) => ({
|
|
82
|
+
proto: i % 5 === 0 ? "udp" : "tcp",
|
|
83
|
+
local: `10.0.0.7:${40000 + i * 13}`,
|
|
84
|
+
remote: `${remote.host}:${[443, 443, 22, 5432, 6379][i % 5]}`,
|
|
85
|
+
state: "ESTAB",
|
|
86
|
+
process: ["bun/1261", "node/8437", "sshd/1024", "postgres/2217", "redis/1555"][i % 5],
|
|
87
|
+
}));
|
|
88
|
+
|
|
89
|
+
t.listeners = [
|
|
90
|
+
{ proto: "tcp", address: "0.0.0.0", port: "443", process: "nginx/981" },
|
|
91
|
+
{ proto: "tcp", address: "0.0.0.0", port: "80", process: "nginx/981" },
|
|
92
|
+
{ proto: "tcp", address: "0.0.0.0", port: "22", process: "sshd/1024" },
|
|
93
|
+
{ proto: "tcp", address: "127.0.0.1", port: "5432", process: "postgres/2217" },
|
|
94
|
+
{ proto: "tcp", address: "127.0.0.1", port: "6379", process: "redis/1555" },
|
|
95
|
+
{ proto: "tcp", address: "0.0.0.0", port: "3000", process: "bun/1261" },
|
|
96
|
+
{ proto: "udp", address: "0.0.0.0", port: "53", process: "resolved/712" },
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
// --- kernel network counters ---------------------------------------------
|
|
100
|
+
const inSegs = 4000 + cpu * 12000 + random() * 2500;
|
|
101
|
+
const outSegs = 3500 + cpu * 11000 + random() * 2200;
|
|
102
|
+
const retrans = Math.max(0, outSegs * (0.001 + random() * 0.004));
|
|
103
|
+
t.net.tcpInSegs += inSegs;
|
|
104
|
+
t.net.tcpOutSegs += outSegs;
|
|
105
|
+
t.net.tcpRetransSegs += retrans;
|
|
106
|
+
t.net.tcpEstablished = t.inboundConnections + t.outboundConnections;
|
|
107
|
+
t.net.tcpPassiveOpens += Math.round(random() * 12);
|
|
108
|
+
t.net.tcpActiveOpens += Math.round(random() * 8);
|
|
109
|
+
t.net.tcpOutRsts += Math.round(random() * 3);
|
|
110
|
+
t.net.udpInDatagrams += Math.round(200 + random() * 400);
|
|
111
|
+
t.net.udpOutDatagrams += Math.round(180 + random() * 350);
|
|
112
|
+
t.net.icmpInMsgs += Math.round(random() * 2);
|
|
113
|
+
t.net.icmpOutMsgs += Math.round(random() * 2);
|
|
114
|
+
t.net.rates = {
|
|
115
|
+
inSegs,
|
|
116
|
+
outSegs,
|
|
117
|
+
retrans,
|
|
118
|
+
passiveOpens: random() * 12,
|
|
119
|
+
activeOpens: random() * 8,
|
|
120
|
+
udpIn: 200 + random() * 400,
|
|
121
|
+
udpOut: 180 + random() * 350,
|
|
122
|
+
};
|
|
123
|
+
t.net.retransRatio = retrans / Math.max(1, outSegs);
|
|
124
|
+
push(t.netInHistory, inSegs);
|
|
125
|
+
push(t.netOutHistory, outSegs);
|
|
126
|
+
push(t.retransHistory, retrans);
|
|
127
|
+
|
|
128
|
+
// --- ssh ------------------------------------------------------------------
|
|
129
|
+
if (t.ssh.length === 0 || (tick % 40 === 0 && random() < 0.7)) {
|
|
130
|
+
const failed = random() < 0.45;
|
|
131
|
+
t.ssh.push({
|
|
132
|
+
time: clock(),
|
|
133
|
+
action: failed ? (random() < 0.5 ? "failed" : "invalid") : "accepted",
|
|
134
|
+
user: failed ? ["root", "admin", "test", "oracle"][Math.floor(random() * 4)] : USERS[Math.floor(random() * 3)],
|
|
135
|
+
from: failed ? `185.${Math.floor(random() * 255)}.${Math.floor(random() * 255)}.${Math.floor(random() * 255)}` : HOSTS[Math.floor(random() * 3)],
|
|
136
|
+
method: failed ? "password" : "publickey",
|
|
137
|
+
});
|
|
138
|
+
if (t.ssh.length > 40) t.ssh.shift();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// --- http -----------------------------------------------------------------
|
|
142
|
+
const requestsPerSecond = 18 + cpu * 90 + random() * 25;
|
|
143
|
+
if (!t.http) {
|
|
144
|
+
t.http = {
|
|
145
|
+
source: "/var/log/nginx/access.log",
|
|
146
|
+
requestsPerSecond,
|
|
147
|
+
total: 0,
|
|
148
|
+
statusClasses: [],
|
|
149
|
+
topPaths: [],
|
|
150
|
+
topClients: [],
|
|
151
|
+
methods: [],
|
|
152
|
+
upgrades: 0,
|
|
153
|
+
recent: [],
|
|
154
|
+
history: [],
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
const http = t.http;
|
|
158
|
+
http.requestsPerSecond = requestsPerSecond;
|
|
159
|
+
http.total += Math.round(requestsPerSecond);
|
|
160
|
+
push(http.history, requestsPerSecond);
|
|
161
|
+
http.upgrades += random() < 0.15 ? 1 : 0;
|
|
162
|
+
http.statusClasses = [
|
|
163
|
+
{ class: "2xx", count: Math.round(http.total * 0.86) },
|
|
164
|
+
{ class: "3xx", count: Math.round(http.total * 0.06) },
|
|
165
|
+
{ class: "4xx", count: Math.round(http.total * 0.06) },
|
|
166
|
+
{ class: "5xx", count: Math.round(http.total * 0.015) },
|
|
167
|
+
{ class: "1xx", count: http.upgrades },
|
|
168
|
+
];
|
|
169
|
+
http.topPaths = PATHS.map((path, i) => ({
|
|
170
|
+
path,
|
|
171
|
+
count: Math.round(http.total * [0.3, 0.18, 0.14, 0.12, 0.09, 0.07, 0.06, 0.04][i]),
|
|
172
|
+
}));
|
|
173
|
+
http.topClients = HOSTS.map((client, i) => ({ client, count: Math.round(http.total * (0.3 - i * 0.05)) }));
|
|
174
|
+
http.methods = [
|
|
175
|
+
{ method: "GET", count: Math.round(http.total * 0.78) },
|
|
176
|
+
{ method: "POST", count: Math.round(http.total * 0.16) },
|
|
177
|
+
{ method: "PUT", count: Math.round(http.total * 0.04) },
|
|
178
|
+
{ method: "DELETE", count: Math.round(http.total * 0.02) },
|
|
179
|
+
];
|
|
180
|
+
if (tick % 3 === 0) {
|
|
181
|
+
const roll = random();
|
|
182
|
+
http.recent.unshift({
|
|
183
|
+
time: clock(),
|
|
184
|
+
method: roll < 0.8 ? "GET" : roll < 0.95 ? "POST" : "DELETE",
|
|
185
|
+
path: PATHS[Math.floor(random() * PATHS.length)],
|
|
186
|
+
status: roll < 0.86 ? "200" : roll < 0.92 ? "304" : roll < 0.98 ? "404" : "500",
|
|
187
|
+
client: HOSTS[Math.floor(random() * HOSTS.length)],
|
|
188
|
+
bytes: Math.round(200 + random() * 24000),
|
|
189
|
+
});
|
|
190
|
+
if (http.recent.length > 40) http.recent.pop();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// --- services, containers, filesystems ------------------------------------
|
|
194
|
+
if (t.services.length === 0) {
|
|
195
|
+
t.services = UNITS.map(([name, active, sub, description]) => ({ name, active, sub, description }));
|
|
196
|
+
t.containers = [
|
|
197
|
+
{ id: "9f2c1a4b7e33", name: "api", image: "hqtui/api:1.4.2", status: "Up 2 hours", cpu: "12.4%", memory: "184 MiB" },
|
|
198
|
+
{ id: "3ab7f0912cd5", name: "worker", image: "hqtui/worker:1.4.2", status: "Up 2 hours", cpu: "6.1%", memory: "96 MiB" },
|
|
199
|
+
{ id: "77e10cc4a9b2", name: "postgres", image: "postgres:16", status: "Up 5 days", cpu: "3.8%", memory: "412 MiB" },
|
|
200
|
+
{ id: "1cd8e5b60f47", name: "redis", image: "redis:7-alpine", status: "Up 5 days", cpu: "0.7%", memory: "34 MiB" },
|
|
201
|
+
];
|
|
202
|
+
t.filesystems = [
|
|
203
|
+
{ mount: "/", device: "/dev/nvme0n1p2", type: "ext4", size: 512 * 1024 ** 3, used: 136 * 1024 ** 3, inodesUsed: 1_240_000, inodesTotal: 32_000_000 },
|
|
204
|
+
{ mount: "/home", device: "/dev/nvme0n1p3", type: "ext4", size: 980 * 1024 ** 3, used: 622 * 1024 ** 3, inodesUsed: 4_100_000, inodesTotal: 61_000_000 },
|
|
205
|
+
{ mount: "/data", device: "/dev/sda1", type: "xfs", size: 2 * 1024 ** 4, used: 1.02 * 1024 ** 4, inodesUsed: 812_000, inodesTotal: 210_000_000 },
|
|
206
|
+
{ mount: "/boot", device: "/dev/nvme0n1p1", type: "vfat", size: 512 * 1024 ** 2, used: 148 * 1024 ** 2, inodesUsed: 0, inodesTotal: 0 },
|
|
207
|
+
];
|
|
208
|
+
t.gpus = [{
|
|
209
|
+
name: "NVIDIA RTX A2000", utilization: 0.34, memoryUsed: 2.1 * 1024 ** 3,
|
|
210
|
+
memoryTotal: 6 * 1024 ** 3, temperature: 52, power: 34.5,
|
|
211
|
+
}];
|
|
212
|
+
t.power = { battery: 96, charging: false, timeRemaining: "Full", powerDraw: 14.2, acConnected: true };
|
|
213
|
+
}
|
|
214
|
+
t.gpus[0].utilization = Math.max(0.02, Math.min(1, cpu * 0.8 + random() * 0.2));
|
|
215
|
+
t.gpus[0].temperature = 44 + t.gpus[0].utilization * 26;
|
|
216
|
+
if (t.power) t.power.powerDraw = 8 + cpu * 22;
|
|
217
|
+
|
|
218
|
+
// --- kernel counters ------------------------------------------------------
|
|
219
|
+
t.kernel.contextSwitchRate = 3000 + cpu * 18000 + random() * 1500;
|
|
220
|
+
t.kernel.interruptRate = 2000 + cpu * 9000 + random() * 900;
|
|
221
|
+
t.kernel.forkRate = 4 + cpu * 60 + random() * 8;
|
|
222
|
+
t.kernel.contextSwitches += t.kernel.contextSwitchRate / 10;
|
|
223
|
+
t.kernel.interrupts += t.kernel.interruptRate / 10;
|
|
224
|
+
t.kernel.forks += t.kernel.forkRate / 10;
|
|
225
|
+
t.kernel.procsRunning = Math.max(1, Math.round(cpu * 14));
|
|
226
|
+
t.kernel.procsBlocked = random() < 0.15 ? 1 : 0;
|
|
227
|
+
t.kernel.entropy = Math.round(3000 + random() * 800);
|
|
228
|
+
t.kernel.openFiles = Math.round(2000 + cpu * 3000);
|
|
229
|
+
t.kernel.maxFiles = 9_223_372;
|
|
230
|
+
t.kernel.pageIn += Math.round(random() * 400);
|
|
231
|
+
t.kernel.pageOut += Math.round(random() * 300);
|
|
232
|
+
|
|
233
|
+
t.states = {
|
|
234
|
+
running: t.kernel.procsRunning,
|
|
235
|
+
sleeping: 240 + Math.round(random() * 20),
|
|
236
|
+
stopped: 0,
|
|
237
|
+
zombie: random() < 0.08 ? 1 : 0,
|
|
238
|
+
total: 0,
|
|
239
|
+
};
|
|
240
|
+
t.states.total = t.states.running + t.states.sleeping + t.states.stopped + t.states.zombie;
|
|
241
|
+
|
|
242
|
+
// --- interfaces -----------------------------------------------------------
|
|
243
|
+
if (t.interfaces.length === 0) {
|
|
244
|
+
t.interfaces = [
|
|
245
|
+
{ name: "eth0", ip: "10.0.0.7", mac: "ac:de:48:00:11:22", state: "up", mtu: 1500, rxRate: 0, txRate: 0, rxTotal: 0, txTotal: 0, rxHistory: [], txHistory: [], errors: 0, drops: 0 },
|
|
246
|
+
{ name: "wg0", ip: "100.84.2.19", mac: "00:00:00:00:00:00", state: "up", mtu: 1280, rxRate: 0, txRate: 0, rxTotal: 0, txTotal: 0, rxHistory: [], txHistory: [], errors: 0, drops: 0 },
|
|
247
|
+
];
|
|
248
|
+
}
|
|
249
|
+
t.interfaces.forEach((iface, i) => {
|
|
250
|
+
const scale = i === 0 ? 1 : 0.25;
|
|
251
|
+
iface.rxRate = Math.max(0, (6e6 + Math.sin(tick / 30 + i) * 3e6 + random() * 4e6) * scale);
|
|
252
|
+
iface.txRate = Math.max(0, (1.8e6 + Math.sin(tick / 20 + i) * 1e6 + random() * 1.5e6) * scale);
|
|
253
|
+
iface.rxTotal += iface.rxRate / 10;
|
|
254
|
+
iface.txTotal += iface.txRate / 10;
|
|
255
|
+
push(iface.rxHistory, iface.rxRate);
|
|
256
|
+
push(iface.txHistory, iface.txRate);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// --- journal --------------------------------------------------------------
|
|
260
|
+
if (tick % 8 === 0) {
|
|
261
|
+
const level = random() < 0.06 ? "ERROR" : random() < 0.16 ? "WARN" : random() < 0.3 ? "DEBUG" : "INFO";
|
|
262
|
+
const unit = AGENTS[Math.floor(random() * AGENTS.length)];
|
|
263
|
+
const messages: Record<string, string[]> = {
|
|
264
|
+
nginx: ["client closed connection while waiting for request", `${Math.round(random() * 400)} requests served`, "upstream timed out"],
|
|
265
|
+
sshd: ["Accepted publickey for anthony", "Connection closed by authenticating user root", "Received disconnect"],
|
|
266
|
+
systemd: ["Started Daily apt download activities", "Reloading nginx configuration", "Reached target Timers"],
|
|
267
|
+
cron: ["(anthony) CMD (run-parts /etc/cron.hourly)", "pam_unix(cron:session): session opened"],
|
|
268
|
+
kernel: ["TCP: request_sock_TCP: Possible SYN flooding", "EXT4-fs: mounted filesystem with ordered data mode"],
|
|
269
|
+
dockerd: ["container start", "health check passed", "image pull complete"],
|
|
270
|
+
};
|
|
271
|
+
const pool = messages[unit] ?? ["event"];
|
|
272
|
+
t.journal.push({
|
|
273
|
+
time: clock(),
|
|
274
|
+
level,
|
|
275
|
+
unit,
|
|
276
|
+
message: pool[Math.floor(random() * pool.length)],
|
|
277
|
+
});
|
|
278
|
+
if (t.journal.length > 200) t.journal.shift();
|
|
279
|
+
}
|
|
280
|
+
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
/** Everything the demo can observe beyond classic CPU/memory/disk monitoring. */
|
|
2
|
+
|
|
3
|
+
export interface Session {
|
|
4
|
+
user: string;
|
|
5
|
+
tty: string;
|
|
6
|
+
from: string;
|
|
7
|
+
loginAt: string;
|
|
8
|
+
idle: string;
|
|
9
|
+
what: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface LoginEvent {
|
|
13
|
+
user: string;
|
|
14
|
+
tty: string;
|
|
15
|
+
from: string;
|
|
16
|
+
when: string;
|
|
17
|
+
status: "ok" | "failed" | "still";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface Connection {
|
|
21
|
+
proto: string;
|
|
22
|
+
local: string;
|
|
23
|
+
remote: string;
|
|
24
|
+
state: string;
|
|
25
|
+
process: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface Listener {
|
|
29
|
+
proto: string;
|
|
30
|
+
address: string;
|
|
31
|
+
port: string;
|
|
32
|
+
process: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface ServiceUnit {
|
|
36
|
+
name: string;
|
|
37
|
+
active: string;
|
|
38
|
+
sub: string;
|
|
39
|
+
description: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface Container {
|
|
43
|
+
id: string;
|
|
44
|
+
name: string;
|
|
45
|
+
image: string;
|
|
46
|
+
status: string;
|
|
47
|
+
cpu: string;
|
|
48
|
+
memory: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface Interface {
|
|
52
|
+
name: string;
|
|
53
|
+
ip: string;
|
|
54
|
+
mac: string;
|
|
55
|
+
state: string;
|
|
56
|
+
mtu: number;
|
|
57
|
+
rxRate: number;
|
|
58
|
+
txRate: number;
|
|
59
|
+
rxTotal: number;
|
|
60
|
+
txTotal: number;
|
|
61
|
+
rxHistory: number[];
|
|
62
|
+
txHistory: number[];
|
|
63
|
+
errors: number;
|
|
64
|
+
drops: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface Filesystem {
|
|
68
|
+
mount: string;
|
|
69
|
+
device: string;
|
|
70
|
+
type: string;
|
|
71
|
+
size: number;
|
|
72
|
+
used: number;
|
|
73
|
+
inodesUsed: number;
|
|
74
|
+
inodesTotal: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface KernelStats {
|
|
78
|
+
contextSwitches: number;
|
|
79
|
+
contextSwitchRate: number;
|
|
80
|
+
interrupts: number;
|
|
81
|
+
interruptRate: number;
|
|
82
|
+
forks: number;
|
|
83
|
+
forkRate: number;
|
|
84
|
+
procsRunning: number;
|
|
85
|
+
procsBlocked: number;
|
|
86
|
+
entropy: number;
|
|
87
|
+
openFiles: number;
|
|
88
|
+
maxFiles: number;
|
|
89
|
+
bootTime: number;
|
|
90
|
+
pageIn: number;
|
|
91
|
+
pageOut: number;
|
|
92
|
+
swapIn: number;
|
|
93
|
+
swapOut: number;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface GpuStats {
|
|
97
|
+
name: string;
|
|
98
|
+
utilization: number;
|
|
99
|
+
memoryUsed: number;
|
|
100
|
+
memoryTotal: number;
|
|
101
|
+
temperature: number;
|
|
102
|
+
power: number;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface PowerStats {
|
|
106
|
+
battery: number;
|
|
107
|
+
charging: boolean;
|
|
108
|
+
timeRemaining: string;
|
|
109
|
+
powerDraw: number;
|
|
110
|
+
acConnected: boolean;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export interface JournalEntry {
|
|
114
|
+
time: string;
|
|
115
|
+
level: string;
|
|
116
|
+
unit: string;
|
|
117
|
+
message: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export interface ProcessStates {
|
|
121
|
+
running: number;
|
|
122
|
+
sleeping: number;
|
|
123
|
+
stopped: number;
|
|
124
|
+
zombie: number;
|
|
125
|
+
total: number;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface ProtocolBucket {
|
|
129
|
+
protocol: string;
|
|
130
|
+
inbound: number;
|
|
131
|
+
outbound: number;
|
|
132
|
+
total: number;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface RemoteHost {
|
|
136
|
+
host: string;
|
|
137
|
+
connections: number;
|
|
138
|
+
protocols: string;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface SshEvent {
|
|
142
|
+
time: string;
|
|
143
|
+
action: "accepted" | "failed" | "invalid" | "disconnect";
|
|
144
|
+
user: string;
|
|
145
|
+
from: string;
|
|
146
|
+
method: string;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface NetCounters {
|
|
150
|
+
tcpActiveOpens: number;
|
|
151
|
+
tcpPassiveOpens: number;
|
|
152
|
+
tcpEstablished: number;
|
|
153
|
+
tcpInSegs: number;
|
|
154
|
+
tcpOutSegs: number;
|
|
155
|
+
tcpRetransSegs: number;
|
|
156
|
+
tcpInErrs: number;
|
|
157
|
+
tcpOutRsts: number;
|
|
158
|
+
udpInDatagrams: number;
|
|
159
|
+
udpOutDatagrams: number;
|
|
160
|
+
udpInErrors: number;
|
|
161
|
+
icmpInMsgs: number;
|
|
162
|
+
icmpOutMsgs: number;
|
|
163
|
+
rates: {
|
|
164
|
+
inSegs: number;
|
|
165
|
+
outSegs: number;
|
|
166
|
+
retrans: number;
|
|
167
|
+
passiveOpens: number;
|
|
168
|
+
activeOpens: number;
|
|
169
|
+
udpIn: number;
|
|
170
|
+
udpOut: number;
|
|
171
|
+
};
|
|
172
|
+
retransRatio: number;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export interface HttpStats {
|
|
176
|
+
source: string;
|
|
177
|
+
requestsPerSecond: number;
|
|
178
|
+
total: number;
|
|
179
|
+
statusClasses: { class: string; count: number }[];
|
|
180
|
+
topPaths: { path: string; count: number }[];
|
|
181
|
+
topClients: { client: string; count: number }[];
|
|
182
|
+
methods: { method: string; count: number }[];
|
|
183
|
+
upgrades: number;
|
|
184
|
+
recent: { time: string; method: string; path: string; status: string; client: string; bytes: number }[];
|
|
185
|
+
history: number[];
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Optional telemetry: absent sections mean "this platform cannot see it". */
|
|
189
|
+
export interface Telemetry {
|
|
190
|
+
sessions: Session[];
|
|
191
|
+
logins: LoginEvent[];
|
|
192
|
+
failedLogins: LoginEvent[];
|
|
193
|
+
connections: Connection[];
|
|
194
|
+
listeners: Listener[];
|
|
195
|
+
services: ServiceUnit[];
|
|
196
|
+
containers: Container[];
|
|
197
|
+
interfaces: Interface[];
|
|
198
|
+
filesystems: Filesystem[];
|
|
199
|
+
kernel: KernelStats;
|
|
200
|
+
gpus: GpuStats[];
|
|
201
|
+
power: PowerStats | null;
|
|
202
|
+
journal: JournalEntry[];
|
|
203
|
+
states: ProcessStates;
|
|
204
|
+
/** Connection counts by state, for the sparkline. */
|
|
205
|
+
connectionHistory: number[];
|
|
206
|
+
sessionHistory: number[];
|
|
207
|
+
/** Live sockets grouped by protocol. */
|
|
208
|
+
protocols: ProtocolBucket[];
|
|
209
|
+
remotes: RemoteHost[];
|
|
210
|
+
inboundConnections: number;
|
|
211
|
+
outboundConnections: number;
|
|
212
|
+
net: NetCounters;
|
|
213
|
+
netInHistory: number[];
|
|
214
|
+
netOutHistory: number[];
|
|
215
|
+
retransHistory: number[];
|
|
216
|
+
ssh: SshEvent[];
|
|
217
|
+
http: HttpStats | null;
|
|
218
|
+
/** True when the process can see privileged sources. */
|
|
219
|
+
privileged: boolean;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function emptyTelemetry(): Telemetry {
|
|
223
|
+
return {
|
|
224
|
+
sessions: [],
|
|
225
|
+
logins: [],
|
|
226
|
+
failedLogins: [],
|
|
227
|
+
connections: [],
|
|
228
|
+
listeners: [],
|
|
229
|
+
services: [],
|
|
230
|
+
containers: [],
|
|
231
|
+
interfaces: [],
|
|
232
|
+
filesystems: [],
|
|
233
|
+
kernel: {
|
|
234
|
+
contextSwitches: 0, contextSwitchRate: 0, interrupts: 0, interruptRate: 0,
|
|
235
|
+
forks: 0, forkRate: 0, procsRunning: 0, procsBlocked: 0, entropy: 0,
|
|
236
|
+
openFiles: 0, maxFiles: 0, bootTime: 0, pageIn: 0, pageOut: 0, swapIn: 0, swapOut: 0,
|
|
237
|
+
},
|
|
238
|
+
gpus: [],
|
|
239
|
+
power: null,
|
|
240
|
+
journal: [],
|
|
241
|
+
states: { running: 0, sleeping: 0, stopped: 0, zombie: 0, total: 0 },
|
|
242
|
+
connectionHistory: [],
|
|
243
|
+
sessionHistory: [],
|
|
244
|
+
protocols: [],
|
|
245
|
+
remotes: [],
|
|
246
|
+
inboundConnections: 0,
|
|
247
|
+
outboundConnections: 0,
|
|
248
|
+
net: {
|
|
249
|
+
tcpActiveOpens: 0, tcpPassiveOpens: 0, tcpEstablished: 0, tcpInSegs: 0,
|
|
250
|
+
tcpOutSegs: 0, tcpRetransSegs: 0, tcpInErrs: 0, tcpOutRsts: 0,
|
|
251
|
+
udpInDatagrams: 0, udpOutDatagrams: 0, udpInErrors: 0,
|
|
252
|
+
icmpInMsgs: 0, icmpOutMsgs: 0,
|
|
253
|
+
rates: { inSegs: 0, outSegs: 0, retrans: 0, passiveOpens: 0, activeOpens: 0, udpIn: 0, udpOut: 0 },
|
|
254
|
+
retransRatio: 0,
|
|
255
|
+
},
|
|
256
|
+
netInHistory: [],
|
|
257
|
+
netOutHistory: [],
|
|
258
|
+
retransHistory: [],
|
|
259
|
+
ssh: [],
|
|
260
|
+
http: null,
|
|
261
|
+
privileged: typeof process.getuid === "function" ? process.getuid() === 0 : false,
|
|
262
|
+
};
|
|
263
|
+
}
|