@poolzin/pool-bot 2026.3.9 → 2026.3.11

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 (128) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/README.md +147 -69
  3. package/dist/.buildstamp +1 -1
  4. package/dist/agents/error-classifier.js +26 -77
  5. package/dist/agents/skills/security.js +1 -7
  6. package/dist/build-info.json +3 -3
  7. package/dist/cli/cron-cli/register.cron-dashboard.js +339 -0
  8. package/dist/cli/cron-cli/register.js +2 -0
  9. package/dist/cli/errors.js +187 -0
  10. package/dist/cli/program/command-registry.js +13 -0
  11. package/dist/cli/program/register.maintenance.js +21 -0
  12. package/dist/cli/program/register.subclis.js +9 -0
  13. package/dist/cli/swarm-cli/register.js +8 -0
  14. package/dist/cli/swarm-cli/register.swarm-status.js +488 -0
  15. package/dist/cli/telemetry-cli/register.js +10 -0
  16. package/dist/cli/telemetry-cli/register.telemetry-alerts.js +176 -0
  17. package/dist/cli/telemetry-cli/register.telemetry-metrics.js +323 -0
  18. package/dist/cli/telemetry-cli/register.telemetry-status.js +179 -0
  19. package/dist/commands/doctor-checks.js +498 -0
  20. package/dist/context-engine/index.js +1 -1
  21. package/dist/context-engine/legacy.js +1 -3
  22. package/dist/context-engine/summarizing.js +5 -8
  23. package/dist/cron/service/timer.js +18 -0
  24. package/dist/gateway/protocol/index.js +5 -2
  25. package/dist/gateway/protocol/schema/error-codes.js +1 -0
  26. package/dist/gateway/protocol/schema/swarm.js +80 -0
  27. package/dist/gateway/protocol/schema.js +1 -0
  28. package/dist/gateway/server-close.js +4 -0
  29. package/dist/gateway/server-constants.js +1 -0
  30. package/dist/gateway/server-cron.js +29 -0
  31. package/dist/gateway/server-maintenance.js +35 -2
  32. package/dist/gateway/server-methods/swarm.js +58 -0
  33. package/dist/gateway/server-methods/telemetry.js +71 -0
  34. package/dist/gateway/server-methods-list.js +8 -0
  35. package/dist/gateway/server-methods.js +9 -2
  36. package/dist/gateway/server.impl.js +33 -16
  37. package/dist/infra/abort-pattern.js +4 -4
  38. package/dist/infra/retry.js +3 -1
  39. package/dist/skills/commands.js +7 -25
  40. package/dist/skills/index.js +14 -17
  41. package/dist/skills/parser.js +12 -27
  42. package/dist/skills/registry.js +3 -6
  43. package/dist/skills/security.js +2 -8
  44. package/dist/swarm/service.js +247 -0
  45. package/dist/telemetry/alert-engine.js +258 -0
  46. package/dist/telemetry/cron-instrumentation.js +49 -0
  47. package/dist/telemetry/gateway-instrumentation.js +80 -0
  48. package/dist/telemetry/instrumentation.js +66 -0
  49. package/dist/telemetry/service.js +345 -0
  50. package/dist/tui/components/assistant-message.js +6 -2
  51. package/dist/tui/components/hyperlink-markdown.js +32 -0
  52. package/dist/tui/components/searchable-select-list.js +12 -1
  53. package/dist/tui/components/user-message.js +6 -2
  54. package/dist/tui/index.js +22 -6
  55. package/dist/tui/theme/theme-detection.js +226 -0
  56. package/dist/tui/tui-command-handlers.js +20 -0
  57. package/dist/tui/tui-formatters.js +4 -3
  58. package/dist/tui/utils/ctrl-c-handler.js +67 -0
  59. package/dist/tui/utils/osc8-hyperlinks.js +208 -0
  60. package/dist/tui/utils/safe-stop.js +180 -0
  61. package/dist/tui/utils/session-key-utils.js +81 -0
  62. package/dist/tui/utils/text-sanitization.js +284 -0
  63. package/dist/utils/lru-cache.js +116 -0
  64. package/dist/utils/performance.js +199 -0
  65. package/dist/utils/retry.js +240 -0
  66. package/docs/MELHORIAS_IMPLEMENTADAS.md +228 -0
  67. package/docs/MELHORIAS_PROFISSIONAIS.md +282 -0
  68. package/docs/PLANO_ACAO_TUI.md +357 -0
  69. package/docs/PROGRESSO_TUI.md +66 -0
  70. package/docs/RELATORIO_FINAL.md +217 -0
  71. package/docs/diagnostico-shell-completion.md +265 -0
  72. package/docs/features/advanced-memory.md +585 -0
  73. package/docs/features/discord-components-v2.md +277 -0
  74. package/docs/features/swarm.md +100 -0
  75. package/docs/features/telemetry.md +284 -0
  76. package/docs/integrations/INTEGRATION_PLAN.md +665 -345
  77. package/docs/models/provider-infrastructure.md +400 -0
  78. package/docs/security/exec-approvals.md +294 -0
  79. package/extensions/bluebubbles/package.json +1 -1
  80. package/extensions/copilot-proxy/package.json +1 -1
  81. package/extensions/diagnostics-otel/package.json +1 -1
  82. package/extensions/discord/package.json +1 -1
  83. package/extensions/feishu/package.json +1 -1
  84. package/extensions/google-antigravity-auth/package.json +1 -1
  85. package/extensions/google-gemini-cli-auth/package.json +1 -1
  86. package/extensions/googlechat/package.json +1 -1
  87. package/extensions/hexstrike-bridge/README.md +119 -0
  88. package/extensions/hexstrike-bridge/index.test.ts +247 -0
  89. package/extensions/hexstrike-bridge/index.ts +487 -0
  90. package/extensions/hexstrike-bridge/package.json +17 -0
  91. package/extensions/imessage/package.json +1 -1
  92. package/extensions/irc/package.json +1 -1
  93. package/extensions/line/package.json +1 -1
  94. package/extensions/llm-task/package.json +1 -1
  95. package/extensions/lobster/package.json +1 -1
  96. package/extensions/matrix/CHANGELOG.md +10 -0
  97. package/extensions/matrix/package.json +1 -1
  98. package/extensions/mattermost/package.json +1 -1
  99. package/extensions/mavalie/README.md +97 -0
  100. package/extensions/mavalie/package.json +15 -0
  101. package/extensions/mavalie/src/index.ts +62 -0
  102. package/extensions/mcp-server/index.ts +14 -0
  103. package/extensions/mcp-server/package.json +11 -0
  104. package/extensions/mcp-server/src/service.ts +540 -0
  105. package/extensions/memory-core/package.json +1 -1
  106. package/extensions/memory-lancedb/package.json +1 -1
  107. package/extensions/minimax-portal-auth/package.json +1 -1
  108. package/extensions/msteams/CHANGELOG.md +10 -0
  109. package/extensions/msteams/package.json +1 -1
  110. package/extensions/nextcloud-talk/package.json +1 -1
  111. package/extensions/nostr/CHANGELOG.md +10 -0
  112. package/extensions/nostr/package.json +1 -1
  113. package/extensions/open-prose/package.json +1 -1
  114. package/extensions/openai-codex-auth/package.json +1 -1
  115. package/extensions/signal/package.json +1 -1
  116. package/extensions/slack/package.json +1 -1
  117. package/extensions/telegram/package.json +1 -1
  118. package/extensions/tlon/package.json +1 -1
  119. package/extensions/twitch/CHANGELOG.md +10 -0
  120. package/extensions/twitch/package.json +1 -1
  121. package/extensions/voice-call/CHANGELOG.md +10 -0
  122. package/extensions/voice-call/package.json +1 -1
  123. package/extensions/whatsapp/package.json +1 -1
  124. package/extensions/zalo/CHANGELOG.md +10 -0
  125. package/extensions/zalo/package.json +1 -1
  126. package/extensions/zalouser/CHANGELOG.md +10 -0
  127. package/extensions/zalouser/package.json +1 -1
  128. 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
+ }