@poolzin/pool-bot 2026.3.7 → 2026.3.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -0
- package/README.md +147 -69
- package/dist/.buildstamp +1 -1
- package/dist/agents/error-classifier.js +251 -0
- package/dist/agents/skills/security.js +211 -0
- 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/lazy-commands.example.js +113 -0
- package/dist/cli/lazy-commands.js +329 -0
- package/dist/cli/program/command-registry.js +26 -0
- package/dist/cli/program/register.maintenance.js +21 -0
- package/dist/cli/program/register.skills.js +4 -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/config/config.js +1 -0
- package/dist/config/secrets-integration.js +88 -0
- package/dist/context-engine/index.js +33 -0
- package/dist/context-engine/legacy.js +179 -0
- package/dist/context-engine/registry.js +86 -0
- package/dist/context-engine/summarizing.js +290 -0
- package/dist/context-engine/types.js +7 -0
- 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 +106 -0
- package/dist/infra/retry.js +96 -0
- package/dist/secrets/index.js +28 -0
- package/dist/secrets/resolver.js +185 -0
- package/dist/secrets/runtime.js +142 -0
- package/dist/secrets/types.js +11 -0
- package/dist/security/dangerous-tools.js +80 -0
- package/dist/security/types.js +12 -0
- package/dist/skills/commands.js +333 -0
- package/dist/skills/index.js +164 -0
- package/dist/skills/loader.js +282 -0
- package/dist/skills/parser.js +446 -0
- package/dist/skills/registry.js +394 -0
- package/dist/skills/security.js +312 -0
- package/dist/skills/types.js +21 -0
- 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/test-utils/index.js +219 -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 +611 -0
- 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/INTEGRATION_PLAN.md +475 -0
- package/docs/INTEGRATION_SUMMARY.md +215 -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/HEXSTRIKE_PLAN.md +796 -0
- package/docs/integrations/INTEGRATION_PLAN.md +744 -0
- package/docs/integrations/PAGE_AGENT_PLAN.md +370 -0
- package/docs/integrations/XYOPS_PLAN.md +978 -0
- package/docs/models/provider-infrastructure.md +400 -0
- package/docs/security/exec-approvals.md +294 -0
- package/docs/skills/IMPLEMENTATION_SUMMARY.md +145 -0
- package/docs/skills/SKILL.md +524 -0
- package/docs/skills.md +405 -0
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/discord/package.json +1 -1
- package/extensions/feishu/package.json +1 -1
- package/extensions/google-antigravity-auth/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/googlechat/package.json +1 -1
- package/extensions/hexstrike-bridge/README.md +119 -0
- package/extensions/hexstrike-bridge/index.test.ts +247 -0
- package/extensions/hexstrike-bridge/index.ts +487 -0
- package/extensions/hexstrike-bridge/package.json +17 -0
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/matrix/CHANGELOG.md +5 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/mattermost/package.json +1 -1
- package/extensions/mcp-server/index.ts +14 -0
- package/extensions/mcp-server/package.json +11 -0
- package/extensions/mcp-server/src/service.ts +540 -0
- package/extensions/memory-core/package.json +1 -1
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +5 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +5 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/openai-codex-auth/package.json +1 -1
- package/extensions/signal/package.json +1 -1
- package/extensions/slack/package.json +1 -1
- package/extensions/telegram/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +5 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +5 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +5 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +5 -0
- package/extensions/zalouser/package.json +1 -1
- package/package.json +8 -1
- package/skills/example-skill/SKILL.md +195 -0
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper to safely get config
|
|
3
|
+
*/
|
|
4
|
+
async function safeLoadConfig() {
|
|
5
|
+
try {
|
|
6
|
+
const { loadConfig } = await import("../config/config.js");
|
|
7
|
+
return (await loadConfig());
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Registry of all doctor checks
|
|
15
|
+
*/
|
|
16
|
+
export const doctorCheckRegistry = {
|
|
17
|
+
config: async () => {
|
|
18
|
+
const start = Date.now();
|
|
19
|
+
try {
|
|
20
|
+
const { readConfigFileSnapshot } = await import("../config/config.js");
|
|
21
|
+
const snapshot = await readConfigFileSnapshot();
|
|
22
|
+
if (!snapshot.exists) {
|
|
23
|
+
return {
|
|
24
|
+
name: "config",
|
|
25
|
+
status: "error",
|
|
26
|
+
message: "Configuration file not found",
|
|
27
|
+
durationMs: Date.now() - start,
|
|
28
|
+
suggestions: ["Run `poolbot setup` to create initial configuration"],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (!snapshot.valid) {
|
|
32
|
+
const issues = snapshot.issues?.map((i) => i.message).join("; ") ?? "Unknown issues";
|
|
33
|
+
return {
|
|
34
|
+
name: "config",
|
|
35
|
+
status: "error",
|
|
36
|
+
message: `Configuration invalid: ${issues}`,
|
|
37
|
+
durationMs: Date.now() - start,
|
|
38
|
+
suggestions: ["Run `poolbot doctor --fix` to auto-repair configuration"],
|
|
39
|
+
details: { issues: snapshot.issues },
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
name: "config",
|
|
44
|
+
status: "ok",
|
|
45
|
+
message: "Configuration is valid",
|
|
46
|
+
durationMs: Date.now() - start,
|
|
47
|
+
details: { path: snapshot.path },
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
return {
|
|
52
|
+
name: "config",
|
|
53
|
+
status: "error",
|
|
54
|
+
message: `Failed to check config: ${String(err)}`,
|
|
55
|
+
durationMs: Date.now() - start,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
auth: async () => {
|
|
60
|
+
const start = Date.now();
|
|
61
|
+
try {
|
|
62
|
+
const cfg = await safeLoadConfig();
|
|
63
|
+
if (!cfg) {
|
|
64
|
+
return {
|
|
65
|
+
name: "auth",
|
|
66
|
+
status: "skipped",
|
|
67
|
+
message: "Could not load configuration",
|
|
68
|
+
durationMs: Date.now() - start,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// Check auth profiles
|
|
72
|
+
const authProfiles = cfg.authProfiles;
|
|
73
|
+
if (!authProfiles || authProfiles.length === 0) {
|
|
74
|
+
return {
|
|
75
|
+
name: "auth",
|
|
76
|
+
status: "warning",
|
|
77
|
+
message: "No authentication profiles configured",
|
|
78
|
+
durationMs: Date.now() - start,
|
|
79
|
+
suggestions: ["Run `poolbot configure` to set up authentication"],
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
const validProfiles = authProfiles.filter((p) => p?.credential);
|
|
83
|
+
if (validProfiles.length === 0) {
|
|
84
|
+
return {
|
|
85
|
+
name: "auth",
|
|
86
|
+
status: "error",
|
|
87
|
+
message: "Auth profiles exist but none have valid credentials",
|
|
88
|
+
durationMs: Date.now() - start,
|
|
89
|
+
suggestions: ["Run `poolbot configure` to re-authenticate"],
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
name: "auth",
|
|
94
|
+
status: "ok",
|
|
95
|
+
message: `${validProfiles.length} authentication profile(s) configured`,
|
|
96
|
+
durationMs: Date.now() - start,
|
|
97
|
+
details: { profileCount: validProfiles.length },
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
catch (err) {
|
|
101
|
+
return {
|
|
102
|
+
name: "auth",
|
|
103
|
+
status: "error",
|
|
104
|
+
message: `Failed to check auth: ${String(err)}`,
|
|
105
|
+
durationMs: Date.now() - start,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
completion: async () => {
|
|
110
|
+
const start = Date.now();
|
|
111
|
+
try {
|
|
112
|
+
const { checkShellCompletionStatus } = await import("./doctor-completion.js");
|
|
113
|
+
const status = await checkShellCompletionStatus();
|
|
114
|
+
if (!status.cacheExists) {
|
|
115
|
+
return {
|
|
116
|
+
name: "completion",
|
|
117
|
+
status: "warning",
|
|
118
|
+
message: "Completion cache not found",
|
|
119
|
+
durationMs: Date.now() - start,
|
|
120
|
+
suggestions: [
|
|
121
|
+
"Run `poolbot completion --write-state` to generate cache",
|
|
122
|
+
"Run `poolbot completion --install` to install to shell profile",
|
|
123
|
+
],
|
|
124
|
+
details: { shell: status.shell },
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
if (!status.profileInstalled) {
|
|
128
|
+
return {
|
|
129
|
+
name: "completion",
|
|
130
|
+
status: "warning",
|
|
131
|
+
message: `Completion cache exists but not installed in ${status.shell} profile`,
|
|
132
|
+
durationMs: Date.now() - start,
|
|
133
|
+
suggestions: ["Run `poolbot completion --install` to enable completion"],
|
|
134
|
+
details: { shell: status.shell, cachePath: status.cachePath },
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
if (status.usesSlowPattern) {
|
|
138
|
+
return {
|
|
139
|
+
name: "completion",
|
|
140
|
+
status: "warning",
|
|
141
|
+
message: "Using slow dynamic completion pattern",
|
|
142
|
+
durationMs: Date.now() - start,
|
|
143
|
+
suggestions: ["Run `poolbot doctor --fix` to upgrade to cached completion"],
|
|
144
|
+
details: { shell: status.shell },
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
name: "completion",
|
|
149
|
+
status: "ok",
|
|
150
|
+
message: `Shell completion installed and cached for ${status.shell}`,
|
|
151
|
+
durationMs: Date.now() - start,
|
|
152
|
+
details: { shell: status.shell, cachePath: status.cachePath },
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
return {
|
|
157
|
+
name: "completion",
|
|
158
|
+
status: "error",
|
|
159
|
+
message: `Failed to check completion: ${String(err)}`,
|
|
160
|
+
durationMs: Date.now() - start,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
gateway: async ({ runtime, options }) => {
|
|
165
|
+
const start = Date.now();
|
|
166
|
+
try {
|
|
167
|
+
const cfg = await safeLoadConfig();
|
|
168
|
+
if (!cfg) {
|
|
169
|
+
return {
|
|
170
|
+
name: "gateway",
|
|
171
|
+
status: "skipped",
|
|
172
|
+
message: "Could not load configuration",
|
|
173
|
+
durationMs: Date.now() - start,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
const { checkGatewayHealth } = await import("./doctor-gateway-health.js");
|
|
177
|
+
const result = await checkGatewayHealth({
|
|
178
|
+
runtime,
|
|
179
|
+
cfg: cfg,
|
|
180
|
+
timeoutMs: options.nonInteractive ? 3000 : 10_000,
|
|
181
|
+
});
|
|
182
|
+
if (!result.healthOk) {
|
|
183
|
+
return {
|
|
184
|
+
name: "gateway",
|
|
185
|
+
status: "error",
|
|
186
|
+
message: "Gateway health check failed",
|
|
187
|
+
durationMs: Date.now() - start,
|
|
188
|
+
suggestions: [
|
|
189
|
+
"Run `poolbot gateway status` for detailed status",
|
|
190
|
+
"Run `poolbot gateway restart` to restart the gateway",
|
|
191
|
+
],
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
name: "gateway",
|
|
196
|
+
status: "ok",
|
|
197
|
+
message: "Gateway is healthy",
|
|
198
|
+
durationMs: Date.now() - start,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
catch (err) {
|
|
202
|
+
return {
|
|
203
|
+
name: "gateway",
|
|
204
|
+
status: "error",
|
|
205
|
+
message: `Failed to check gateway: ${String(err)}`,
|
|
206
|
+
durationMs: Date.now() - start,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
security: async () => {
|
|
211
|
+
const start = Date.now();
|
|
212
|
+
try {
|
|
213
|
+
const cfg = await safeLoadConfig();
|
|
214
|
+
if (!cfg) {
|
|
215
|
+
return {
|
|
216
|
+
name: "security",
|
|
217
|
+
status: "skipped",
|
|
218
|
+
message: "Could not load configuration",
|
|
219
|
+
durationMs: Date.now() - start,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
// Check for security warnings
|
|
223
|
+
const warnings = [];
|
|
224
|
+
const suggestions = [];
|
|
225
|
+
// Check gateway auth
|
|
226
|
+
const gateway = cfg.gateway;
|
|
227
|
+
const auth = gateway?.auth;
|
|
228
|
+
const authMode = auth?.mode;
|
|
229
|
+
if (authMode === "off") {
|
|
230
|
+
warnings.push("Gateway authentication is disabled");
|
|
231
|
+
suggestions.push("Enable token auth: poolbot config set gateway.auth.mode token");
|
|
232
|
+
}
|
|
233
|
+
// Check channel allowlists
|
|
234
|
+
const channels = cfg.channels;
|
|
235
|
+
if (channels) {
|
|
236
|
+
for (const [name, channel] of Object.entries(channels)) {
|
|
237
|
+
if (channel && typeof channel === "object") {
|
|
238
|
+
const ch = channel;
|
|
239
|
+
if (!ch.allowFrom || ch.allowFrom.length === 0) {
|
|
240
|
+
warnings.push(`Channel ${name} has no allowlist configured`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (warnings.length > 0) {
|
|
246
|
+
return {
|
|
247
|
+
name: "security",
|
|
248
|
+
status: "warning",
|
|
249
|
+
message: `${warnings.length} security warning(s) found`,
|
|
250
|
+
durationMs: Date.now() - start,
|
|
251
|
+
suggestions,
|
|
252
|
+
details: { warnings },
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
name: "security",
|
|
257
|
+
status: "ok",
|
|
258
|
+
message: "No security warnings detected",
|
|
259
|
+
durationMs: Date.now() - start,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
catch (err) {
|
|
263
|
+
return {
|
|
264
|
+
name: "security",
|
|
265
|
+
status: "error",
|
|
266
|
+
message: `Failed to check security: ${String(err)}`,
|
|
267
|
+
durationMs: Date.now() - start,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
plugins: async () => {
|
|
272
|
+
const start = Date.now();
|
|
273
|
+
// This is a simplified check - real implementation would load plugin registry
|
|
274
|
+
return {
|
|
275
|
+
name: "plugins",
|
|
276
|
+
status: "ok",
|
|
277
|
+
message: "Plugin check not yet implemented",
|
|
278
|
+
durationMs: Date.now() - start,
|
|
279
|
+
};
|
|
280
|
+
},
|
|
281
|
+
memory: async () => {
|
|
282
|
+
const start = Date.now();
|
|
283
|
+
try {
|
|
284
|
+
const cfg = await safeLoadConfig();
|
|
285
|
+
if (!cfg) {
|
|
286
|
+
return {
|
|
287
|
+
name: "memory",
|
|
288
|
+
status: "skipped",
|
|
289
|
+
message: "Could not load configuration",
|
|
290
|
+
durationMs: Date.now() - start,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
const agents = cfg.agents;
|
|
294
|
+
const defaults = agents?.defaults;
|
|
295
|
+
const memorySearch = defaults?.memorySearch;
|
|
296
|
+
const memoryEnabled = memorySearch?.enabled ?? false;
|
|
297
|
+
if (!memoryEnabled) {
|
|
298
|
+
return {
|
|
299
|
+
name: "memory",
|
|
300
|
+
status: "warning",
|
|
301
|
+
message: "Memory search is disabled",
|
|
302
|
+
durationMs: Date.now() - start,
|
|
303
|
+
suggestions: [
|
|
304
|
+
"Enable memory: poolbot config set agents.defaults.memorySearch.enabled true",
|
|
305
|
+
],
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
name: "memory",
|
|
310
|
+
status: "ok",
|
|
311
|
+
message: "Memory search is enabled",
|
|
312
|
+
durationMs: Date.now() - start,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
catch (err) {
|
|
316
|
+
return {
|
|
317
|
+
name: "memory",
|
|
318
|
+
status: "error",
|
|
319
|
+
message: `Failed to check memory: ${String(err)}`,
|
|
320
|
+
durationMs: Date.now() - start,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
workspace: async () => {
|
|
325
|
+
const start = Date.now();
|
|
326
|
+
try {
|
|
327
|
+
const agentScope = await import("../agents/agent-scope.js");
|
|
328
|
+
const cfg = await safeLoadConfig();
|
|
329
|
+
if (!cfg) {
|
|
330
|
+
return {
|
|
331
|
+
name: "workspace",
|
|
332
|
+
status: "skipped",
|
|
333
|
+
message: "Could not load configuration",
|
|
334
|
+
durationMs: Date.now() - start,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
const agentId = agentScope.resolveDefaultAgentId(cfg);
|
|
338
|
+
const workspaceDir = agentScope.resolveAgentWorkspaceDir(cfg, agentId);
|
|
339
|
+
// Check if workspace exists
|
|
340
|
+
const fs = await import("node:fs");
|
|
341
|
+
if (!fs.existsSync(workspaceDir)) {
|
|
342
|
+
return {
|
|
343
|
+
name: "workspace",
|
|
344
|
+
status: "warning",
|
|
345
|
+
message: `Workspace directory does not exist`,
|
|
346
|
+
durationMs: Date.now() - start,
|
|
347
|
+
suggestions: ["The workspace will be created on first agent run"],
|
|
348
|
+
details: { path: workspaceDir },
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
return {
|
|
352
|
+
name: "workspace",
|
|
353
|
+
status: "ok",
|
|
354
|
+
message: `Workspace ready`,
|
|
355
|
+
durationMs: Date.now() - start,
|
|
356
|
+
details: { path: workspaceDir },
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
catch (err) {
|
|
360
|
+
return {
|
|
361
|
+
name: "workspace",
|
|
362
|
+
status: "error",
|
|
363
|
+
message: `Failed to check workspace: ${String(err)}`,
|
|
364
|
+
durationMs: Date.now() - start,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
state: async () => {
|
|
369
|
+
const start = Date.now();
|
|
370
|
+
try {
|
|
371
|
+
const { resolveStateDir } = await import("../config/paths.js");
|
|
372
|
+
const path = await import("node:path");
|
|
373
|
+
const fs = await import("node:fs");
|
|
374
|
+
const stateDir = resolveStateDir();
|
|
375
|
+
const checks = {
|
|
376
|
+
stateDir: fs.existsSync(stateDir),
|
|
377
|
+
completions: fs.existsSync(path.join(stateDir, "completions")),
|
|
378
|
+
sessions: fs.existsSync(path.join(stateDir, "sessions")),
|
|
379
|
+
};
|
|
380
|
+
const missing = Object.entries(checks)
|
|
381
|
+
.filter(([, exists]) => !exists)
|
|
382
|
+
.map(([name]) => name);
|
|
383
|
+
if (missing.length > 0) {
|
|
384
|
+
return {
|
|
385
|
+
name: "state",
|
|
386
|
+
status: "warning",
|
|
387
|
+
message: `Missing state directories: ${missing.join(", ")}`,
|
|
388
|
+
durationMs: Date.now() - start,
|
|
389
|
+
suggestions: ["Run `poolbot doctor --fix` to create missing directories"],
|
|
390
|
+
details: { checks },
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
return {
|
|
394
|
+
name: "state",
|
|
395
|
+
status: "ok",
|
|
396
|
+
message: "All state directories present",
|
|
397
|
+
durationMs: Date.now() - start,
|
|
398
|
+
details: { checks },
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
catch (err) {
|
|
402
|
+
return {
|
|
403
|
+
name: "state",
|
|
404
|
+
status: "error",
|
|
405
|
+
message: `Failed to check state: ${String(err)}`,
|
|
406
|
+
durationMs: Date.now() - start,
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
},
|
|
410
|
+
};
|
|
411
|
+
/**
|
|
412
|
+
* Run a specific check by name
|
|
413
|
+
*/
|
|
414
|
+
export async function runDoctorCheck(name, params) {
|
|
415
|
+
const checkFn = doctorCheckRegistry[name];
|
|
416
|
+
if (!checkFn) {
|
|
417
|
+
return {
|
|
418
|
+
name,
|
|
419
|
+
status: "error",
|
|
420
|
+
message: `Unknown check: ${name}`,
|
|
421
|
+
durationMs: 0,
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
return checkFn(params);
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Run all checks and generate report
|
|
428
|
+
*/
|
|
429
|
+
export async function runAllDoctorChecks(params) {
|
|
430
|
+
const checks = [
|
|
431
|
+
"config",
|
|
432
|
+
"auth",
|
|
433
|
+
"completion",
|
|
434
|
+
"gateway",
|
|
435
|
+
"security",
|
|
436
|
+
"memory",
|
|
437
|
+
"workspace",
|
|
438
|
+
"state",
|
|
439
|
+
];
|
|
440
|
+
const results = [];
|
|
441
|
+
for (const checkName of checks) {
|
|
442
|
+
const result = await runDoctorCheck(checkName, params);
|
|
443
|
+
results.push(result);
|
|
444
|
+
}
|
|
445
|
+
// Calculate health score
|
|
446
|
+
const weights = { ok: 1, warning: 0.5, error: 0, skipped: 0.75 };
|
|
447
|
+
const totalWeight = results.reduce((sum, r) => sum + weights[r.status], 0);
|
|
448
|
+
const healthScore = Math.round((totalWeight / results.length) * 100);
|
|
449
|
+
return {
|
|
450
|
+
timestamp: new Date().toISOString(),
|
|
451
|
+
version: "2026.3.9", // Should be dynamic
|
|
452
|
+
checks: results,
|
|
453
|
+
summary: {
|
|
454
|
+
total: results.length,
|
|
455
|
+
ok: results.filter((r) => r.status === "ok").length,
|
|
456
|
+
warning: results.filter((r) => r.status === "warning").length,
|
|
457
|
+
error: results.filter((r) => r.status === "error").length,
|
|
458
|
+
skipped: results.filter((r) => r.status === "skipped").length,
|
|
459
|
+
autoFixed: results.filter((r) => r.autoFixed).length,
|
|
460
|
+
},
|
|
461
|
+
healthScore,
|
|
462
|
+
};
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Format report for console output
|
|
466
|
+
*/
|
|
467
|
+
export function formatDoctorReport(report) {
|
|
468
|
+
const lines = [];
|
|
469
|
+
// Header
|
|
470
|
+
lines.push("╔════════════════════════════════════════════════════════╗");
|
|
471
|
+
lines.push("║ 🏥 PoolBot Doctor Report ║");
|
|
472
|
+
lines.push("╚════════════════════════════════════════════════════════╝");
|
|
473
|
+
lines.push("");
|
|
474
|
+
// Health score
|
|
475
|
+
const scoreColor = report.healthScore >= 80 ? "🟢" : report.healthScore >= 50 ? "🟡" : "🔴";
|
|
476
|
+
lines.push(`Health Score: ${scoreColor} ${report.healthScore}/100`);
|
|
477
|
+
lines.push("");
|
|
478
|
+
// Summary
|
|
479
|
+
lines.push("Summary:");
|
|
480
|
+
lines.push(` ✅ OK: ${report.summary.ok}/${report.summary.total}`);
|
|
481
|
+
lines.push(` ⚠️ Warning: ${report.summary.warning}/${report.summary.total}`);
|
|
482
|
+
lines.push(` ❌ Error: ${report.summary.error}/${report.summary.total}`);
|
|
483
|
+
if (report.summary.autoFixed > 0) {
|
|
484
|
+
lines.push(` 🔧 Auto-fixed: ${report.summary.autoFixed}`);
|
|
485
|
+
}
|
|
486
|
+
lines.push("");
|
|
487
|
+
// Individual checks
|
|
488
|
+
for (const check of report.checks) {
|
|
489
|
+
const icon = check.status === "ok" ? "✅" : check.status === "warning" ? "⚠️" : "❌";
|
|
490
|
+
lines.push(`${icon} ${check.name.padEnd(12)} ${check.message}`);
|
|
491
|
+
if (check.suggestions && check.suggestions.length > 0) {
|
|
492
|
+
for (const suggestion of check.suggestions) {
|
|
493
|
+
lines.push(` 💡 ${suggestion}`);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
return lines.join("\n");
|
|
498
|
+
}
|
package/dist/config/config.js
CHANGED
|
@@ -2,6 +2,7 @@ export { createConfigIO, loadConfig, parseConfigJson5, readConfigFileSnapshot, r
|
|
|
2
2
|
export { migrateLegacyConfig } from "./legacy-migrate.js";
|
|
3
3
|
export * from "./paths.js";
|
|
4
4
|
export * from "./runtime-overrides.js";
|
|
5
|
+
export * from "./secrets-integration.js";
|
|
5
6
|
export * from "./types.js";
|
|
6
7
|
export { validateConfigObject, validateConfigObjectWithPlugins } from "./validation.js";
|
|
7
8
|
export { PoolBotSchema } from "./zod-schema.js";
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config Secrets Integration
|
|
3
|
+
*
|
|
4
|
+
* Integrates the secrets management system with config loading.
|
|
5
|
+
* Resolves SecretRefs in config values at runtime.
|
|
6
|
+
*/
|
|
7
|
+
import { SecretsRuntime } from "../secrets/index.js";
|
|
8
|
+
const SECRET_PATHS = [
|
|
9
|
+
"agent.model",
|
|
10
|
+
"agent.apiKey",
|
|
11
|
+
"channels.telegram.botToken",
|
|
12
|
+
"channels.discord.token",
|
|
13
|
+
"channels.slack.botToken",
|
|
14
|
+
"channels.slack.appToken",
|
|
15
|
+
"gateway.auth.token",
|
|
16
|
+
"gateway.auth.password",
|
|
17
|
+
"memory.apiKey",
|
|
18
|
+
"tools.browser.apiKey",
|
|
19
|
+
];
|
|
20
|
+
/**
|
|
21
|
+
* Deeply traverse config and resolve secret references
|
|
22
|
+
*/
|
|
23
|
+
export function resolveConfigSecrets(config, secretsRuntime) {
|
|
24
|
+
const resolved = { ...config };
|
|
25
|
+
// Resolve specific known secret paths
|
|
26
|
+
for (const path of SECRET_PATHS) {
|
|
27
|
+
resolvePath(resolved, path.split("."), secretsRuntime);
|
|
28
|
+
}
|
|
29
|
+
// Also scan for any string values that look like secret refs
|
|
30
|
+
scanAndResolveSecrets(resolved, "", secretsRuntime);
|
|
31
|
+
return resolved;
|
|
32
|
+
}
|
|
33
|
+
function resolvePath(obj, path, runtime) {
|
|
34
|
+
if (path.length === 0)
|
|
35
|
+
return;
|
|
36
|
+
const [head, ...tail] = path;
|
|
37
|
+
if (!(head in obj))
|
|
38
|
+
return;
|
|
39
|
+
if (tail.length === 0) {
|
|
40
|
+
// Leaf node - resolve if it's a string
|
|
41
|
+
const value = obj[head];
|
|
42
|
+
if (typeof value === "string") {
|
|
43
|
+
const result = runtime.resolve(head, value);
|
|
44
|
+
if (result.value !== undefined) {
|
|
45
|
+
obj[head] = result.value;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
// Traverse deeper
|
|
51
|
+
const child = obj[head];
|
|
52
|
+
if (typeof child === "object" && child !== null && !Array.isArray(child)) {
|
|
53
|
+
resolvePath(child, tail, runtime);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function scanAndResolveSecrets(obj, path, runtime) {
|
|
58
|
+
if (typeof obj === "string") {
|
|
59
|
+
// Check if it looks like a secret ref
|
|
60
|
+
if (obj.startsWith("env:") || obj.startsWith("file:")) {
|
|
61
|
+
runtime.resolve(path || "unknown", obj);
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (Array.isArray(obj)) {
|
|
66
|
+
obj.forEach((item, index) => {
|
|
67
|
+
scanAndResolveSecrets(item, `${path}[${index}]`, runtime);
|
|
68
|
+
});
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
if (typeof obj === "object" && obj !== null) {
|
|
72
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
73
|
+
const newPath = path ? `${path}.${key}` : key;
|
|
74
|
+
scanAndResolveSecrets(value, newPath, runtime);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Create a secrets runtime from config
|
|
80
|
+
*/
|
|
81
|
+
export function createSecretsRuntimeFromConfig(_config, secretsConfig) {
|
|
82
|
+
return new SecretsRuntime({
|
|
83
|
+
allowDirectValues: true,
|
|
84
|
+
warnOnDirectValues: true,
|
|
85
|
+
failOnUnresolved: false,
|
|
86
|
+
...secretsConfig,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Engine - Pluggable conversation context management
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```typescript
|
|
6
|
+
* import { LegacyContextEngine, globalContextEngineRegistry } from './context-engine/index.js';
|
|
7
|
+
*
|
|
8
|
+
* // Register the legacy engine
|
|
9
|
+
* globalContextEngineRegistry.register('legacy', LegacyContextEngine);
|
|
10
|
+
*
|
|
11
|
+
* // Get engine instance
|
|
12
|
+
* const engine = globalContextEngineRegistry.getEngine('legacy');
|
|
13
|
+
*
|
|
14
|
+
* // Bootstrap for a session
|
|
15
|
+
* await engine.bootstrap({ sessionId: 'session-123' });
|
|
16
|
+
*
|
|
17
|
+
* // Ingest a message
|
|
18
|
+
* await engine.ingest({
|
|
19
|
+
* sessionId: 'session-123',
|
|
20
|
+
* message: { role: 'user', content: 'Hello!' }
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // Assemble context
|
|
24
|
+
* const result = await engine.assemble({
|
|
25
|
+
* sessionId: 'session-123',
|
|
26
|
+
* messages: []
|
|
27
|
+
* });
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
// Registry
|
|
31
|
+
export { ContextEngineRegistry, globalContextEngineRegistry, resolveContextEngine, } from "./registry.js";
|
|
32
|
+
// Legacy Engine
|
|
33
|
+
export { LegacyContextEngine, createLegacyContextEngine } from "./legacy.js";
|