@poolzin/pool-bot 2026.3.9 → 2026.3.10
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/CHANGELOG.md +24 -0
- package/README.md +147 -69
- package/dist/.buildstamp +1 -1
- package/dist/agents/error-classifier.js +26 -77
- package/dist/agents/skills/security.js +1 -7
- package/dist/build-info.json +3 -3
- package/dist/cli/cron-cli/register.cron-dashboard.js +339 -0
- package/dist/cli/cron-cli/register.js +2 -0
- package/dist/cli/errors.js +187 -0
- package/dist/cli/program/command-registry.js +13 -0
- package/dist/cli/program/register.maintenance.js +21 -0
- package/dist/cli/program/register.subclis.js +9 -0
- package/dist/cli/swarm-cli/register.js +8 -0
- package/dist/cli/swarm-cli/register.swarm-status.js +488 -0
- package/dist/cli/telemetry-cli/register.js +10 -0
- package/dist/cli/telemetry-cli/register.telemetry-alerts.js +176 -0
- package/dist/cli/telemetry-cli/register.telemetry-metrics.js +323 -0
- package/dist/cli/telemetry-cli/register.telemetry-status.js +179 -0
- package/dist/commands/doctor-checks.js +498 -0
- package/dist/context-engine/index.js +1 -1
- package/dist/context-engine/legacy.js +1 -3
- package/dist/context-engine/summarizing.js +5 -8
- package/dist/cron/service/timer.js +18 -0
- package/dist/gateway/protocol/index.js +5 -2
- package/dist/gateway/protocol/schema/error-codes.js +1 -0
- package/dist/gateway/protocol/schema/swarm.js +80 -0
- package/dist/gateway/protocol/schema.js +1 -0
- package/dist/gateway/server-close.js +4 -0
- package/dist/gateway/server-constants.js +1 -0
- package/dist/gateway/server-cron.js +29 -0
- package/dist/gateway/server-maintenance.js +35 -2
- package/dist/gateway/server-methods/swarm.js +58 -0
- package/dist/gateway/server-methods/telemetry.js +71 -0
- package/dist/gateway/server-methods-list.js +8 -0
- package/dist/gateway/server-methods.js +9 -2
- package/dist/gateway/server.impl.js +33 -16
- package/dist/infra/abort-pattern.js +4 -4
- package/dist/infra/retry.js +3 -1
- package/dist/skills/commands.js +7 -25
- package/dist/skills/index.js +14 -17
- package/dist/skills/parser.js +12 -27
- package/dist/skills/registry.js +3 -6
- package/dist/skills/security.js +2 -8
- package/dist/swarm/service.js +247 -0
- package/dist/telemetry/alert-engine.js +258 -0
- package/dist/telemetry/cron-instrumentation.js +49 -0
- package/dist/telemetry/gateway-instrumentation.js +80 -0
- package/dist/telemetry/instrumentation.js +66 -0
- package/dist/telemetry/service.js +345 -0
- package/dist/tui/components/assistant-message.js +6 -2
- package/dist/tui/components/hyperlink-markdown.js +32 -0
- package/dist/tui/components/searchable-select-list.js +12 -1
- package/dist/tui/components/user-message.js +6 -2
- package/dist/tui/index.js +22 -6
- package/dist/tui/theme/theme-detection.js +226 -0
- package/dist/tui/tui-command-handlers.js +20 -0
- package/dist/tui/tui-formatters.js +4 -3
- package/dist/tui/utils/ctrl-c-handler.js +67 -0
- package/dist/tui/utils/osc8-hyperlinks.js +208 -0
- package/dist/tui/utils/safe-stop.js +180 -0
- package/dist/tui/utils/session-key-utils.js +81 -0
- package/dist/tui/utils/text-sanitization.js +284 -0
- package/dist/utils/lru-cache.js +116 -0
- package/dist/utils/performance.js +199 -0
- package/dist/utils/retry.js +240 -0
- package/docs/MELHORIAS_IMPLEMENTADAS.md +228 -0
- package/docs/MELHORIAS_PROFISSIONAIS.md +282 -0
- package/docs/PLANO_ACAO_TUI.md +357 -0
- package/docs/PROGRESSO_TUI.md +66 -0
- package/docs/RELATORIO_FINAL.md +217 -0
- package/docs/diagnostico-shell-completion.md +265 -0
- package/docs/features/advanced-memory.md +585 -0
- package/docs/features/discord-components-v2.md +277 -0
- package/docs/features/swarm.md +100 -0
- package/docs/features/telemetry.md +284 -0
- package/docs/integrations/INTEGRATION_PLAN.md +665 -345
- package/docs/models/provider-infrastructure.md +400 -0
- package/docs/security/exec-approvals.md +294 -0
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/discord/package.json +1 -1
- package/extensions/feishu/package.json +1 -1
- package/extensions/google-antigravity-auth/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/googlechat/package.json +1 -1
- package/extensions/hexstrike-bridge/README.md +119 -0
- package/extensions/hexstrike-bridge/index.test.ts +247 -0
- package/extensions/hexstrike-bridge/index.ts +487 -0
- package/extensions/hexstrike-bridge/package.json +17 -0
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/matrix/CHANGELOG.md +5 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/mattermost/package.json +1 -1
- package/extensions/mcp-server/index.ts +14 -0
- package/extensions/mcp-server/package.json +11 -0
- package/extensions/mcp-server/src/service.ts +540 -0
- package/extensions/memory-core/package.json +1 -1
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +5 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +5 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/openai-codex-auth/package.json +1 -1
- package/extensions/signal/package.json +1 -1
- package/extensions/slack/package.json +1 -1
- package/extensions/telegram/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +5 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +5 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +5 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +5 -0
- package/extensions/zalouser/package.json +1 -1
- package/package.json +8 -1
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { danger } from "../../globals.js";
|
|
2
|
+
import { defaultRuntime } from "../../runtime.js";
|
|
3
|
+
import { colorize, isRich, theme } from "../../terminal/theme.js";
|
|
4
|
+
import { addGatewayClientOptions, callGatewayFromCli } from "../gateway-rpc.js";
|
|
5
|
+
import { renderTable } from "../../terminal/table.js";
|
|
6
|
+
// Sparkline characters for visual history
|
|
7
|
+
const SPARK_CHARS = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"];
|
|
8
|
+
// Print metrics dashboard header
|
|
9
|
+
function printDashboardHeader(runtime = defaultRuntime) {
|
|
10
|
+
const rich = isRich();
|
|
11
|
+
const now = new Date();
|
|
12
|
+
const timestamp = now.toLocaleString();
|
|
13
|
+
runtime.log("");
|
|
14
|
+
if (rich) {
|
|
15
|
+
runtime.log(colorize(rich, theme.heading, "╔═══════════════════════════════════════════════════════════╗"));
|
|
16
|
+
runtime.log(colorize(rich, theme.heading, `║ 📈 TELEMETRY METRICS ${timestamp.padStart(26)} ║`));
|
|
17
|
+
runtime.log(colorize(rich, theme.heading, "╚═══════════════════════════════════════════════════════════╝"));
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
runtime.log("═══════════════════════════════════════════════════════════");
|
|
21
|
+
runtime.log(`TELEMETRY METRICS - ${timestamp}`);
|
|
22
|
+
runtime.log("═══════════════════════════════════════════════════════════");
|
|
23
|
+
}
|
|
24
|
+
runtime.log("");
|
|
25
|
+
}
|
|
26
|
+
// Generate sparkline from value (normalized 0-1)
|
|
27
|
+
function generateSparkline(value, rich) {
|
|
28
|
+
const normalized = Math.max(0, Math.min(1, value));
|
|
29
|
+
const index = Math.floor(normalized * (SPARK_CHARS.length - 1));
|
|
30
|
+
const char = SPARK_CHARS[index];
|
|
31
|
+
if (!rich)
|
|
32
|
+
return char;
|
|
33
|
+
if (normalized > 0.7)
|
|
34
|
+
return colorize(rich, theme.success, char);
|
|
35
|
+
if (normalized > 0.4)
|
|
36
|
+
return colorize(rich, theme.warn, char);
|
|
37
|
+
return colorize(rich, theme.error, char);
|
|
38
|
+
}
|
|
39
|
+
// Format number with commas
|
|
40
|
+
function formatNumber(num) {
|
|
41
|
+
return num.toLocaleString();
|
|
42
|
+
}
|
|
43
|
+
// Format duration in ms
|
|
44
|
+
function formatDuration(ms) {
|
|
45
|
+
if (ms < 1)
|
|
46
|
+
return `${(ms * 1000).toFixed(2)}μs`;
|
|
47
|
+
if (ms < 1000)
|
|
48
|
+
return `${ms.toFixed(1)}ms`;
|
|
49
|
+
if (ms < 60000)
|
|
50
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
51
|
+
return `${(ms / 60000).toFixed(1)}m`;
|
|
52
|
+
}
|
|
53
|
+
// Print counters table
|
|
54
|
+
function printCountersTable(counters, runtime = defaultRuntime) {
|
|
55
|
+
const rich = isRich();
|
|
56
|
+
runtime.log(rich ? colorize(rich, theme.heading, " Counters") : " Counters");
|
|
57
|
+
const entries = Object.entries(counters).sort((a, b) => b[1] - a[1]);
|
|
58
|
+
if (entries.length === 0) {
|
|
59
|
+
runtime.log(rich
|
|
60
|
+
? colorize(rich, theme.muted, " No counter metrics available.")
|
|
61
|
+
: " No counter metrics available.");
|
|
62
|
+
runtime.log("");
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const maxValue = Math.max(...entries.map(([, v]) => v));
|
|
66
|
+
const columns = [
|
|
67
|
+
{
|
|
68
|
+
key: "name",
|
|
69
|
+
header: rich ? colorize(rich, theme.heading, "Name") : "Name",
|
|
70
|
+
align: "left",
|
|
71
|
+
minWidth: 30,
|
|
72
|
+
flex: true,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
key: "value",
|
|
76
|
+
header: rich ? colorize(rich, theme.heading, "Value") : "Value",
|
|
77
|
+
align: "right",
|
|
78
|
+
minWidth: 12,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
key: "visual",
|
|
82
|
+
header: rich ? colorize(rich, theme.heading, "") : "",
|
|
83
|
+
align: "left",
|
|
84
|
+
minWidth: 10,
|
|
85
|
+
maxWidth: 15,
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
const rows = entries.map(([name, value]) => {
|
|
89
|
+
const normalized = maxValue > 0 ? value / maxValue : 0;
|
|
90
|
+
const barLength = Math.max(1, Math.floor(normalized * 10));
|
|
91
|
+
const bar = "█".repeat(barLength);
|
|
92
|
+
return {
|
|
93
|
+
name: rich ? colorize(rich, theme.info, name) : name,
|
|
94
|
+
value: rich ? colorize(rich, theme.accent, formatNumber(value)) : formatNumber(value),
|
|
95
|
+
visual: rich ? colorize(rich, theme.success, bar) : bar,
|
|
96
|
+
};
|
|
97
|
+
});
|
|
98
|
+
const table = renderTable({
|
|
99
|
+
columns,
|
|
100
|
+
rows,
|
|
101
|
+
border: rich ? "unicode" : "ascii",
|
|
102
|
+
padding: 1,
|
|
103
|
+
});
|
|
104
|
+
runtime.log(table);
|
|
105
|
+
runtime.log("");
|
|
106
|
+
}
|
|
107
|
+
// Print histograms table
|
|
108
|
+
function printHistogramsTable(histograms, runtime = defaultRuntime) {
|
|
109
|
+
const rich = isRich();
|
|
110
|
+
runtime.log(rich ? colorize(rich, theme.heading, " Histograms (Durations)") : " Histograms (Durations)");
|
|
111
|
+
const entries = Object.entries(histograms).sort((a, b) => b[1].count - a[1].count);
|
|
112
|
+
if (entries.length === 0) {
|
|
113
|
+
runtime.log(rich
|
|
114
|
+
? colorize(rich, theme.muted, " No histogram metrics available.")
|
|
115
|
+
: " No histogram metrics available.");
|
|
116
|
+
runtime.log("");
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const columns = [
|
|
120
|
+
{
|
|
121
|
+
key: "name",
|
|
122
|
+
header: rich ? colorize(rich, theme.heading, "Name") : "Name",
|
|
123
|
+
align: "left",
|
|
124
|
+
minWidth: 25,
|
|
125
|
+
flex: true,
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
key: "count",
|
|
129
|
+
header: rich ? colorize(rich, theme.heading, "Count") : "Count",
|
|
130
|
+
align: "right",
|
|
131
|
+
minWidth: 8,
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
key: "avg",
|
|
135
|
+
header: rich ? colorize(rich, theme.heading, "Avg") : "Avg",
|
|
136
|
+
align: "right",
|
|
137
|
+
minWidth: 10,
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
key: "min",
|
|
141
|
+
header: rich ? colorize(rich, theme.heading, "Min") : "Min",
|
|
142
|
+
align: "right",
|
|
143
|
+
minWidth: 10,
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
key: "max",
|
|
147
|
+
header: rich ? colorize(rich, theme.heading, "Max") : "Max",
|
|
148
|
+
align: "right",
|
|
149
|
+
minWidth: 10,
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
key: "p95",
|
|
153
|
+
header: rich ? colorize(rich, theme.heading, "P95*") : "P95*",
|
|
154
|
+
align: "right",
|
|
155
|
+
minWidth: 10,
|
|
156
|
+
},
|
|
157
|
+
];
|
|
158
|
+
const rows = entries.map(([name, stats]) => {
|
|
159
|
+
// Estimate P95 as max * 0.9 (rough approximation)
|
|
160
|
+
const p95 = stats.max * 0.9;
|
|
161
|
+
return {
|
|
162
|
+
name: rich ? colorize(rich, theme.info, name) : name,
|
|
163
|
+
count: rich
|
|
164
|
+
? colorize(rich, theme.accent, formatNumber(stats.count))
|
|
165
|
+
: formatNumber(stats.count),
|
|
166
|
+
avg: formatDuration(stats.avg),
|
|
167
|
+
min: formatDuration(stats.min),
|
|
168
|
+
max: formatDuration(stats.max),
|
|
169
|
+
p95: formatDuration(p95),
|
|
170
|
+
};
|
|
171
|
+
});
|
|
172
|
+
const table = renderTable({
|
|
173
|
+
columns,
|
|
174
|
+
rows,
|
|
175
|
+
border: rich ? "unicode" : "ascii",
|
|
176
|
+
padding: 1,
|
|
177
|
+
});
|
|
178
|
+
runtime.log(table);
|
|
179
|
+
if (rich) {
|
|
180
|
+
runtime.log(colorize(rich, theme.muted, " * P95 is estimated from max value"));
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
runtime.log(" * P95 is estimated from max value");
|
|
184
|
+
}
|
|
185
|
+
runtime.log("");
|
|
186
|
+
}
|
|
187
|
+
// Print gauges table
|
|
188
|
+
function printGaugesTable(gauges, runtime = defaultRuntime) {
|
|
189
|
+
const rich = isRich();
|
|
190
|
+
runtime.log(rich ? colorize(rich, theme.heading, " Gauges (Current Values)") : " Gauges (Current Values)");
|
|
191
|
+
const entries = Object.entries(gauges).sort((a, b) => b[1] - a[1]);
|
|
192
|
+
if (entries.length === 0) {
|
|
193
|
+
runtime.log(rich
|
|
194
|
+
? colorize(rich, theme.muted, " No gauge metrics available.")
|
|
195
|
+
: " No gauge metrics available.");
|
|
196
|
+
runtime.log("");
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const maxValue = Math.max(...entries.map(([, v]) => Math.abs(v)), 1);
|
|
200
|
+
const columns = [
|
|
201
|
+
{
|
|
202
|
+
key: "name",
|
|
203
|
+
header: rich ? colorize(rich, theme.heading, "Name") : "Name",
|
|
204
|
+
align: "left",
|
|
205
|
+
minWidth: 30,
|
|
206
|
+
flex: true,
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
key: "value",
|
|
210
|
+
header: rich ? colorize(rich, theme.heading, "Value") : "Value",
|
|
211
|
+
align: "right",
|
|
212
|
+
minWidth: 12,
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
key: "indicator",
|
|
216
|
+
header: rich ? colorize(rich, theme.heading, "") : "",
|
|
217
|
+
align: "center",
|
|
218
|
+
minWidth: 3,
|
|
219
|
+
},
|
|
220
|
+
];
|
|
221
|
+
const rows = entries.map(([name, value]) => {
|
|
222
|
+
const normalized = Math.abs(value) / maxValue;
|
|
223
|
+
const indicator = generateSparkline(normalized, rich);
|
|
224
|
+
let coloredValue;
|
|
225
|
+
if (rich) {
|
|
226
|
+
if (value > 0) {
|
|
227
|
+
coloredValue = colorize(rich, theme.success, formatNumber(value));
|
|
228
|
+
}
|
|
229
|
+
else if (value < 0) {
|
|
230
|
+
coloredValue = colorize(rich, theme.error, formatNumber(value));
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
coloredValue = colorize(rich, theme.muted, formatNumber(value));
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
coloredValue = formatNumber(value);
|
|
238
|
+
}
|
|
239
|
+
return {
|
|
240
|
+
name: rich ? colorize(rich, theme.info, name) : name,
|
|
241
|
+
value: coloredValue,
|
|
242
|
+
indicator,
|
|
243
|
+
};
|
|
244
|
+
});
|
|
245
|
+
const table = renderTable({
|
|
246
|
+
columns,
|
|
247
|
+
rows,
|
|
248
|
+
border: rich ? "unicode" : "ascii",
|
|
249
|
+
padding: 1,
|
|
250
|
+
});
|
|
251
|
+
runtime.log(table);
|
|
252
|
+
runtime.log("");
|
|
253
|
+
}
|
|
254
|
+
// Print summary stats
|
|
255
|
+
function printSummaryStats(counters, histograms, gauges, runtime = defaultRuntime) {
|
|
256
|
+
const rich = isRich();
|
|
257
|
+
const totalCounterValue = Object.values(counters).reduce((a, b) => a + b, 0);
|
|
258
|
+
const totalHistogramCount = Object.values(histograms).reduce((a, b) => a + b.count, 0);
|
|
259
|
+
const avgGaugeValue = Object.values(gauges).length > 0
|
|
260
|
+
? Object.values(gauges).reduce((a, b) => a + b, 0) / Object.values(gauges).length
|
|
261
|
+
: 0;
|
|
262
|
+
if (rich) {
|
|
263
|
+
const stats = [
|
|
264
|
+
`${colorize(rich, theme.info, "Counters:")} ${Object.keys(counters).length} (${formatNumber(totalCounterValue)} total)`,
|
|
265
|
+
`${colorize(rich, theme.accent, "Histograms:")} ${Object.keys(histograms).length} (${formatNumber(totalHistogramCount)} samples)`,
|
|
266
|
+
`${colorize(rich, theme.success, "Gauges:")} ${Object.keys(gauges).length} (avg: ${avgGaugeValue.toFixed(2)})`,
|
|
267
|
+
].join(" │ ");
|
|
268
|
+
runtime.log(` ${stats}`);
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
runtime.log(` Counters: ${Object.keys(counters).length} (${formatNumber(totalCounterValue)} total) | Histograms: ${Object.keys(histograms).length} (${formatNumber(totalHistogramCount)} samples) | Gauges: ${Object.keys(gauges).length} (avg: ${avgGaugeValue.toFixed(2)})`);
|
|
272
|
+
}
|
|
273
|
+
runtime.log("");
|
|
274
|
+
}
|
|
275
|
+
// Print footer
|
|
276
|
+
function printFooter(runtime = defaultRuntime) {
|
|
277
|
+
const rich = isRich();
|
|
278
|
+
runtime.log("");
|
|
279
|
+
if (rich) {
|
|
280
|
+
runtime.log(colorize(rich, theme.muted, " Commands: poolbot telemetry status | poolbot telemetry export"));
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
runtime.log(" Commands: poolbot telemetry status | poolbot telemetry export");
|
|
284
|
+
}
|
|
285
|
+
runtime.log("");
|
|
286
|
+
}
|
|
287
|
+
export function registerTelemetryMetricsCommand(telemetry) {
|
|
288
|
+
addGatewayClientOptions(telemetry
|
|
289
|
+
.command("metrics")
|
|
290
|
+
.alias("m")
|
|
291
|
+
.description("Display telemetry metrics dashboard")
|
|
292
|
+
.option("--json", "Output JSON (disables visual dashboard)", false)
|
|
293
|
+
.option("--type <type>", "Filter by metric type (counter|histogram|gauge)", "all")
|
|
294
|
+
.action(async (opts) => {
|
|
295
|
+
try {
|
|
296
|
+
const res = await callGatewayFromCli("telemetry.metrics", opts, {
|
|
297
|
+
type: opts.type,
|
|
298
|
+
});
|
|
299
|
+
if (opts.json) {
|
|
300
|
+
defaultRuntime.log(JSON.stringify(res, null, 2));
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
const data = res;
|
|
304
|
+
printDashboardHeader();
|
|
305
|
+
printSummaryStats(data.counters, data.histograms, data.gauges);
|
|
306
|
+
const typeFilter = opts.type;
|
|
307
|
+
if (typeFilter === "all" || typeFilter === "counter") {
|
|
308
|
+
printCountersTable(data.counters);
|
|
309
|
+
}
|
|
310
|
+
if (typeFilter === "all" || typeFilter === "histogram") {
|
|
311
|
+
printHistogramsTable(data.histograms);
|
|
312
|
+
}
|
|
313
|
+
if (typeFilter === "all" || typeFilter === "gauge") {
|
|
314
|
+
printGaugesTable(data.gauges);
|
|
315
|
+
}
|
|
316
|
+
printFooter();
|
|
317
|
+
}
|
|
318
|
+
catch (err) {
|
|
319
|
+
defaultRuntime.error(danger(String(err)));
|
|
320
|
+
defaultRuntime.exit(1);
|
|
321
|
+
}
|
|
322
|
+
}));
|
|
323
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { danger } from "../../globals.js";
|
|
2
|
+
import { defaultRuntime } from "../../runtime.js";
|
|
3
|
+
import { colorize, isRich, theme } from "../../terminal/theme.js";
|
|
4
|
+
import { addGatewayClientOptions, callGatewayFromCli } from "../gateway-rpc.js";
|
|
5
|
+
// Status icons
|
|
6
|
+
const STATUS_ICONS = {
|
|
7
|
+
enabled: "●",
|
|
8
|
+
disabled: "○",
|
|
9
|
+
connected: "✓",
|
|
10
|
+
disconnected: "✗",
|
|
11
|
+
warning: "⚠",
|
|
12
|
+
};
|
|
13
|
+
// Print telemetry status header
|
|
14
|
+
function printStatusHeader(runtime = defaultRuntime) {
|
|
15
|
+
const rich = isRich();
|
|
16
|
+
const now = new Date();
|
|
17
|
+
const timestamp = now.toLocaleString();
|
|
18
|
+
runtime.log("");
|
|
19
|
+
if (rich) {
|
|
20
|
+
runtime.log(colorize(rich, theme.heading, "╔═══════════════════════════════════════════════════════════╗"));
|
|
21
|
+
runtime.log(colorize(rich, theme.heading, `║ 📊 TELEMETRY STATUS ${timestamp.padStart(26)} ║`));
|
|
22
|
+
runtime.log(colorize(rich, theme.heading, "╚═══════════════════════════════════════════════════════════╝"));
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
runtime.log("═══════════════════════════════════════════════════════════");
|
|
26
|
+
runtime.log(`TELEMETRY STATUS - ${timestamp}`);
|
|
27
|
+
runtime.log("═══════════════════════════════════════════════════════════");
|
|
28
|
+
}
|
|
29
|
+
runtime.log("");
|
|
30
|
+
}
|
|
31
|
+
// Format exporter type with icon
|
|
32
|
+
function formatExporterType(type, rich) {
|
|
33
|
+
if (!rich)
|
|
34
|
+
return type;
|
|
35
|
+
switch (type) {
|
|
36
|
+
case "console":
|
|
37
|
+
return `${colorize(rich, theme.info, "🖥️")} console`;
|
|
38
|
+
case "memory":
|
|
39
|
+
return `${colorize(rich, theme.accent, "💾")} memory`;
|
|
40
|
+
case "otlp":
|
|
41
|
+
return `${colorize(rich, theme.success, "🌐")} otlp`;
|
|
42
|
+
case "none":
|
|
43
|
+
return `${colorize(rich, theme.muted, "⛔")} none`;
|
|
44
|
+
default:
|
|
45
|
+
return type;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Format enabled status
|
|
49
|
+
function formatEnabled(enabled, rich) {
|
|
50
|
+
if (!rich)
|
|
51
|
+
return enabled ? "enabled" : "disabled";
|
|
52
|
+
if (enabled) {
|
|
53
|
+
return `${colorize(rich, theme.success, STATUS_ICONS.enabled)} enabled`;
|
|
54
|
+
}
|
|
55
|
+
return `${colorize(rich, theme.muted, STATUS_ICONS.disabled)} disabled`;
|
|
56
|
+
}
|
|
57
|
+
// Print configuration section
|
|
58
|
+
function printConfiguration(config, runtime = defaultRuntime) {
|
|
59
|
+
const rich = isRich();
|
|
60
|
+
runtime.log(rich ? colorize(rich, theme.heading, " Configuration") : " Configuration");
|
|
61
|
+
runtime.log(rich
|
|
62
|
+
? colorize(rich, theme.muted, " ─────────────────────────────────────────────────────────")
|
|
63
|
+
: " ─────────────────────────────────────────────────────────");
|
|
64
|
+
const rows = [
|
|
65
|
+
{ label: "Enabled", value: formatEnabled(config.enabled, rich) },
|
|
66
|
+
{ label: "Service Name", value: config.serviceName },
|
|
67
|
+
{ label: "Service Version", value: config.serviceVersion },
|
|
68
|
+
];
|
|
69
|
+
// Tracing config
|
|
70
|
+
rows.push({ label: "Tracing", value: formatEnabled(config.tracing.enabled, rich) }, { label: " Exporter", value: formatExporterType(config.tracing.exporter, rich) }, { label: " Sample Rate", value: `${(config.tracing.sampleRate * 100).toFixed(0)}%` });
|
|
71
|
+
if (config.tracing.exporter === "otlp" && config.tracing.otlpEndpoint) {
|
|
72
|
+
rows.push({ label: " OTLP Endpoint", value: config.tracing.otlpEndpoint });
|
|
73
|
+
}
|
|
74
|
+
// Metrics config
|
|
75
|
+
rows.push({ label: "Metrics", value: formatEnabled(config.metrics.enabled, rich) }, { label: " Exporter", value: formatExporterType(config.metrics.exporter, rich) }, { label: " Export Interval", value: `${config.metrics.exportIntervalMs}ms` });
|
|
76
|
+
if (config.metrics.exporter === "otlp" && config.metrics.otlpEndpoint) {
|
|
77
|
+
rows.push({ label: " OTLP Endpoint", value: config.metrics.otlpEndpoint });
|
|
78
|
+
}
|
|
79
|
+
const maxLabelWidth = Math.max(...rows.map((r) => r.label.length));
|
|
80
|
+
for (const row of rows) {
|
|
81
|
+
const paddedLabel = row.label.padStart(maxLabelWidth);
|
|
82
|
+
if (rich) {
|
|
83
|
+
runtime.log(` ${colorize(rich, theme.muted, paddedLabel)}: ${row.value}`);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
runtime.log(` ${paddedLabel}: ${row.value}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
runtime.log("");
|
|
90
|
+
}
|
|
91
|
+
// Print metrics summary
|
|
92
|
+
function printMetricsSummary(metrics, runtime = defaultRuntime) {
|
|
93
|
+
const rich = isRich();
|
|
94
|
+
runtime.log(rich ? colorize(rich, theme.heading, " Metrics Summary") : " Metrics Summary");
|
|
95
|
+
runtime.log(rich
|
|
96
|
+
? colorize(rich, theme.muted, " ─────────────────────────────────────────────────────────")
|
|
97
|
+
: " ─────────────────────────────────────────────────────────");
|
|
98
|
+
const counterCount = Object.keys(metrics.counters).length;
|
|
99
|
+
const histogramCount = Object.keys(metrics.histograms).length;
|
|
100
|
+
const gaugeCount = Object.keys(metrics.gauges).length;
|
|
101
|
+
if (rich) {
|
|
102
|
+
runtime.log(` ${colorize(rich, theme.info, "Counters:")} ${counterCount}`);
|
|
103
|
+
runtime.log(` ${colorize(rich, theme.accent, "Histograms:")} ${histogramCount}`);
|
|
104
|
+
runtime.log(` ${colorize(rich, theme.success, "Gauges:")} ${gaugeCount}`);
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
runtime.log(` Counters: ${counterCount}`);
|
|
108
|
+
runtime.log(` Histograms: ${histogramCount}`);
|
|
109
|
+
runtime.log(` Gauges: ${gaugeCount}`);
|
|
110
|
+
}
|
|
111
|
+
runtime.log("");
|
|
112
|
+
}
|
|
113
|
+
// Print connection status
|
|
114
|
+
function printConnectionStatus(status, runtime = defaultRuntime) {
|
|
115
|
+
const rich = isRich();
|
|
116
|
+
runtime.log(rich ? colorize(rich, theme.heading, " Connection Status") : " Connection Status");
|
|
117
|
+
runtime.log(rich
|
|
118
|
+
? colorize(rich, theme.muted, " ─────────────────────────────────────────────────────────")
|
|
119
|
+
: " ─────────────────────────────────────────────────────────");
|
|
120
|
+
const connectionStatus = status.isConnected
|
|
121
|
+
? rich
|
|
122
|
+
? `${colorize(rich, theme.success, STATUS_ICONS.connected)} Connected`
|
|
123
|
+
: "Connected"
|
|
124
|
+
: rich
|
|
125
|
+
? `${colorize(rich, theme.error, STATUS_ICONS.disconnected)} Disconnected`
|
|
126
|
+
: "Disconnected";
|
|
127
|
+
runtime.log(` Status: ${connectionStatus}`);
|
|
128
|
+
if (status.lastExportAt) {
|
|
129
|
+
runtime.log(` Last Export: ${status.lastExportAt}`);
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
runtime.log(` Last Export: ${rich ? colorize(rich, theme.muted, "never") : "never"}`);
|
|
133
|
+
}
|
|
134
|
+
if (status.exportErrors > 0) {
|
|
135
|
+
runtime.log(` Errors: ${rich ? colorize(rich, theme.error, String(status.exportErrors)) : status.exportErrors}`);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
runtime.log(` Errors: ${rich ? colorize(rich, theme.success, "0") : "0"}`);
|
|
139
|
+
}
|
|
140
|
+
runtime.log("");
|
|
141
|
+
}
|
|
142
|
+
// Print footer with tips
|
|
143
|
+
function printFooter(runtime = defaultRuntime) {
|
|
144
|
+
const rich = isRich();
|
|
145
|
+
runtime.log("");
|
|
146
|
+
if (rich) {
|
|
147
|
+
runtime.log(colorize(rich, theme.muted, " Commands: poolbot telemetry metrics | poolbot config set telemetry.enabled true"));
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
runtime.log(" Commands: poolbot telemetry metrics | poolbot config set telemetry.enabled true");
|
|
151
|
+
}
|
|
152
|
+
runtime.log("");
|
|
153
|
+
}
|
|
154
|
+
export function registerTelemetryStatusCommand(telemetry) {
|
|
155
|
+
addGatewayClientOptions(telemetry
|
|
156
|
+
.command("status")
|
|
157
|
+
.alias("info")
|
|
158
|
+
.description("Show telemetry configuration and status")
|
|
159
|
+
.option("--json", "Output JSON (disables visual formatting)", false)
|
|
160
|
+
.action(async (opts) => {
|
|
161
|
+
try {
|
|
162
|
+
const res = await callGatewayFromCli("telemetry.status", opts, {});
|
|
163
|
+
if (opts.json) {
|
|
164
|
+
defaultRuntime.log(JSON.stringify(res, null, 2));
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const data = res;
|
|
168
|
+
printStatusHeader();
|
|
169
|
+
printConfiguration(data.config);
|
|
170
|
+
printMetricsSummary(data.metrics);
|
|
171
|
+
printConnectionStatus(data.status);
|
|
172
|
+
printFooter();
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
defaultRuntime.error(danger(String(err)));
|
|
176
|
+
defaultRuntime.exit(1);
|
|
177
|
+
}
|
|
178
|
+
}));
|
|
179
|
+
}
|