@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.
Files changed (125) hide show
  1. package/CHANGELOG.md +24 -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 +5 -0
  97. package/extensions/matrix/package.json +1 -1
  98. package/extensions/mattermost/package.json +1 -1
  99. package/extensions/mcp-server/index.ts +14 -0
  100. package/extensions/mcp-server/package.json +11 -0
  101. package/extensions/mcp-server/src/service.ts +540 -0
  102. package/extensions/memory-core/package.json +1 -1
  103. package/extensions/memory-lancedb/package.json +1 -1
  104. package/extensions/minimax-portal-auth/package.json +1 -1
  105. package/extensions/msteams/CHANGELOG.md +5 -0
  106. package/extensions/msteams/package.json +1 -1
  107. package/extensions/nextcloud-talk/package.json +1 -1
  108. package/extensions/nostr/CHANGELOG.md +5 -0
  109. package/extensions/nostr/package.json +1 -1
  110. package/extensions/open-prose/package.json +1 -1
  111. package/extensions/openai-codex-auth/package.json +1 -1
  112. package/extensions/signal/package.json +1 -1
  113. package/extensions/slack/package.json +1 -1
  114. package/extensions/telegram/package.json +1 -1
  115. package/extensions/tlon/package.json +1 -1
  116. package/extensions/twitch/CHANGELOG.md +5 -0
  117. package/extensions/twitch/package.json +1 -1
  118. package/extensions/voice-call/CHANGELOG.md +5 -0
  119. package/extensions/voice-call/package.json +1 -1
  120. package/extensions/whatsapp/package.json +1 -1
  121. package/extensions/zalo/CHANGELOG.md +5 -0
  122. package/extensions/zalo/package.json +1 -1
  123. package/extensions/zalouser/CHANGELOG.md +5 -0
  124. package/extensions/zalouser/package.json +1 -1
  125. package/package.json +8 -1
@@ -0,0 +1,488 @@
1
+ import { danger } from "../../globals.js";
2
+ import { defaultRuntime } from "../../runtime.js";
3
+ import { colorize, isRich, theme } from "../../terminal/theme.js";
4
+ import { renderTable } from "../../terminal/table.js";
5
+ import { addGatewayClientOptions, callGatewayFromCli } from "../gateway-rpc.js";
6
+ // Status icons
7
+ const STATUS_ICONS = {
8
+ idle: "◌",
9
+ working: "▶",
10
+ error: "✖",
11
+ offline: "⊘",
12
+ pending: "○",
13
+ assigned: "◎",
14
+ in_progress: "◐",
15
+ completed: "●",
16
+ failed: "✖",
17
+ active: "●",
18
+ paused: "⏸",
19
+ };
20
+ // Format member status with icon
21
+ function formatMemberStatus(status, rich) {
22
+ const icon = STATUS_ICONS[status] ?? "?";
23
+ if (!rich)
24
+ return status;
25
+ switch (status) {
26
+ case "idle":
27
+ return `${colorize(rich, theme.success, icon)} ${status}`;
28
+ case "working":
29
+ return `${colorize(rich, theme.warn, icon)} ${status}`;
30
+ case "error":
31
+ return `${colorize(rich, theme.error, icon)} ${status}`;
32
+ case "offline":
33
+ return `${colorize(rich, theme.muted, icon)} ${status}`;
34
+ default:
35
+ return status;
36
+ }
37
+ }
38
+ // Format task status with icon
39
+ function formatTaskStatus(status, rich) {
40
+ const icon = STATUS_ICONS[status] ?? "?";
41
+ if (!rich)
42
+ return status;
43
+ switch (status) {
44
+ case "completed":
45
+ return `${colorize(rich, theme.success, icon)} ${status}`;
46
+ case "in_progress":
47
+ return `${colorize(rich, theme.warn, icon)} ${status}`;
48
+ case "failed":
49
+ return `${colorize(rich, theme.error, icon)} ${status}`;
50
+ case "assigned":
51
+ return `${colorize(rich, theme.accent, icon)} ${status}`;
52
+ default:
53
+ return `${colorize(rich, theme.muted, icon)} ${status}`;
54
+ }
55
+ }
56
+ // Format swarm status
57
+ function formatSwarmStatus(status, rich) {
58
+ const icon = STATUS_ICONS[status] ?? "?";
59
+ if (!rich)
60
+ return status;
61
+ switch (status) {
62
+ case "active":
63
+ return `${colorize(rich, theme.success, icon)} ${status}`;
64
+ case "paused":
65
+ return `${colorize(rich, theme.warn, icon)} ${status}`;
66
+ case "completed":
67
+ return `${colorize(rich, theme.accentBright, icon)} ${status}`;
68
+ case "error":
69
+ return `${colorize(rich, theme.error, icon)} ${status}`;
70
+ default:
71
+ return status;
72
+ }
73
+ }
74
+ // Format relative time
75
+ function formatRelativeTime(ms) {
76
+ const now = Date.now();
77
+ const delta = now - ms;
78
+ if (delta < 60_000)
79
+ return "just now";
80
+ if (delta < 3_600_000)
81
+ return `${Math.round(delta / 60_000)}m ago`;
82
+ if (delta < 86_400_000)
83
+ return `${Math.round(delta / 3_600_000)}h ago`;
84
+ return `${Math.round(delta / 86_400_000)}d ago`;
85
+ }
86
+ // Print swarm header
87
+ function printSwarmHeader(swarm, runtime = defaultRuntime) {
88
+ const rich = isRich();
89
+ runtime.log("");
90
+ if (rich) {
91
+ runtime.log(colorize(rich, theme.heading, "╔═══════════════════════════════════════════════════════════╗"));
92
+ runtime.log(colorize(rich, theme.heading, `║ 🐝 SWARM: ${swarm.name.slice(0, 40).padEnd(40)} ║`));
93
+ runtime.log(colorize(rich, theme.heading, "╚═══════════════════════════════════════════════════════════╝"));
94
+ }
95
+ else {
96
+ runtime.log("═══════════════════════════════════════════════════════════");
97
+ runtime.log(`SWARM: ${swarm.name}`);
98
+ runtime.log("═══════════════════════════════════════════════════════════");
99
+ }
100
+ if (swarm.description) {
101
+ runtime.log(` ${swarm.description}`);
102
+ }
103
+ runtime.log("");
104
+ }
105
+ // Print swarm stats
106
+ function printSwarmStats(swarm, runtime = defaultRuntime) {
107
+ const rich = isRich();
108
+ const totalTasks = swarm.tasks.length;
109
+ const completedTasks = swarm.tasks.filter((t) => t.status === "completed").length;
110
+ const failedTasks = swarm.tasks.filter((t) => t.status === "failed").length;
111
+ const inProgressTasks = swarm.tasks.filter((t) => t.status === "in_progress").length;
112
+ const pendingTasks = swarm.tasks.filter((t) => t.status === "pending" || t.status === "assigned").length;
113
+ const activeMembers = swarm.members.filter((m) => m.status !== "offline").length;
114
+ const workingMembers = swarm.members.filter((m) => m.status === "working").length;
115
+ const completionRate = totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0;
116
+ if (rich) {
117
+ const stats = [
118
+ `${colorize(rich, theme.info, "Status:")} ${formatSwarmStatus(swarm.status, rich)}`,
119
+ `${colorize(rich, theme.info, "Strategy:")} ${swarm.strategy}`,
120
+ `${colorize(rich, theme.info, "Members:")} ${activeMembers}/${swarm.members.length} active`,
121
+ `${colorize(rich, theme.info, "Working:")} ${workingMembers}`,
122
+ ].join(" │ ");
123
+ runtime.log(` ${stats}`);
124
+ const taskStats = [
125
+ `${colorize(rich, theme.success, "Completed:")} ${completedTasks}`,
126
+ `${colorize(rich, theme.warn, "In Progress:")} ${inProgressTasks}`,
127
+ `${colorize(rich, theme.muted, "Pending:")} ${pendingTasks}`,
128
+ `${colorize(rich, theme.error, "Failed:")} ${failedTasks}`,
129
+ `${colorize(rich, theme.accent, "Completion:")} ${completionRate}%`,
130
+ ].join(" │ ");
131
+ runtime.log(` ${taskStats}`);
132
+ }
133
+ else {
134
+ runtime.log(` Status: ${swarm.status} | Strategy: ${swarm.strategy}`);
135
+ runtime.log(` Members: ${activeMembers}/${swarm.members.length} active | Working: ${workingMembers}`);
136
+ runtime.log(` Tasks: ${completedTasks} completed | ${inProgressTasks} in progress | ${pendingTasks} pending | ${failedTasks} failed`);
137
+ runtime.log(` Completion Rate: ${completionRate}%`);
138
+ }
139
+ runtime.log("");
140
+ }
141
+ // Print members table
142
+ function printMembersTable(members, runtime = defaultRuntime) {
143
+ const rich = isRich();
144
+ if (members.length === 0) {
145
+ runtime.log(rich ? colorize(rich, theme.muted, " No swarm members.") : " No swarm members.");
146
+ return;
147
+ }
148
+ const sortedMembers = [...members].sort((a, b) => {
149
+ // Sort by status (working first, then idle, then error/offline)
150
+ const statusOrder = { working: 0, idle: 1, error: 2, offline: 3 };
151
+ return statusOrder[a.status] - statusOrder[b.status];
152
+ });
153
+ const columns = [
154
+ {
155
+ key: "agent",
156
+ header: rich ? colorize(rich, theme.heading, "Agent") : "Agent",
157
+ align: "left",
158
+ minWidth: 15,
159
+ flex: true,
160
+ },
161
+ {
162
+ key: "status",
163
+ header: rich ? colorize(rich, theme.heading, "Status") : "Status",
164
+ align: "left",
165
+ minWidth: 12,
166
+ maxWidth: 14,
167
+ },
168
+ {
169
+ key: "task",
170
+ header: rich ? colorize(rich, theme.heading, "Current Task") : "Current Task",
171
+ align: "left",
172
+ minWidth: 20,
173
+ flex: true,
174
+ },
175
+ {
176
+ key: "capabilities",
177
+ header: rich ? colorize(rich, theme.heading, "Capabilities") : "Capabilities",
178
+ align: "left",
179
+ minWidth: 15,
180
+ maxWidth: 20,
181
+ },
182
+ {
183
+ key: "completed",
184
+ header: rich ? colorize(rich, theme.heading, "Done") : "Done",
185
+ align: "right",
186
+ minWidth: 6,
187
+ maxWidth: 8,
188
+ },
189
+ {
190
+ key: "heartbeat",
191
+ header: rich ? colorize(rich, theme.heading, "Last Seen") : "Last Seen",
192
+ align: "right",
193
+ minWidth: 10,
194
+ maxWidth: 12,
195
+ },
196
+ ];
197
+ const rows = sortedMembers.map((member) => {
198
+ const capabilities = member.capabilities.slice(0, 3).join(", ") + (member.capabilities.length > 3 ? "..." : "");
199
+ return {
200
+ agent: rich ? colorize(rich, theme.info, member.agentId) : member.agentId,
201
+ status: formatMemberStatus(member.status, rich),
202
+ task: member.currentTaskId
203
+ ? rich
204
+ ? colorize(rich, theme.accent, member.currentTaskId.slice(0, 25))
205
+ : member.currentTaskId.slice(0, 25)
206
+ : rich
207
+ ? colorize(rich, theme.muted, "-")
208
+ : "-",
209
+ capabilities: capabilities || (rich ? colorize(rich, theme.muted, "-") : "-"),
210
+ completed: String(member.completedTasks),
211
+ heartbeat: formatRelativeTime(member.lastHeartbeatAt),
212
+ };
213
+ });
214
+ const table = renderTable({
215
+ columns,
216
+ rows,
217
+ border: rich ? "unicode" : "ascii",
218
+ padding: 1,
219
+ });
220
+ runtime.log(rich ? colorize(rich, theme.heading, " Members:") : " Members:");
221
+ runtime.log(table);
222
+ }
223
+ // Print tasks table
224
+ function printTasksTable(tasks, runtime = defaultRuntime) {
225
+ const rich = isRich();
226
+ if (tasks.length === 0) {
227
+ runtime.log(rich ? colorize(rich, theme.muted, " No tasks.") : " No tasks.");
228
+ return;
229
+ }
230
+ // Sort: in_progress first, then assigned, pending, completed, failed
231
+ const statusOrder = {
232
+ in_progress: 0,
233
+ assigned: 1,
234
+ pending: 2,
235
+ completed: 3,
236
+ failed: 4,
237
+ };
238
+ const sortedTasks = [...tasks]
239
+ .sort((a, b) => {
240
+ if (statusOrder[a.status] !== statusOrder[b.status]) {
241
+ return statusOrder[a.status] - statusOrder[b.status];
242
+ }
243
+ return b.priority - a.priority;
244
+ })
245
+ .slice(0, 20); // Show top 20 tasks
246
+ const columns = [
247
+ {
248
+ key: "id",
249
+ header: rich ? colorize(rich, theme.heading, "Task") : "Task",
250
+ align: "left",
251
+ minWidth: 12,
252
+ maxWidth: 16,
253
+ },
254
+ {
255
+ key: "status",
256
+ header: rich ? colorize(rich, theme.heading, "Status") : "Status",
257
+ align: "left",
258
+ minWidth: 12,
259
+ maxWidth: 14,
260
+ },
261
+ {
262
+ key: "priority",
263
+ header: rich ? colorize(rich, theme.heading, "Prio") : "Prio",
264
+ align: "center",
265
+ minWidth: 6,
266
+ maxWidth: 8,
267
+ },
268
+ {
269
+ key: "assigned",
270
+ header: rich ? colorize(rich, theme.heading, "Assigned To") : "Assigned To",
271
+ align: "left",
272
+ minWidth: 12,
273
+ flex: true,
274
+ },
275
+ {
276
+ key: "description",
277
+ header: rich ? colorize(rich, theme.heading, "Description") : "Description",
278
+ align: "left",
279
+ minWidth: 25,
280
+ flex: true,
281
+ },
282
+ ];
283
+ const rows = sortedTasks.map((task) => {
284
+ const prioColor = task.priority >= 8 ? theme.error : task.priority >= 5 ? theme.warn : theme.muted;
285
+ return {
286
+ id: rich ? colorize(rich, theme.accent, task.id.slice(0, 12)) : task.id.slice(0, 12),
287
+ status: formatTaskStatus(task.status, rich),
288
+ priority: rich ? colorize(rich, prioColor, String(task.priority)) : String(task.priority),
289
+ assigned: task.assignedTo
290
+ ? rich
291
+ ? colorize(rich, theme.info, task.assignedTo)
292
+ : task.assignedTo
293
+ : rich
294
+ ? colorize(rich, theme.muted, "-")
295
+ : "-",
296
+ description: task.description.slice(0, 40) + (task.description.length > 40 ? "..." : ""),
297
+ };
298
+ });
299
+ const table = renderTable({
300
+ columns,
301
+ rows,
302
+ border: rich ? "unicode" : "ascii",
303
+ padding: 1,
304
+ });
305
+ runtime.log(rich ? colorize(rich, theme.heading, " Tasks:") : " Tasks:");
306
+ if (tasks.length > 20) {
307
+ runtime.log(rich
308
+ ? colorize(rich, theme.muted, ` (showing top 20 of ${tasks.length} tasks)`)
309
+ : ` (showing top 20 of ${tasks.length} tasks)`);
310
+ }
311
+ runtime.log(table);
312
+ }
313
+ // Print legend
314
+ function printLegend(runtime = defaultRuntime) {
315
+ const rich = isRich();
316
+ runtime.log("");
317
+ if (rich) {
318
+ runtime.log(colorize(rich, theme.muted, " Legend:"));
319
+ runtime.log(` Members: ${colorize(rich, theme.success, STATUS_ICONS.idle)} idle ${colorize(rich, theme.warn, STATUS_ICONS.working)} working ${colorize(rich, theme.error, STATUS_ICONS.error)} error ${colorize(rich, theme.muted, STATUS_ICONS.offline)} offline`);
320
+ runtime.log(` Tasks: ${colorize(rich, theme.muted, STATUS_ICONS.pending)} pending ${colorize(rich, theme.accent, STATUS_ICONS.assigned)} assigned ${colorize(rich, theme.warn, STATUS_ICONS.in_progress)} in progress ${colorize(rich, theme.success, STATUS_ICONS.completed)} completed ${colorize(rich, theme.error, STATUS_ICONS.failed)} failed`);
321
+ }
322
+ else {
323
+ runtime.log(" Legend:");
324
+ runtime.log(" Members: ◌ idle ▶ working ✖ error ⊘ offline");
325
+ runtime.log(" Tasks: ○ pending ◎ assigned ◐ in progress ● completed ✖ failed");
326
+ }
327
+ }
328
+ // Print footer
329
+ function printFooter(runtime = defaultRuntime) {
330
+ const rich = isRich();
331
+ runtime.log("");
332
+ if (rich) {
333
+ runtime.log(colorize(rich, theme.muted, " Commands: poolbot swarm list | poolbot swarm create --help | poolbot swarm task add"));
334
+ }
335
+ else {
336
+ runtime.log(" Commands: poolbot swarm list | poolbot swarm create --help | poolbot swarm task add");
337
+ }
338
+ runtime.log("");
339
+ }
340
+ export function registerSwarmStatusCommand(swarm) {
341
+ addGatewayClientOptions(swarm
342
+ .command("status")
343
+ .description("Show swarm status dashboard")
344
+ .option("--id <swarmId>", "Swarm ID", "default")
345
+ .option("--json", "Output JSON", false)
346
+ .action(async (opts) => {
347
+ try {
348
+ const res = await callGatewayFromCli("swarm.status", opts, { swarmId: opts.id });
349
+ const swarmData = res.swarm;
350
+ if (!swarmData) {
351
+ defaultRuntime.error(danger("Swarm not found"));
352
+ defaultRuntime.exit(1);
353
+ }
354
+ if (opts.json) {
355
+ defaultRuntime.log(JSON.stringify(swarmData, null, 2));
356
+ return;
357
+ }
358
+ printSwarmHeader(swarmData);
359
+ printSwarmStats(swarmData);
360
+ printMembersTable(swarmData.members);
361
+ defaultRuntime.log("");
362
+ printTasksTable(swarmData.tasks);
363
+ printLegend();
364
+ printFooter();
365
+ }
366
+ catch (err) {
367
+ defaultRuntime.error(danger(String(err)));
368
+ defaultRuntime.exit(1);
369
+ }
370
+ }));
371
+ }
372
+ export function registerSwarmListCommand(swarm) {
373
+ addGatewayClientOptions(swarm
374
+ .command("list")
375
+ .alias("ls")
376
+ .description("List all swarms")
377
+ .option("--json", "Output JSON", false)
378
+ .action(async (opts) => {
379
+ try {
380
+ const res = await callGatewayFromCli("swarm.list", opts, {});
381
+ const swarms = res.swarms;
382
+ if (opts.json) {
383
+ defaultRuntime.log(JSON.stringify(swarms, null, 2));
384
+ return;
385
+ }
386
+ const rich = isRich();
387
+ const columns = [
388
+ {
389
+ key: "id",
390
+ header: rich ? colorize(rich, theme.heading, "ID") : "ID",
391
+ align: "left",
392
+ minWidth: 12,
393
+ maxWidth: 14,
394
+ },
395
+ {
396
+ key: "name",
397
+ header: rich ? colorize(rich, theme.heading, "Name") : "Name",
398
+ align: "left",
399
+ minWidth: 20,
400
+ flex: true,
401
+ },
402
+ {
403
+ key: "status",
404
+ header: rich ? colorize(rich, theme.heading, "Status") : "Status",
405
+ align: "left",
406
+ minWidth: 10,
407
+ maxWidth: 12,
408
+ },
409
+ {
410
+ key: "members",
411
+ header: rich ? colorize(rich, theme.heading, "Members") : "Members",
412
+ align: "center",
413
+ minWidth: 8,
414
+ maxWidth: 10,
415
+ },
416
+ {
417
+ key: "tasks",
418
+ header: rich ? colorize(rich, theme.heading, "Tasks") : "Tasks",
419
+ align: "center",
420
+ minWidth: 8,
421
+ maxWidth: 10,
422
+ },
423
+ ];
424
+ const rows = swarms.map((s) => ({
425
+ id: rich ? colorize(rich, theme.accent, s.id) : s.id,
426
+ name: rich ? colorize(rich, theme.info, s.name) : s.name,
427
+ status: formatSwarmStatus(s.status, rich),
428
+ members: String(s.members),
429
+ tasks: String(s.tasks),
430
+ }));
431
+ const table = renderTable({
432
+ columns,
433
+ rows,
434
+ border: rich ? "unicode" : "ascii",
435
+ padding: 1,
436
+ });
437
+ defaultRuntime.log("");
438
+ defaultRuntime.log(rich ? colorize(rich, theme.heading, "Active Swarms:") : "Active Swarms:");
439
+ defaultRuntime.log(table);
440
+ defaultRuntime.log("");
441
+ }
442
+ catch (err) {
443
+ defaultRuntime.error(danger(String(err)));
444
+ defaultRuntime.exit(1);
445
+ }
446
+ }));
447
+ }
448
+ export function registerSwarmCreateCommand(swarm) {
449
+ addGatewayClientOptions(swarm
450
+ .command("create")
451
+ .description("Create a new agent swarm")
452
+ .requiredOption("--name <name>", "Swarm name")
453
+ .option("--description <text>", "Swarm description")
454
+ .option("--strategy <strategy>", "Task distribution strategy (round_robin|least_busy|capability_match|priority_queue)", "capability_match")
455
+ .option("--orchestrator <agentId>", "Orchestrator agent ID", "main")
456
+ .option("--json", "Output JSON", false)
457
+ .action(async (opts) => {
458
+ try {
459
+ const result = (await callGatewayFromCli("swarm.create", opts, {
460
+ name: opts.name,
461
+ description: opts.description,
462
+ strategy: opts.strategy,
463
+ orchestrator: opts.orchestrator,
464
+ }));
465
+ if (opts.json) {
466
+ defaultRuntime.log(JSON.stringify(result, null, 2));
467
+ return;
468
+ }
469
+ const rich = isRich();
470
+ defaultRuntime.log("");
471
+ defaultRuntime.log(rich
472
+ ? colorize(rich, theme.success, `✓ Swarm "${result.name}" created successfully`)
473
+ : `✓ Swarm "${result.name}" created successfully`);
474
+ defaultRuntime.log(` ID: ${result.id}`);
475
+ defaultRuntime.log(` Strategy: ${result.strategy}`);
476
+ defaultRuntime.log(` Orchestrator: ${result.orchestratorAgentId}`);
477
+ defaultRuntime.log("");
478
+ defaultRuntime.log(rich
479
+ ? colorize(rich, theme.muted, " Use 'poolbot swarm join' to add agents to the swarm.")
480
+ : " Use 'poolbot swarm join' to add agents to the swarm.");
481
+ defaultRuntime.log("");
482
+ }
483
+ catch (err) {
484
+ defaultRuntime.error(danger(String(err)));
485
+ defaultRuntime.exit(1);
486
+ }
487
+ }));
488
+ }
@@ -0,0 +1,10 @@
1
+ import { registerTelemetryStatusCommand } from "./register.telemetry-status.js";
2
+ import { registerTelemetryMetricsCommand } from "./register.telemetry-metrics.js";
3
+ export function registerTelemetryCli(program) {
4
+ const telemetry = program
5
+ .command("telemetry")
6
+ .alias("tel")
7
+ .description("OpenTelemetry observability and metrics");
8
+ registerTelemetryStatusCommand(telemetry);
9
+ registerTelemetryMetricsCommand(telemetry);
10
+ }
@@ -0,0 +1,176 @@
1
+ import { defaultRuntime } from "../../runtime.js";
2
+ import { colorize, isRich, theme } from "../../terminal/theme.js";
3
+ import { addGatewayClientOptions, callGatewayFromCli } from "../gateway-rpc.js";
4
+ import { renderTable } from "../../terminal/table.js";
5
+ // Print alerts header
6
+ function printAlertsHeader(runtime = defaultRuntime) {
7
+ const rich = isRich();
8
+ const now = new Date();
9
+ const timestamp = now.toLocaleString();
10
+ runtime.log("");
11
+ if (rich) {
12
+ runtime.log(colorize(rich, theme.heading, "╔═══════════════════════════════════════════════════════════╗"));
13
+ runtime.log(colorize(rich, theme.heading, `║ 🚨 TELEMETRY ALERTS ${timestamp.padStart(26)} ║`));
14
+ runtime.log(colorize(rich, theme.heading, "╚═══════════════════════════════════════════════════════════╝"));
15
+ }
16
+ else {
17
+ runtime.log("═══════════════════════════════════════════════════════════");
18
+ runtime.log(`TELEMETRY ALERTS - ${timestamp}`);
19
+ runtime.log("═══════════════════════════════════════════════════════════");
20
+ }
21
+ runtime.log("");
22
+ }
23
+ // Format severity with color
24
+ function formatSeverity(severity, rich) {
25
+ if (!rich)
26
+ return severity.toUpperCase();
27
+ switch (severity) {
28
+ case "critical":
29
+ return colorize(rich, theme.error, "CRITICAL");
30
+ case "warning":
31
+ return colorize(rich, theme.warn, "WARNING");
32
+ case "info":
33
+ return colorize(rich, theme.info, "INFO");
34
+ default:
35
+ return severity.toUpperCase();
36
+ }
37
+ }
38
+ // Format timestamp
39
+ function formatTimestamp(ts) {
40
+ return new Date(ts).toLocaleTimeString();
41
+ }
42
+ // Render alerts table
43
+ function renderAlertsTable(alerts, runtime = defaultRuntime) {
44
+ const rich = isRich();
45
+ if (alerts.length === 0) {
46
+ runtime.log(rich ? colorize(rich, theme.success, "✓ No active alerts") : "No active alerts");
47
+ runtime.log("");
48
+ return;
49
+ }
50
+ const columns = [
51
+ { key: "severity", header: "Severity", align: "center", minWidth: 10 },
52
+ { key: "time", header: "Time", align: "left", minWidth: 10 },
53
+ { key: "name", header: "Alert", align: "left", minWidth: 30 },
54
+ { key: "metric", header: "Metric", align: "left", minWidth: 25 },
55
+ { key: "threshold", header: "Threshold", align: "left", minWidth: 15 },
56
+ ];
57
+ const rows = alerts.map((alert) => ({
58
+ severity: formatSeverity(alert.severity, rich),
59
+ time: formatTimestamp(alert.timestamp),
60
+ name: alert.name,
61
+ metric: `${alert.metric} = ${alert.value}`,
62
+ threshold: `${alert.operator} ${alert.threshold}`,
63
+ }));
64
+ renderTable({ columns, rows });
65
+ runtime.log("");
66
+ }
67
+ // Render alert summary
68
+ function renderAlertSummary(alerts, runtime = defaultRuntime) {
69
+ const rich = isRich();
70
+ const critical = alerts.filter((a) => a.severity === "critical").length;
71
+ const warning = alerts.filter((a) => a.severity === "warning").length;
72
+ const info = alerts.filter((a) => a.severity === "info").length;
73
+ runtime.log("");
74
+ if (rich) {
75
+ runtime.log(colorize(rich, theme.heading, "Alert Summary:"));
76
+ runtime.log(` ${colorize(rich, theme.error, "●")} Critical: ${critical}`);
77
+ runtime.log(` ${colorize(rich, theme.warn, "●")} Warning: ${warning}`);
78
+ runtime.log(` ${colorize(rich, theme.info, "●")} Info: ${info}`);
79
+ }
80
+ else {
81
+ runtime.log("Alert Summary:");
82
+ runtime.log(` Critical: ${critical}`);
83
+ runtime.log(` Warning: ${warning}`);
84
+ runtime.log(` Info: ${info}`);
85
+ }
86
+ runtime.log("");
87
+ }
88
+ export function registerTelemetryAlertsCommand(parent) {
89
+ const cmd = parent
90
+ .command("alerts")
91
+ .description("Manage telemetry alerts")
92
+ .option("-w, --watch", "Watch for new alerts (live mode)")
93
+ .option("-t, --test", "Test alert evaluation")
94
+ .action(async (opts) => {
95
+ const runtime = defaultRuntime;
96
+ const rich = isRich();
97
+ if (opts.test) {
98
+ runtime.log("Testing alert evaluation...");
99
+ const res = await callGatewayFromCli("telemetry.alerts.test", opts, { allowNoAuth: true });
100
+ if (!res.ok) {
101
+ const msg = res.error && typeof res.error === "object" && "message" in res.error
102
+ ? String(res.error.message)
103
+ : "Unknown error";
104
+ runtime.error(`Failed to test alerts: ${msg}`);
105
+ process.exit(1);
106
+ }
107
+ const data = res.data;
108
+ printAlertsHeader();
109
+ if (data.triggered) {
110
+ renderAlertsTable(data.alerts);
111
+ renderAlertSummary(data.alerts);
112
+ }
113
+ else {
114
+ runtime.log(rich ? colorize(rich, theme.success, "✓ No alerts triggered") : "No alerts triggered");
115
+ runtime.log("");
116
+ }
117
+ return;
118
+ }
119
+ if (opts.watch) {
120
+ runtime.log("Watching for alerts... (Press Ctrl+C to exit)");
121
+ runtime.log("");
122
+ // Poll every 5 seconds
123
+ const pollInterval = setInterval(async () => {
124
+ const res = await callGatewayFromCli("telemetry.alerts.list", opts, {
125
+ allowNoAuth: true,
126
+ });
127
+ if (!res.ok) {
128
+ const msg = res.error && typeof res.error === "object" && "message" in res.error
129
+ ? String(res.error.message)
130
+ : "Unknown error";
131
+ runtime.error(`Failed to fetch alerts: ${msg}`);
132
+ return;
133
+ }
134
+ const data = res.data;
135
+ // Clear screen in rich mode
136
+ if (isRich()) {
137
+ process.stdout.write("\x1Bc");
138
+ }
139
+ printAlertsHeader();
140
+ renderAlertsTable(data.alerts);
141
+ renderAlertSummary(data.alerts);
142
+ }, 5000);
143
+ // Handle graceful shutdown
144
+ process.on("SIGINT", () => {
145
+ clearInterval(pollInterval);
146
+ runtime.log("\nStopped watching alerts.");
147
+ process.exit(0);
148
+ });
149
+ // Initial fetch
150
+ const res = await callGatewayFromCli("telemetry.alerts.list", opts, { allowNoAuth: true });
151
+ if (res.ok) {
152
+ const data = res.data;
153
+ printAlertsHeader();
154
+ renderAlertsTable(data.alerts);
155
+ renderAlertSummary(data.alerts);
156
+ }
157
+ // Keep process alive
158
+ await new Promise(() => { });
159
+ return;
160
+ }
161
+ // Single fetch
162
+ const res = await callGatewayFromCli("telemetry.alerts.list", opts, { allowNoAuth: true });
163
+ if (!res.ok) {
164
+ const msg = res.error && typeof res.error === "object" && "message" in res.error
165
+ ? String(res.error.message)
166
+ : "Unknown error";
167
+ runtime.error(`Failed to fetch alerts: ${msg}`);
168
+ process.exit(1);
169
+ }
170
+ const data = res.data;
171
+ printAlertsHeader();
172
+ renderAlertsTable(data.alerts);
173
+ renderAlertSummary(data.alerts);
174
+ });
175
+ addGatewayClientOptions(cmd);
176
+ }