@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.
- package/CHANGELOG.md +35 -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 +10 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/mattermost/package.json +1 -1
- package/extensions/mavalie/README.md +97 -0
- package/extensions/mavalie/package.json +15 -0
- package/extensions/mavalie/src/index.ts +62 -0
- 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 +10 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +10 -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 +10 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +10 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +10 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +10 -0
- package/extensions/zalouser/package.json +1 -1
- 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
|
+
}
|