@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,62 @@
|
|
|
1
|
+
import { emptyPluginConfigSchema } from "poolbot/plugin-sdk";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Mavalie Plugin for PoolBot
|
|
5
|
+
*
|
|
6
|
+
* Template mínimo válido de plugin para PoolBot.
|
|
7
|
+
* Este plugin registra um provider customizado que pode ser expandido
|
|
8
|
+
* conforme necessidades específicas.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const mavaliePlugin = {
|
|
12
|
+
id: "mavalie",
|
|
13
|
+
name: "Mavalie",
|
|
14
|
+
description: "Custom Mavalie integration for PoolBot",
|
|
15
|
+
configSchema: emptyPluginConfigSchema(),
|
|
16
|
+
|
|
17
|
+
register(api) {
|
|
18
|
+
// Log de registro do plugin
|
|
19
|
+
api.runtime?.log?.("Mavalie plugin registered");
|
|
20
|
+
|
|
21
|
+
// TODO: Adicione aqui o registro de:
|
|
22
|
+
// - Providers (modelos customizados)
|
|
23
|
+
// - Canais (integrações de mensageria)
|
|
24
|
+
// - Ferramentas (tools para agentes)
|
|
25
|
+
// - Hooks (eventos do ciclo de vida)
|
|
26
|
+
|
|
27
|
+
// Exemplo de registro de provider (descomente e adapte):
|
|
28
|
+
/*
|
|
29
|
+
api.registerProvider({
|
|
30
|
+
id: "mavalie",
|
|
31
|
+
label: "Mavalie Provider",
|
|
32
|
+
docsPath: "/providers/models",
|
|
33
|
+
auth: [
|
|
34
|
+
{
|
|
35
|
+
id: "api-key",
|
|
36
|
+
label: "API Key",
|
|
37
|
+
kind: "apiKey",
|
|
38
|
+
run: async (ctx) => {
|
|
39
|
+
const key = await ctx.prompter.password({
|
|
40
|
+
message: "Enter your Mavalie API key:",
|
|
41
|
+
});
|
|
42
|
+
return {
|
|
43
|
+
profiles: [
|
|
44
|
+
{
|
|
45
|
+
profileId: "mavalie:default",
|
|
46
|
+
credential: {
|
|
47
|
+
type: "apiKey",
|
|
48
|
+
provider: "mavalie",
|
|
49
|
+
key: String(key),
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
});
|
|
58
|
+
*/
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export default mavaliePlugin;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PoolBotPluginApi } from "../../src/plugins/types.js";
|
|
2
|
+
|
|
3
|
+
import { createMcpServerService } from "./src/service.js";
|
|
4
|
+
|
|
5
|
+
const plugin = {
|
|
6
|
+
id: "mcp-server",
|
|
7
|
+
name: "MCP Server",
|
|
8
|
+
description: "MCP (Model Context Protocol) server - exposes PoolBot tools via MCP protocol",
|
|
9
|
+
register(api: PoolBotPluginApi) {
|
|
10
|
+
api.registerService(createMcpServerService());
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export default plugin;
|
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
|
|
2
|
+
import type { PoolBotPluginService } from "../../../src/plugins/types.js";
|
|
3
|
+
|
|
4
|
+
// MCP Protocol types
|
|
5
|
+
interface McpTool {
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
inputSchema: unknown;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface McpTextContent {
|
|
12
|
+
type: "text";
|
|
13
|
+
text: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface McpImageContent {
|
|
17
|
+
type: "image";
|
|
18
|
+
data: string;
|
|
19
|
+
mimeType: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type McpContent = McpTextContent | McpImageContent;
|
|
23
|
+
|
|
24
|
+
interface McpToolResult {
|
|
25
|
+
content: McpContent[];
|
|
26
|
+
isError?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface McpRequest {
|
|
30
|
+
jsonrpc: "2.0";
|
|
31
|
+
id: number | string;
|
|
32
|
+
method: string;
|
|
33
|
+
params?: unknown;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface McpResponse {
|
|
37
|
+
jsonrpc: "2.0";
|
|
38
|
+
id: number | string;
|
|
39
|
+
result?: unknown;
|
|
40
|
+
error?: {
|
|
41
|
+
code: number;
|
|
42
|
+
message: string;
|
|
43
|
+
data?: unknown;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Tool registry to store PoolBot tools mapped to MCP
|
|
48
|
+
class McpToolRegistry {
|
|
49
|
+
private tools = new Map<string, McpTool>();
|
|
50
|
+
private handlers = new Map<string, (args: unknown) => Promise<McpToolResult>>();
|
|
51
|
+
|
|
52
|
+
register(
|
|
53
|
+
name: string,
|
|
54
|
+
description: string,
|
|
55
|
+
inputSchema: unknown,
|
|
56
|
+
handler: (args: unknown) => Promise<McpToolResult>,
|
|
57
|
+
): void {
|
|
58
|
+
this.tools.set(name, { name, description, inputSchema });
|
|
59
|
+
this.handlers.set(name, handler);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
getTool(name: string): McpTool | undefined {
|
|
63
|
+
return this.tools.get(name);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
getAllTools(): McpTool[] {
|
|
67
|
+
return Array.from(this.tools.values());
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async execute(name: string, args: unknown): Promise<McpToolResult> {
|
|
71
|
+
const handler = this.handlers.get(name);
|
|
72
|
+
if (!handler) {
|
|
73
|
+
return {
|
|
74
|
+
content: [{ type: "text", text: `Tool not found: ${name}` }],
|
|
75
|
+
isError: true,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
return await handler(args);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
return {
|
|
82
|
+
content: [
|
|
83
|
+
{
|
|
84
|
+
type: "text",
|
|
85
|
+
text: `Error executing tool ${name}: ${error instanceof Error ? error.message : String(error)}`,
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
isError: true,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// MCP Server implementation
|
|
95
|
+
class McpServer {
|
|
96
|
+
private registry = new McpToolRegistry();
|
|
97
|
+
private server?: ReturnType<typeof createServer>;
|
|
98
|
+
private port: number;
|
|
99
|
+
|
|
100
|
+
constructor(port = 8765) {
|
|
101
|
+
this.port = port;
|
|
102
|
+
this.registerBuiltinTools();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private registerBuiltinTools(): void {
|
|
106
|
+
// Register basic PoolBot tools as MCP tools
|
|
107
|
+
this.registry.register(
|
|
108
|
+
"poolbot_read_file",
|
|
109
|
+
"Read a file from the filesystem",
|
|
110
|
+
{
|
|
111
|
+
type: "object",
|
|
112
|
+
properties: {
|
|
113
|
+
file_path: {
|
|
114
|
+
type: "string",
|
|
115
|
+
description: "Absolute path to the file to read",
|
|
116
|
+
},
|
|
117
|
+
offset: {
|
|
118
|
+
type: "number",
|
|
119
|
+
description: "Line number to start reading from (0-indexed)",
|
|
120
|
+
},
|
|
121
|
+
limit: {
|
|
122
|
+
type: "number",
|
|
123
|
+
description: "Maximum number of lines to read",
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
required: ["file_path"],
|
|
127
|
+
},
|
|
128
|
+
async (args) => {
|
|
129
|
+
const { file_path, offset, limit } = args as {
|
|
130
|
+
file_path: string;
|
|
131
|
+
offset?: number;
|
|
132
|
+
limit?: number;
|
|
133
|
+
};
|
|
134
|
+
try {
|
|
135
|
+
const fs = await import("node:fs/promises");
|
|
136
|
+
const content = await fs.readFile(file_path, "utf-8");
|
|
137
|
+
const lines = content.split("\n");
|
|
138
|
+
const start = offset ?? 0;
|
|
139
|
+
const end = limit ? start + limit : lines.length;
|
|
140
|
+
const selectedLines = lines.slice(start, end);
|
|
141
|
+
return {
|
|
142
|
+
content: [
|
|
143
|
+
{
|
|
144
|
+
type: "text",
|
|
145
|
+
text: selectedLines.join("\n"),
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
};
|
|
149
|
+
} catch (error) {
|
|
150
|
+
return {
|
|
151
|
+
content: [
|
|
152
|
+
{
|
|
153
|
+
type: "text",
|
|
154
|
+
text: `Error reading file: ${error instanceof Error ? error.message : String(error)}`,
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
isError: true,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
},
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
this.registry.register(
|
|
164
|
+
"poolbot_write_file",
|
|
165
|
+
"Write content to a file",
|
|
166
|
+
{
|
|
167
|
+
type: "object",
|
|
168
|
+
properties: {
|
|
169
|
+
file_path: {
|
|
170
|
+
type: "string",
|
|
171
|
+
description: "Absolute path to the file to write",
|
|
172
|
+
},
|
|
173
|
+
content: {
|
|
174
|
+
type: "string",
|
|
175
|
+
description: "Content to write to the file",
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
required: ["file_path", "content"],
|
|
179
|
+
},
|
|
180
|
+
async (args) => {
|
|
181
|
+
const { file_path, content } = args as { file_path: string; content: string };
|
|
182
|
+
try {
|
|
183
|
+
const fs = await import("node:fs/promises");
|
|
184
|
+
const path = await import("node:path");
|
|
185
|
+
await fs.mkdir(path.dirname(file_path), { recursive: true });
|
|
186
|
+
await fs.writeFile(file_path, content, "utf-8");
|
|
187
|
+
return {
|
|
188
|
+
content: [
|
|
189
|
+
{
|
|
190
|
+
type: "text",
|
|
191
|
+
text: `Successfully wrote to ${file_path}`,
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
};
|
|
195
|
+
} catch (error) {
|
|
196
|
+
return {
|
|
197
|
+
content: [
|
|
198
|
+
{
|
|
199
|
+
type: "text",
|
|
200
|
+
text: `Error writing file: ${error instanceof Error ? error.message : String(error)}`,
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
isError: true,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
this.registry.register(
|
|
210
|
+
"poolbot_execute_command",
|
|
211
|
+
"Execute a shell command",
|
|
212
|
+
{
|
|
213
|
+
type: "object",
|
|
214
|
+
properties: {
|
|
215
|
+
command: {
|
|
216
|
+
type: "string",
|
|
217
|
+
description: "Command to execute",
|
|
218
|
+
},
|
|
219
|
+
workdir: {
|
|
220
|
+
type: "string",
|
|
221
|
+
description: "Working directory for the command",
|
|
222
|
+
},
|
|
223
|
+
timeout: {
|
|
224
|
+
type: "number",
|
|
225
|
+
description: "Timeout in milliseconds",
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
required: ["command"],
|
|
229
|
+
},
|
|
230
|
+
async (args) => {
|
|
231
|
+
const { command, workdir, timeout } = args as {
|
|
232
|
+
command: string;
|
|
233
|
+
workdir?: string;
|
|
234
|
+
timeout?: number;
|
|
235
|
+
};
|
|
236
|
+
try {
|
|
237
|
+
const { exec } = await import("node:child_process");
|
|
238
|
+
const { promisify } = await import("node:util");
|
|
239
|
+
const execAsync = promisify(exec);
|
|
240
|
+
|
|
241
|
+
const result = await execAsync(command, {
|
|
242
|
+
cwd: workdir,
|
|
243
|
+
timeout: timeout ?? 120000,
|
|
244
|
+
});
|
|
245
|
+
return {
|
|
246
|
+
content: [
|
|
247
|
+
{
|
|
248
|
+
type: "text",
|
|
249
|
+
text: result.stdout || "(no output)",
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
};
|
|
253
|
+
} catch (error) {
|
|
254
|
+
return {
|
|
255
|
+
content: [
|
|
256
|
+
{
|
|
257
|
+
type: "text",
|
|
258
|
+
text: `Error executing command: ${error instanceof Error ? error.message : String(error)}`,
|
|
259
|
+
},
|
|
260
|
+
],
|
|
261
|
+
isError: true,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
this.registry.register(
|
|
268
|
+
"poolbot_list_directory",
|
|
269
|
+
"List files and directories",
|
|
270
|
+
{
|
|
271
|
+
type: "object",
|
|
272
|
+
properties: {
|
|
273
|
+
path: {
|
|
274
|
+
type: "string",
|
|
275
|
+
description: "Directory path to list",
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
required: ["path"],
|
|
279
|
+
},
|
|
280
|
+
async (args) => {
|
|
281
|
+
const { path: dirPath } = args as { path: string };
|
|
282
|
+
try {
|
|
283
|
+
const fs = await import("node:fs/promises");
|
|
284
|
+
const entries = await fs.readdir(dirPath, { withFileTypes: true });
|
|
285
|
+
const result = entries
|
|
286
|
+
.map((entry) => `${entry.isDirectory() ? "📁" : "📄"} ${entry.name}`)
|
|
287
|
+
.join("\n");
|
|
288
|
+
return {
|
|
289
|
+
content: [
|
|
290
|
+
{
|
|
291
|
+
type: "text",
|
|
292
|
+
text: result || "(empty directory)",
|
|
293
|
+
},
|
|
294
|
+
],
|
|
295
|
+
};
|
|
296
|
+
} catch (error) {
|
|
297
|
+
return {
|
|
298
|
+
content: [
|
|
299
|
+
{
|
|
300
|
+
type: "text",
|
|
301
|
+
text: `Error listing directory: ${error instanceof Error ? error.message : String(error)}`,
|
|
302
|
+
},
|
|
303
|
+
],
|
|
304
|
+
isError: true,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
this.registry.register(
|
|
311
|
+
"poolbot_search_files",
|
|
312
|
+
"Search for files matching a pattern",
|
|
313
|
+
{
|
|
314
|
+
type: "object",
|
|
315
|
+
properties: {
|
|
316
|
+
pattern: {
|
|
317
|
+
type: "string",
|
|
318
|
+
description: "Glob pattern to search for",
|
|
319
|
+
},
|
|
320
|
+
path: {
|
|
321
|
+
type: "string",
|
|
322
|
+
description: "Base directory to search in",
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
required: ["pattern"],
|
|
326
|
+
},
|
|
327
|
+
async (args) => {
|
|
328
|
+
const { pattern, path: basePath = "." } = args as {
|
|
329
|
+
pattern: string;
|
|
330
|
+
path?: string;
|
|
331
|
+
};
|
|
332
|
+
try {
|
|
333
|
+
const { glob } = await import("glob");
|
|
334
|
+
const matches = await glob(pattern, { cwd: basePath, absolute: true });
|
|
335
|
+
return {
|
|
336
|
+
content: [
|
|
337
|
+
{
|
|
338
|
+
type: "text",
|
|
339
|
+
text: matches.join("\n") || "(no matches)",
|
|
340
|
+
},
|
|
341
|
+
],
|
|
342
|
+
};
|
|
343
|
+
} catch (error) {
|
|
344
|
+
return {
|
|
345
|
+
content: [
|
|
346
|
+
{
|
|
347
|
+
type: "text",
|
|
348
|
+
text: `Error searching files: ${error instanceof Error ? error.message : String(error)}`,
|
|
349
|
+
},
|
|
350
|
+
],
|
|
351
|
+
isError: true,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
);
|
|
356
|
+
|
|
357
|
+
this.registry.register(
|
|
358
|
+
"poolbot_send_message",
|
|
359
|
+
"Send a message through PoolBot",
|
|
360
|
+
{
|
|
361
|
+
type: "object",
|
|
362
|
+
properties: {
|
|
363
|
+
channel: {
|
|
364
|
+
type: "string",
|
|
365
|
+
description: "Channel to send message to (e.g., telegram, discord)",
|
|
366
|
+
},
|
|
367
|
+
to: {
|
|
368
|
+
type: "string",
|
|
369
|
+
description: "Recipient ID or chat ID",
|
|
370
|
+
},
|
|
371
|
+
message: {
|
|
372
|
+
type: "string",
|
|
373
|
+
description: "Message content",
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
required: ["channel", "to", "message"],
|
|
377
|
+
},
|
|
378
|
+
async (args) => {
|
|
379
|
+
const { channel, to, message } = args as {
|
|
380
|
+
channel: string;
|
|
381
|
+
to: string;
|
|
382
|
+
message: string;
|
|
383
|
+
};
|
|
384
|
+
// This would integrate with PoolBot's message sending
|
|
385
|
+
// For now, return a placeholder
|
|
386
|
+
return {
|
|
387
|
+
content: [
|
|
388
|
+
{
|
|
389
|
+
type: "text",
|
|
390
|
+
text: `Message queued for ${channel}:${to}: ${message.slice(0, 100)}...`,
|
|
391
|
+
},
|
|
392
|
+
],
|
|
393
|
+
};
|
|
394
|
+
},
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
private async handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {
|
|
399
|
+
// Set CORS headers
|
|
400
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
401
|
+
res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
|
|
402
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
403
|
+
|
|
404
|
+
if (req.method === "OPTIONS") {
|
|
405
|
+
res.writeHead(200);
|
|
406
|
+
res.end();
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (req.method !== "POST") {
|
|
411
|
+
res.writeHead(405, { "Content-Type": "application/json" });
|
|
412
|
+
res.end(JSON.stringify({ error: "Method not allowed" }));
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Read body
|
|
417
|
+
const chunks: Buffer[] = [];
|
|
418
|
+
for await (const chunk of req) {
|
|
419
|
+
chunks.push(chunk);
|
|
420
|
+
}
|
|
421
|
+
const body = Buffer.concat(chunks).toString("utf-8");
|
|
422
|
+
|
|
423
|
+
let request: McpRequest;
|
|
424
|
+
try {
|
|
425
|
+
request = JSON.parse(body) as McpRequest;
|
|
426
|
+
} catch {
|
|
427
|
+
const response: McpResponse = {
|
|
428
|
+
jsonrpc: "2.0",
|
|
429
|
+
id: 0,
|
|
430
|
+
error: { code: -32700, message: "Parse error" },
|
|
431
|
+
};
|
|
432
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
433
|
+
res.end(JSON.stringify(response));
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const response = await this.processRequest(request);
|
|
438
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
439
|
+
res.end(JSON.stringify(response));
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
private async processRequest(request: McpRequest): Promise<McpResponse> {
|
|
443
|
+
const { id, method, params } = request;
|
|
444
|
+
|
|
445
|
+
switch (method) {
|
|
446
|
+
case "initialize": {
|
|
447
|
+
return {
|
|
448
|
+
jsonrpc: "2.0",
|
|
449
|
+
id,
|
|
450
|
+
result: {
|
|
451
|
+
protocolVersion: "2024-11-05",
|
|
452
|
+
capabilities: {
|
|
453
|
+
tools: {},
|
|
454
|
+
},
|
|
455
|
+
serverInfo: {
|
|
456
|
+
name: "poolbot-mcp-server",
|
|
457
|
+
version: "2026.3.9",
|
|
458
|
+
},
|
|
459
|
+
},
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
case "tools/list": {
|
|
464
|
+
return {
|
|
465
|
+
jsonrpc: "2.0",
|
|
466
|
+
id,
|
|
467
|
+
result: {
|
|
468
|
+
tools: this.registry.getAllTools(),
|
|
469
|
+
},
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
case "tools/call": {
|
|
474
|
+
const { name, arguments: args } = params as { name: string; arguments?: unknown };
|
|
475
|
+
const result = await this.registry.execute(name, args);
|
|
476
|
+
return {
|
|
477
|
+
jsonrpc: "2.0",
|
|
478
|
+
id,
|
|
479
|
+
result,
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
default: {
|
|
484
|
+
return {
|
|
485
|
+
jsonrpc: "2.0",
|
|
486
|
+
id,
|
|
487
|
+
error: { code: -32601, message: `Method not found: ${method}` },
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
start(): Promise<void> {
|
|
494
|
+
return new Promise((resolve) => {
|
|
495
|
+
this.server = createServer((req, res) => {
|
|
496
|
+
this.handleRequest(req, res).catch(() => {
|
|
497
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
498
|
+
res.end(JSON.stringify({ error: "Internal server error" }));
|
|
499
|
+
});
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
this.server.listen(this.port, () => {
|
|
503
|
+
console.log(`MCP Server listening on port ${this.port}`);
|
|
504
|
+
resolve();
|
|
505
|
+
});
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
stop(): Promise<void> {
|
|
510
|
+
return new Promise((resolve) => {
|
|
511
|
+
if (this.server) {
|
|
512
|
+
this.server.close(() => {
|
|
513
|
+
resolve();
|
|
514
|
+
});
|
|
515
|
+
} else {
|
|
516
|
+
resolve();
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
export function createMcpServerService(): PoolBotPluginService {
|
|
523
|
+
let server: McpServer | null = null;
|
|
524
|
+
|
|
525
|
+
return {
|
|
526
|
+
id: "mcp-server",
|
|
527
|
+
async start(ctx) {
|
|
528
|
+
const port = Number(process.env.POOLBOT_MCP_PORT) || 8765;
|
|
529
|
+
server = new McpServer(port);
|
|
530
|
+
await server.start();
|
|
531
|
+
ctx.logger.info(`MCP Server started on port ${port}`);
|
|
532
|
+
},
|
|
533
|
+
async stop() {
|
|
534
|
+
if (server) {
|
|
535
|
+
await server.stop();
|
|
536
|
+
server = null;
|
|
537
|
+
}
|
|
538
|
+
},
|
|
539
|
+
};
|
|
540
|
+
}
|