@nocville/npc 0.2.1 → 0.2.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/LICENSE +21 -0
- package/README.md +58 -120
- package/dist/cjs/cli.js +55 -34
- package/dist/cjs/core/base-npc.js +8 -0
- package/dist/cjs/core/config.js +19 -2
- package/dist/cjs/core/errors.js +23 -0
- package/dist/cjs/core/http.js +121 -0
- package/dist/cjs/core/pixel-art.js +110 -0
- package/dist/cjs/core/polling-npc.js +99 -14
- package/dist/cjs/core/session-filter.js +3 -1
- package/dist/cjs/core/text.js +13 -0
- package/dist/cjs/core/thresholds.js +29 -0
- package/dist/cjs/index.js +10 -2
- package/dist/cjs/npcs/agent-common/session.js +1 -0
- package/dist/cjs/npcs/github-watcher/defaults.js +7 -1
- package/dist/cjs/npcs/github-watcher/github-api.js +82 -45
- package/dist/cjs/npcs/github-watcher/index.js +82 -36
- package/dist/cjs/npcs/internet-connectivity/checks.js +70 -67
- package/dist/cjs/npcs/internet-connectivity/defaults.js +5 -2
- package/dist/cjs/npcs/internet-connectivity/index.js +134 -131
- package/dist/cjs/npcs/log-watcher/buffer.js +61 -0
- package/dist/cjs/npcs/log-watcher/index.js +109 -103
- package/dist/cjs/npcs/log-watcher/tail.js +100 -53
- package/dist/cjs/npcs/process-watcher/index.js +61 -51
- package/dist/cjs/npcs/process-watcher/process-scanner.js +63 -69
- package/dist/cjs/npcs/receptionist/defaults.js +4 -0
- package/dist/cjs/npcs/receptionist/index.js +45 -19
- package/dist/cjs/npcs/receptionist/weather.js +42 -47
- package/dist/cjs/npcs/system-monitor/collectors.js +66 -17
- package/dist/cjs/npcs/system-monitor/index.js +52 -28
- package/dist/cjs/npcs/web-page-watcher/basic-mode.js +30 -14
- package/dist/cjs/npcs/web-page-watcher/capture.js +23 -1
- package/dist/cjs/npcs/web-page-watcher/defaults.js +44 -2
- package/dist/cjs/npcs/web-page-watcher/index.js +119 -68
- package/dist/cjs/npcs/web-page-watcher/pixelate.js +2 -1
- package/dist/cli.js +56 -35
- package/dist/core/base-npc.d.ts +5 -0
- package/dist/core/base-npc.js +8 -0
- package/dist/core/config.d.ts +5 -0
- package/dist/core/config.js +18 -2
- package/dist/core/errors.d.ts +11 -0
- package/dist/core/errors.js +18 -0
- package/dist/core/http.d.ts +52 -0
- package/dist/core/http.js +111 -0
- package/dist/core/pixel-art.d.ts +38 -0
- package/dist/core/pixel-art.js +105 -0
- package/dist/core/polling-npc.d.ts +22 -1
- package/dist/core/polling-npc.js +98 -13
- package/dist/core/session-filter.d.ts +2 -2
- package/dist/core/session-filter.js +3 -1
- package/dist/core/text.d.ts +6 -0
- package/dist/core/text.js +12 -0
- package/dist/core/thresholds.d.ts +9 -1
- package/dist/core/thresholds.js +27 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +4 -2
- package/dist/npcs/agent-common/session.js +1 -0
- package/dist/npcs/github-watcher/defaults.d.ts +6 -0
- package/dist/npcs/github-watcher/defaults.js +7 -1
- package/dist/npcs/github-watcher/github-api.d.ts +21 -7
- package/dist/npcs/github-watcher/github-api.js +80 -45
- package/dist/npcs/github-watcher/index.d.ts +6 -0
- package/dist/npcs/github-watcher/index.js +83 -37
- package/dist/npcs/internet-connectivity/checks.d.ts +15 -18
- package/dist/npcs/internet-connectivity/checks.js +70 -67
- package/dist/npcs/internet-connectivity/defaults.d.ts +3 -0
- package/dist/npcs/internet-connectivity/defaults.js +5 -2
- package/dist/npcs/internet-connectivity/index.d.ts +19 -6
- package/dist/npcs/internet-connectivity/index.js +135 -133
- package/dist/npcs/log-watcher/buffer.d.ts +22 -0
- package/dist/npcs/log-watcher/buffer.js +58 -0
- package/dist/npcs/log-watcher/index.d.ts +22 -21
- package/dist/npcs/log-watcher/index.js +112 -106
- package/dist/npcs/log-watcher/tail.d.ts +23 -3
- package/dist/npcs/log-watcher/tail.js +101 -54
- package/dist/npcs/process-watcher/index.d.ts +1 -1
- package/dist/npcs/process-watcher/index.js +62 -52
- package/dist/npcs/process-watcher/process-scanner.d.ts +29 -1
- package/dist/npcs/process-watcher/process-scanner.js +61 -71
- package/dist/npcs/receptionist/defaults.d.ts +4 -0
- package/dist/npcs/receptionist/defaults.js +4 -0
- package/dist/npcs/receptionist/index.d.ts +12 -2
- package/dist/npcs/receptionist/index.js +46 -20
- package/dist/npcs/receptionist/weather.d.ts +8 -6
- package/dist/npcs/receptionist/weather.js +39 -45
- package/dist/npcs/system-monitor/collectors.d.ts +15 -3
- package/dist/npcs/system-monitor/collectors.js +64 -17
- package/dist/npcs/system-monitor/defaults.d.ts +1 -1
- package/dist/npcs/system-monitor/index.d.ts +2 -1
- package/dist/npcs/system-monitor/index.js +54 -30
- package/dist/npcs/web-page-watcher/basic-mode.d.ts +2 -2
- package/dist/npcs/web-page-watcher/basic-mode.js +30 -14
- package/dist/npcs/web-page-watcher/capture.d.ts +18 -0
- package/dist/npcs/web-page-watcher/capture.js +21 -1
- package/dist/npcs/web-page-watcher/defaults.d.ts +23 -0
- package/dist/npcs/web-page-watcher/defaults.js +41 -1
- package/dist/npcs/web-page-watcher/index.d.ts +18 -1
- package/dist/npcs/web-page-watcher/index.js +121 -70
- package/dist/npcs/web-page-watcher/pixelate.js +2 -1
- package/package.json +2 -7
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.InternetConnectivityNPC = void 0;
|
|
4
4
|
exports.evaluateConnectivityStatus = evaluateConnectivityStatus;
|
|
5
|
+
exports.parseLatencyHost = parseLatencyHost;
|
|
5
6
|
const polling_npc_js_1 = require("../../core/polling-npc.js");
|
|
6
7
|
const thresholds_js_1 = require("../../core/thresholds.js");
|
|
7
8
|
const screen_js_1 = require("../../core/screen.js");
|
|
8
9
|
const config_js_1 = require("../../core/config.js");
|
|
10
|
+
const errors_js_1 = require("../../core/errors.js");
|
|
11
|
+
const http_js_1 = require("../../core/http.js");
|
|
9
12
|
const checks_js_1 = require("./checks.js");
|
|
10
13
|
const defaults_js_1 = require("./defaults.js");
|
|
11
14
|
/**
|
|
@@ -16,13 +19,15 @@ const defaults_js_1 = require("./defaults.js");
|
|
|
16
19
|
function evaluateConnectivityStatus(sample, thresholds) {
|
|
17
20
|
if (sample.allFailed)
|
|
18
21
|
return 'critical';
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
(0, thresholds_js_1.evaluateThreshold)(sample.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
const statuses = [sample.anyFailed ? 'warning' : 'healthy'];
|
|
23
|
+
if (sample.dnsAvg !== null)
|
|
24
|
+
statuses.push((0, thresholds_js_1.evaluateThreshold)(sample.dnsAvg, thresholds.dnsTime));
|
|
25
|
+
if (sample.httpAvg !== null)
|
|
26
|
+
statuses.push((0, thresholds_js_1.evaluateThreshold)(sample.httpAvg, thresholds.httpTime));
|
|
27
|
+
if (sample.latencyAvg !== null) {
|
|
28
|
+
statuses.push((0, thresholds_js_1.evaluateThreshold)(sample.latencyAvg, thresholds.latencyTime));
|
|
29
|
+
}
|
|
30
|
+
return (0, thresholds_js_1.worstStatus)(statuses);
|
|
26
31
|
}
|
|
27
32
|
/** Scale a threshold set by a ratio, preserving direction. */
|
|
28
33
|
function scaleThresholds(base, ratio) {
|
|
@@ -37,6 +42,30 @@ function scaleThresholds(base, ratio) {
|
|
|
37
42
|
latencyTime: scale(base.latencyTime),
|
|
38
43
|
};
|
|
39
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Parse a latency target: "host", "host:port", "[ipv6]:port" or a bare IPv6
|
|
47
|
+
* address. Throws NpcConfigError on an out-of-range port.
|
|
48
|
+
*/
|
|
49
|
+
function parseLatencyHost(input, defaultPort = defaults_js_1.DEFAULTS.latencyPort) {
|
|
50
|
+
const s = input.trim();
|
|
51
|
+
const toPort = (raw) => {
|
|
52
|
+
const port = Number(raw);
|
|
53
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
54
|
+
throw new errors_js_1.NpcConfigError(`Invalid port in latency host "${s}"`);
|
|
55
|
+
}
|
|
56
|
+
return port;
|
|
57
|
+
};
|
|
58
|
+
const bracketed = s.match(/^\[([^\]]+)\](?::(\d*))?$/);
|
|
59
|
+
if (bracketed) {
|
|
60
|
+
return { host: bracketed[1], port: bracketed[2] ? toPort(bracketed[2]) : defaultPort };
|
|
61
|
+
}
|
|
62
|
+
const colons = s.split(':').length - 1;
|
|
63
|
+
if (colons === 1) {
|
|
64
|
+
const [host, port] = s.split(':');
|
|
65
|
+
return { host, port: toPort(port) };
|
|
66
|
+
}
|
|
67
|
+
return { host: s, port: defaultPort }; // plain host, or a bare IPv6 address
|
|
68
|
+
}
|
|
40
69
|
class InternetConnectivityNPC extends polling_npc_js_1.PollingNPC {
|
|
41
70
|
dnsHosts;
|
|
42
71
|
httpUrls;
|
|
@@ -53,10 +82,16 @@ class InternetConnectivityNPC extends polling_npc_js_1.PollingNPC {
|
|
|
53
82
|
});
|
|
54
83
|
this.dnsHosts = config.dnsHosts ?? defaults_js_1.DEFAULTS.dnsHosts;
|
|
55
84
|
this.httpUrls = config.httpUrls ?? defaults_js_1.DEFAULTS.httpUrls;
|
|
56
|
-
this.latencyHosts = config.latencyHosts
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
85
|
+
this.latencyHosts = (config.latencyHosts ?? defaults_js_1.DEFAULTS.latencyHosts).map((h) => typeof h === 'string' ? parseLatencyHost(h) : h);
|
|
86
|
+
// Every check must finish well inside the poll interval.
|
|
87
|
+
this.httpTimeout = Math.min(Math.max(500, config.httpTimeout ?? defaults_js_1.DEFAULTS.httpTimeout), this.pollInterval / 2);
|
|
88
|
+
const badUrl = this.httpUrls.find((u) => !(0, http_js_1.isHttpUrl)(u));
|
|
89
|
+
if (badUrl !== undefined) {
|
|
90
|
+
throw new errors_js_1.NpcConfigError(`--http-urls: "${badUrl}" is not an http(s) URL`);
|
|
91
|
+
}
|
|
92
|
+
if (this.dnsHosts.length === 0 && this.httpUrls.length === 0) {
|
|
93
|
+
throw new errors_js_1.NpcConfigError('internet-connectivity needs at least one DNS host or HTTP URL');
|
|
94
|
+
}
|
|
60
95
|
// Threshold resolution: explicit thresholds win; else if the legacy
|
|
61
96
|
// --slow-threshold differs from the default, scale the live thresholds by
|
|
62
97
|
// that ratio for back-compat; else use the defaults.
|
|
@@ -92,115 +127,91 @@ class InternetConnectivityNPC extends polling_npc_js_1.PollingNPC {
|
|
|
92
127
|
}
|
|
93
128
|
async collect() {
|
|
94
129
|
const [dnsResults, httpResults, latencyResults] = await Promise.all([
|
|
95
|
-
(0, checks_js_1.checkDns)(this.dnsHosts),
|
|
130
|
+
(0, checks_js_1.checkDns)(this.dnsHosts, this.httpTimeout),
|
|
96
131
|
(0, checks_js_1.checkHttp)(this.httpUrls, this.httpTimeout),
|
|
97
|
-
(0, checks_js_1.checkLatency)(this.latencyHosts),
|
|
132
|
+
(0, checks_js_1.checkLatency)(this.latencyHosts, this.httpTimeout),
|
|
98
133
|
]);
|
|
99
|
-
const dnsOk = dnsResults
|
|
100
|
-
const
|
|
101
|
-
const
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
const latencyAvg = avgTime(latencyResults);
|
|
134
|
+
const dnsOk = countOk(dnsResults);
|
|
135
|
+
const httpOk = countOk(httpResults);
|
|
136
|
+
const latencyOk = countOk(latencyResults);
|
|
137
|
+
const dnsAvg = avgOkTime(dnsResults);
|
|
138
|
+
const httpAvg = avgOkTime(httpResults);
|
|
139
|
+
const latencyAvg = avgOkTime(latencyResults);
|
|
106
140
|
const isOnline = dnsOk > 0 || httpOk > 0;
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const message = allFailed
|
|
114
|
-
? this.buildOfflineMessage(dnsResults, httpResults)
|
|
115
|
-
: this.buildOnlineMessage(dnsAvg, httpAvg, latencyAvg);
|
|
116
|
-
(0, screen_js_1.pushHistory)(this.dnsHistory, round(dnsAvg));
|
|
117
|
-
(0, screen_js_1.pushHistory)(this.httpHistory, round(httpAvg));
|
|
118
|
-
(0, screen_js_1.pushHistory)(this.latencyHistory, round(latencyAvg));
|
|
119
|
-
const screenData = {
|
|
120
|
-
layout: 'multi-metric',
|
|
121
|
-
values: [
|
|
122
|
-
{
|
|
123
|
-
label: 'DNS',
|
|
124
|
-
value: `${round(dnsAvg)}ms`,
|
|
125
|
-
color: (0, screen_js_1.statusColor)((0, thresholds_js_1.evaluateThreshold)(dnsAvg, this.thresholds.dnsTime)),
|
|
126
|
-
sparkline: [...this.dnsHistory],
|
|
127
|
-
},
|
|
128
|
-
{
|
|
129
|
-
label: 'HTTP',
|
|
130
|
-
value: `${round(httpAvg)}ms`,
|
|
131
|
-
color: (0, screen_js_1.statusColor)((0, thresholds_js_1.evaluateThreshold)(httpAvg, this.thresholds.httpTime)),
|
|
132
|
-
sparkline: [...this.httpHistory],
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
label: 'TCP',
|
|
136
|
-
value: `${round(latencyAvg)}ms`,
|
|
137
|
-
color: (0, screen_js_1.statusColor)((0, thresholds_js_1.evaluateThreshold)(latencyAvg, this.thresholds.latencyTime)),
|
|
138
|
-
sparkline: [...this.latencyHistory],
|
|
139
|
-
},
|
|
140
|
-
{ label: 'NET', value: isOnline ? 'ONLINE' : 'OFFLINE', color: isOnline ? 'green' : 'red' },
|
|
141
|
-
],
|
|
142
|
-
};
|
|
141
|
+
const anyFailed = dnsOk < dnsResults.length || httpOk < httpResults.length || latencyOk < latencyResults.length;
|
|
142
|
+
const status = evaluateConnectivityStatus({ allFailed: !isOnline, anyFailed, dnsAvg, httpAvg, latencyAvg }, this.thresholds);
|
|
143
|
+
const message = isOnline
|
|
144
|
+
? `Online | DNS: ${fmtMs(dnsAvg)} | HTTP: ${fmtMs(httpAvg)} | Latency: ${fmtMs(latencyAvg)}` +
|
|
145
|
+
(anyFailed ? ` | ${failureSummary(dnsResults, httpResults, latencyResults)}` : '')
|
|
146
|
+
: offlineMessage(dnsResults, httpResults);
|
|
143
147
|
return {
|
|
144
148
|
status,
|
|
145
|
-
mood,
|
|
149
|
+
mood: (0, thresholds_js_1.moodForStatus)(status),
|
|
146
150
|
message,
|
|
147
151
|
subStatus: isOnline ? undefined : 'offline',
|
|
148
|
-
screenData
|
|
152
|
+
screenData: {
|
|
153
|
+
layout: 'multi-metric',
|
|
154
|
+
values: [
|
|
155
|
+
this.panel('DNS', dnsResults, dnsAvg, this.dnsHistory, this.thresholds.dnsTime),
|
|
156
|
+
this.panel('HTTP', httpResults, httpAvg, this.httpHistory, this.thresholds.httpTime),
|
|
157
|
+
this.panel('TCP', latencyResults, latencyAvg, this.latencyHistory, this.thresholds.latencyTime),
|
|
158
|
+
{
|
|
159
|
+
label: 'NET',
|
|
160
|
+
value: isOnline ? 'ONLINE' : 'OFFLINE',
|
|
161
|
+
color: isOnline ? 'green' : 'red',
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
},
|
|
149
165
|
metrics: {
|
|
150
|
-
dns_avg_ms: round(dnsAvg),
|
|
151
|
-
http_avg_ms: round(httpAvg),
|
|
152
|
-
latency_avg_ms: round(latencyAvg),
|
|
166
|
+
dns_avg_ms: round(dnsAvg ?? 0),
|
|
167
|
+
http_avg_ms: round(httpAvg ?? 0),
|
|
168
|
+
latency_avg_ms: round(latencyAvg ?? 0),
|
|
153
169
|
dns_ok_count: dnsOk,
|
|
154
|
-
dns_fail_count:
|
|
170
|
+
dns_fail_count: dnsResults.length - dnsOk,
|
|
155
171
|
http_ok_count: httpOk,
|
|
156
|
-
http_fail_count:
|
|
172
|
+
http_fail_count: httpResults.length - httpOk,
|
|
173
|
+
latency_ok_count: latencyOk,
|
|
174
|
+
latency_fail_count: latencyResults.length - latencyOk,
|
|
157
175
|
is_online: isOnline ? 1 : 0,
|
|
158
176
|
},
|
|
159
177
|
};
|
|
160
178
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
179
|
+
/**
|
|
180
|
+
* One dashboard panel. A category whose checks all failed shows FAIL in red
|
|
181
|
+
* (and a 0 sparkline sample) rather than a misleading latency; a partial
|
|
182
|
+
* failure is amber.
|
|
183
|
+
*/
|
|
184
|
+
panel(label, results, avg, history, threshold) {
|
|
185
|
+
if (results.length === 0)
|
|
186
|
+
return { label, value: 'off', color: 'gray' };
|
|
187
|
+
(0, screen_js_1.pushHistory)(history, round(avg ?? 0));
|
|
188
|
+
const sparkline = [...history];
|
|
189
|
+
if (avg === null)
|
|
190
|
+
return { label, value: 'FAIL', color: 'red', sparkline };
|
|
191
|
+
const color = countOk(results) < results.length ? 'amber' : (0, screen_js_1.statusColor)((0, thresholds_js_1.evaluateThreshold)(avg, threshold));
|
|
192
|
+
return { label, value: fmtMs(avg), color, sparkline };
|
|
173
193
|
}
|
|
174
194
|
/**
|
|
175
|
-
* Build config from CLI flags.
|
|
195
|
+
* Build config from CLI flags. List flags are comma-separated; latency
|
|
196
|
+
* hosts accept "host", "host:port" or "[ipv6]:port".
|
|
176
197
|
*/
|
|
177
198
|
static configFromFlags(flags) {
|
|
178
199
|
const config = {};
|
|
179
|
-
const
|
|
180
|
-
if (
|
|
181
|
-
config.dnsHosts =
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
if (latencyHostsRaw) {
|
|
189
|
-
config.latencyHosts = latencyHostsRaw.split(',').map((s) => {
|
|
190
|
-
const trimmed = s.trim();
|
|
191
|
-
const colonIndex = trimmed.lastIndexOf(':');
|
|
192
|
-
if (colonIndex !== -1) {
|
|
193
|
-
const host = trimmed.slice(0, colonIndex);
|
|
194
|
-
const port = Number(trimmed.slice(colonIndex + 1));
|
|
195
|
-
if (!Number.isNaN(port))
|
|
196
|
-
return { host, port };
|
|
197
|
-
}
|
|
198
|
-
return { host: trimmed, port: 80 };
|
|
199
|
-
});
|
|
200
|
-
}
|
|
200
|
+
const dnsHosts = (0, config_js_1.listFlag)(flags, 'dns-hosts');
|
|
201
|
+
if (dnsHosts)
|
|
202
|
+
config.dnsHosts = dnsHosts;
|
|
203
|
+
const httpUrls = (0, config_js_1.listFlag)(flags, 'http-urls');
|
|
204
|
+
if (httpUrls)
|
|
205
|
+
config.httpUrls = httpUrls;
|
|
206
|
+
const latencyHosts = (0, config_js_1.listFlag)(flags, 'latency-hosts');
|
|
207
|
+
if (latencyHosts)
|
|
208
|
+
config.latencyHosts = latencyHosts.map((h) => parseLatencyHost(h));
|
|
201
209
|
const slowThreshold = (0, config_js_1.numberFlag)(flags, 'slow-threshold');
|
|
202
210
|
if (slowThreshold !== undefined)
|
|
203
211
|
config.slowThreshold = slowThreshold;
|
|
212
|
+
const timeout = (0, config_js_1.numberFlag)(flags, 'timeout');
|
|
213
|
+
if (timeout !== undefined)
|
|
214
|
+
config.httpTimeout = timeout;
|
|
204
215
|
const pollInterval = (0, config_js_1.numberFlag)(flags, 'poll-interval');
|
|
205
216
|
if (pollInterval !== undefined)
|
|
206
217
|
config.pollInterval = pollInterval;
|
|
@@ -209,43 +220,35 @@ class InternetConnectivityNPC extends polling_npc_js_1.PollingNPC {
|
|
|
209
220
|
}
|
|
210
221
|
exports.InternetConnectivityNPC = InternetConnectivityNPC;
|
|
211
222
|
// ---------- Helpers ----------
|
|
212
|
-
function
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
223
|
+
function countOk(results) {
|
|
224
|
+
return results.filter((r) => r.ok).length;
|
|
225
|
+
}
|
|
226
|
+
/** Mean time of the successful checks only — a 5s timeout is a failure, not a latency. */
|
|
227
|
+
function avgOkTime(results) {
|
|
228
|
+
const ok = results.filter((r) => r.ok);
|
|
229
|
+
if (ok.length === 0)
|
|
230
|
+
return null;
|
|
231
|
+
return ok.reduce((acc, r) => acc + r.timeMs, 0) / ok.length;
|
|
217
232
|
}
|
|
218
233
|
function round(n) {
|
|
219
234
|
return Math.round(n * 100) / 100;
|
|
220
235
|
}
|
|
221
|
-
function
|
|
222
|
-
|
|
223
|
-
case 'healthy':
|
|
224
|
-
return 'happy';
|
|
225
|
-
case 'warning':
|
|
226
|
-
return 'concerned';
|
|
227
|
-
case 'critical':
|
|
228
|
-
return 'stressed';
|
|
229
|
-
default:
|
|
230
|
-
return 'idle';
|
|
231
|
-
}
|
|
236
|
+
function fmtMs(n) {
|
|
237
|
+
return n === null ? 'FAIL' : `${Math.round(n)}ms`;
|
|
232
238
|
}
|
|
233
|
-
/**
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
return { host: item, port: 80 };
|
|
249
|
-
});
|
|
239
|
+
/** "failing: 8.8.8.8:443, dns.example" — names the targets, never error text. */
|
|
240
|
+
function failureSummary(...groups) {
|
|
241
|
+
const failed = groups.flat().filter((r) => !r.ok);
|
|
242
|
+
return `failing: ${failed.map((r) => r.target).join(', ')}`;
|
|
243
|
+
}
|
|
244
|
+
function offlineMessage(dnsResults, httpResults) {
|
|
245
|
+
const problems = [];
|
|
246
|
+
if (dnsResults.length > 0 && dnsResults.every((r) => !r.ok)) {
|
|
247
|
+
problems.push('DNS resolution failed');
|
|
248
|
+
}
|
|
249
|
+
if (httpResults.length > 0 && httpResults.every((r) => !r.ok)) {
|
|
250
|
+
problems.push('no HTTP connectivity');
|
|
251
|
+
}
|
|
252
|
+
return `OFFLINE: ${problems.join(', ') || 'all checks failed'}`;
|
|
250
253
|
}
|
|
251
254
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WindowCounter = void 0;
|
|
3
4
|
exports.classifyLine = classifyLine;
|
|
5
|
+
exports.cleanLogLine = cleanLogLine;
|
|
4
6
|
exports.pushRecent = pushRecent;
|
|
5
7
|
exports.buildLogScreen = buildLogScreen;
|
|
6
8
|
exports.shouldEmitBurst = shouldEmitBurst;
|
|
9
|
+
const text_js_1 = require("../../core/text.js");
|
|
10
|
+
const activity_js_1 = require("../agent-common/activity.js");
|
|
7
11
|
/** Classify a raw log line against the error/warning patterns. */
|
|
8
12
|
function classifyLine(line, errorRe, warningRe) {
|
|
9
13
|
if (errorRe.test(line))
|
|
@@ -12,6 +16,23 @@ function classifyLine(line, errorRe, warningRe) {
|
|
|
12
16
|
return 'warning';
|
|
13
17
|
return 'info';
|
|
14
18
|
}
|
|
19
|
+
// ANSI CSI/OSC escape sequences (colours, cursor moves, hyperlinks).
|
|
20
|
+
// eslint-disable-next-line no-control-regex
|
|
21
|
+
const ANSI = /\u001b(?:\[[0-?]*[ -/]*[@-~]|\][^\u0007\u001b]*(?:\u0007|\u001b\\))/g;
|
|
22
|
+
// `password=…`, `"token": "…"`, `Authorization: …` style pairs common in logs
|
|
23
|
+
// (redactSecrets covers CLI flags, bearer tokens, URL credentials and keys).
|
|
24
|
+
const SECRET_PAIR = /\b([\w-]*(?:password|passwd|pwd|secret|token|api[_-]?key|access[_-]?key|authorization|cookie|session)[\w-]*)(["']?\s*[:=]\s*)("[^"]*"|'[^']*'|[^\s,;&]+)/gi;
|
|
25
|
+
/**
|
|
26
|
+
* Prepare a log line for display to the whole team: strip terminal escapes,
|
|
27
|
+
* redact anything secret-looking, drop control characters and truncate.
|
|
28
|
+
* Log lines routinely contain tokens and passwords; only redacted text ever
|
|
29
|
+
* leaves the machine.
|
|
30
|
+
*/
|
|
31
|
+
function cleanLogLine(line, max = 200) {
|
|
32
|
+
const plain = line.replace(ANSI, '');
|
|
33
|
+
const redacted = (0, activity_js_1.redactSecrets)(plain).replace(SECRET_PAIR, (_m, key, sep) => `${key}${sep}***`);
|
|
34
|
+
return (0, text_js_1.sanitizeText)(redacted, max);
|
|
35
|
+
}
|
|
15
36
|
/**
|
|
16
37
|
* Push a line into the bounded ring buffer: each entry is capped at `cap`
|
|
17
38
|
* characters and the buffer keeps only the last `max` entries.
|
|
@@ -37,4 +58,44 @@ function buildLogScreen(buffer) {
|
|
|
37
58
|
function shouldEmitBurst(lastEmit, now, windowMs = 5000) {
|
|
38
59
|
return now - lastEmit >= windowMs;
|
|
39
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Sliding-window error/warning counter in one-second buckets: memory is
|
|
63
|
+
* bounded by the window length, not by how many lines a runaway logger writes.
|
|
64
|
+
*/
|
|
65
|
+
class WindowCounter {
|
|
66
|
+
windowMs;
|
|
67
|
+
buckets = new Map();
|
|
68
|
+
constructor(windowMs) {
|
|
69
|
+
this.windowMs = windowMs;
|
|
70
|
+
}
|
|
71
|
+
add(level, now = Date.now()) {
|
|
72
|
+
const key = Math.floor(now / 1000);
|
|
73
|
+
const bucket = this.buckets.get(key) ?? { errors: 0, warnings: 0 };
|
|
74
|
+
if (level === 'error')
|
|
75
|
+
bucket.errors++;
|
|
76
|
+
else
|
|
77
|
+
bucket.warnings++;
|
|
78
|
+
this.buckets.set(key, bucket);
|
|
79
|
+
if (this.buckets.size > this.windowMs / 1000 + 2)
|
|
80
|
+
this.prune(now);
|
|
81
|
+
}
|
|
82
|
+
counts(now = Date.now()) {
|
|
83
|
+
this.prune(now);
|
|
84
|
+
let errors = 0;
|
|
85
|
+
let warnings = 0;
|
|
86
|
+
for (const b of this.buckets.values()) {
|
|
87
|
+
errors += b.errors;
|
|
88
|
+
warnings += b.warnings;
|
|
89
|
+
}
|
|
90
|
+
return { errors, warnings };
|
|
91
|
+
}
|
|
92
|
+
prune(now) {
|
|
93
|
+
const oldest = Math.floor((now - this.windowMs) / 1000);
|
|
94
|
+
for (const key of this.buckets.keys()) {
|
|
95
|
+
if (key < oldest)
|
|
96
|
+
this.buckets.delete(key);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
exports.WindowCounter = WindowCounter;
|
|
40
101
|
//# sourceMappingURL=buffer.js.map
|