@profullstack/hqtui-demo 0.1.2 → 0.1.4

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.
Files changed (48) hide show
  1. package/README.md +74 -8
  2. package/dist/main.js +36 -12
  3. package/dist/main.js.map +1 -1
  4. package/dist/screens/dashboard.js +11 -3
  5. package/dist/screens/dashboard.js.map +1 -1
  6. package/dist/screens/index.js +4 -0
  7. package/dist/screens/index.js.map +1 -1
  8. package/dist/screens/network.js +86 -0
  9. package/dist/screens/network.js.map +1 -0
  10. package/dist/screens/services.js +121 -0
  11. package/dist/screens/services.js.map +1 -0
  12. package/dist/screens/sessions.js +80 -0
  13. package/dist/screens/sessions.js.map +1 -0
  14. package/dist/screens/traffic.js +183 -0
  15. package/dist/screens/traffic.js.map +1 -0
  16. package/dist/simulation.js +6 -0
  17. package/dist/simulation.js.map +1 -1
  18. package/dist/state.js +6 -2
  19. package/dist/state.js.map +1 -1
  20. package/dist/system/common.js +2 -0
  21. package/dist/system/common.js.map +1 -1
  22. package/dist/system/linux-telemetry.js +568 -0
  23. package/dist/system/linux-telemetry.js.map +1 -0
  24. package/dist/system/linux-traffic.js +282 -0
  25. package/dist/system/linux-traffic.js.map +1 -0
  26. package/dist/system/linux.js +119 -28
  27. package/dist/system/linux.js.map +1 -1
  28. package/dist/system/simulated-telemetry.js +267 -0
  29. package/dist/system/simulated-telemetry.js.map +1 -0
  30. package/dist/system/telemetry.js +44 -0
  31. package/dist/system/telemetry.js.map +1 -0
  32. package/package.json +4 -3
  33. package/src/main.ts +28 -12
  34. package/src/screens/dashboard.ts +9 -3
  35. package/src/screens/index.ts +4 -0
  36. package/src/screens/network.ts +91 -0
  37. package/src/screens/services.ts +136 -0
  38. package/src/screens/sessions.ts +85 -0
  39. package/src/screens/traffic.ts +199 -0
  40. package/src/simulation.ts +12 -0
  41. package/src/state.ts +16 -3
  42. package/src/system/common.ts +2 -0
  43. package/src/system/linux-telemetry.ts +584 -0
  44. package/src/system/linux-traffic.ts +375 -0
  45. package/src/system/linux.ts +128 -24
  46. package/src/system/simulated-telemetry.ts +280 -0
  47. package/src/system/telemetry.ts +263 -0
  48. package/src/system/types.ts +2 -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
+ }
@@ -7,6 +7,8 @@ export interface Collector {
7
7
  source: string;
8
8
  /** Metrics this platform could not provide. Shown in the help screen. */
9
9
  unavailable: string[];
10
+ /** Why hardware sensors are missing, when they are. */
11
+ sensorNote?: string;
10
12
  refresh(dt: number): Promise<void>;
11
13
  current(): SystemSample;
12
14
  }