@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.
- package/CHANGELOG.md +24 -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 +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/dist/skills/index.js
CHANGED
|
@@ -27,27 +27,27 @@
|
|
|
27
27
|
// ============================================================================
|
|
28
28
|
export {
|
|
29
29
|
// Errors
|
|
30
|
-
SkillError, } from
|
|
30
|
+
SkillError, } from "./types.js";
|
|
31
31
|
// ============================================================================
|
|
32
32
|
// Parser
|
|
33
33
|
// ============================================================================
|
|
34
|
-
export { parseSkill, parseSkillFile, parseSkills, isSkillFile, extractSkillId, parseYaml, parseSections, validateMetadata, extractContent, } from
|
|
34
|
+
export { parseSkill, parseSkillFile, parseSkills, isSkillFile, extractSkillId, parseYaml, parseSections, validateMetadata, extractContent, } from "./parser.js";
|
|
35
35
|
// ============================================================================
|
|
36
36
|
// Registry
|
|
37
37
|
// ============================================================================
|
|
38
|
-
export { SkillsRegistry, getRegistry, resetRegistry
|
|
38
|
+
export { SkillsRegistry, getRegistry, resetRegistry } from "./registry.js";
|
|
39
39
|
// ============================================================================
|
|
40
40
|
// Loader
|
|
41
41
|
// ============================================================================
|
|
42
|
-
export { SkillLoader, getLoader, resetLoader
|
|
42
|
+
export { SkillLoader, getLoader, resetLoader } from "./loader.js";
|
|
43
43
|
// ============================================================================
|
|
44
44
|
// Security
|
|
45
45
|
// ============================================================================
|
|
46
|
-
export { scanSkill, quickSecurityCheck, getSecuritySummary, formatFindings, PATTERNS, } from
|
|
46
|
+
export { scanSkill, quickSecurityCheck, getSecuritySummary, formatFindings, PATTERNS, } from "./security.js";
|
|
47
47
|
// ============================================================================
|
|
48
48
|
// Commands
|
|
49
49
|
// ============================================================================
|
|
50
|
-
export { registerSkillsCommands, listCommand, viewCommand, searchCommand, enableCommand, disableCommand, scanCommand, statsCommand, } from
|
|
50
|
+
export { registerSkillsCommands, listCommand, viewCommand, searchCommand, enableCommand, disableCommand, scanCommand, statsCommand, } from "./commands.js";
|
|
51
51
|
// ============================================================================
|
|
52
52
|
// Context Integration (Future Enhancement)
|
|
53
53
|
// ============================================================================
|
|
@@ -59,20 +59,17 @@ export { registerSkillsCommands, listCommand, viewCommand, searchCommand, enable
|
|
|
59
59
|
/**
|
|
60
60
|
* Current skills system version
|
|
61
61
|
*/
|
|
62
|
-
export const SKILLS_VERSION =
|
|
62
|
+
export const SKILLS_VERSION = "1.0.0";
|
|
63
63
|
/**
|
|
64
64
|
* Default skill directories
|
|
65
65
|
*/
|
|
66
|
-
export const DEFAULT_SKILL_PATHS = [
|
|
67
|
-
'./skills',
|
|
68
|
-
'~/.poolbot/skills',
|
|
69
|
-
];
|
|
66
|
+
export const DEFAULT_SKILL_PATHS = ["./skills", "~/.poolbot/skills"];
|
|
70
67
|
// ============================================================================
|
|
71
68
|
// Convenience Functions
|
|
72
69
|
// ============================================================================
|
|
73
|
-
import { getRegistry } from
|
|
74
|
-
import { getLoader } from
|
|
75
|
-
import { scanSkill } from
|
|
70
|
+
import { getRegistry } from "./registry.js";
|
|
71
|
+
import { getLoader } from "./loader.js";
|
|
72
|
+
import { scanSkill } from "./security.js";
|
|
76
73
|
/**
|
|
77
74
|
* Quick skill lookup by ID
|
|
78
75
|
*/
|
|
@@ -115,7 +112,7 @@ export async function getSkillForContext(skillId, config = {}) {
|
|
|
115
112
|
const fullConfig = {
|
|
116
113
|
skillIds: [skillId],
|
|
117
114
|
maxTokens: 4000,
|
|
118
|
-
disclosureLevel:
|
|
115
|
+
disclosureLevel: "summary",
|
|
119
116
|
includeLinkedFiles: false,
|
|
120
117
|
...config,
|
|
121
118
|
};
|
|
@@ -160,8 +157,8 @@ export async function initSkills(config) {
|
|
|
160
157
|
* Reset the skills system (useful for testing)
|
|
161
158
|
*/
|
|
162
159
|
export function resetSkills() {
|
|
163
|
-
const { resetRegistry } = require(
|
|
164
|
-
const { resetLoader } = require(
|
|
160
|
+
const { resetRegistry } = require("./registry.js");
|
|
161
|
+
const { resetLoader } = require("./loader.js");
|
|
165
162
|
resetRegistry();
|
|
166
163
|
resetLoader();
|
|
167
164
|
}
|
package/dist/skills/parser.js
CHANGED
|
@@ -52,7 +52,8 @@ function parseYaml(yaml) {
|
|
|
52
52
|
continue;
|
|
53
53
|
}
|
|
54
54
|
// Check for multiline value start (pipe | or greater-than >)
|
|
55
|
-
if (currentKey &&
|
|
55
|
+
if (currentKey &&
|
|
56
|
+
(trimmed === "|" || trimmed === ">" || trimmed.startsWith("|-") || trimmed.startsWith(">-"))) {
|
|
56
57
|
multilineValue = [];
|
|
57
58
|
continue;
|
|
58
59
|
}
|
|
@@ -60,7 +61,8 @@ function parseYaml(yaml) {
|
|
|
60
61
|
if (currentKey && multilineValue.length > 0) {
|
|
61
62
|
// Check if next line is still part of multiline (indented or empty)
|
|
62
63
|
const nextLine = lines[i + 1];
|
|
63
|
-
if (nextLine !== undefined &&
|
|
64
|
+
if (nextLine !== undefined &&
|
|
65
|
+
(nextLine.startsWith(" ") || nextLine.startsWith("\t") || nextLine.trim() === "")) {
|
|
64
66
|
multilineValue.push(line);
|
|
65
67
|
continue;
|
|
66
68
|
}
|
|
@@ -271,12 +273,8 @@ function validateMetadata(frontmatter, options = {}) {
|
|
|
271
273
|
createdAt,
|
|
272
274
|
updatedAt,
|
|
273
275
|
license: frontmatter.license ? String(frontmatter.license) : undefined,
|
|
274
|
-
repository: frontmatter.repository
|
|
275
|
-
|
|
276
|
-
: undefined,
|
|
277
|
-
documentation: frontmatter.documentation
|
|
278
|
-
? String(frontmatter.documentation)
|
|
279
|
-
: undefined,
|
|
276
|
+
repository: frontmatter.repository ? String(frontmatter.repository) : undefined,
|
|
277
|
+
documentation: frontmatter.documentation ? String(frontmatter.documentation) : undefined,
|
|
280
278
|
enabledByDefault: frontmatter.enabledByDefault === true,
|
|
281
279
|
};
|
|
282
280
|
}
|
|
@@ -288,25 +286,12 @@ function validateMetadata(frontmatter, options = {}) {
|
|
|
288
286
|
*/
|
|
289
287
|
function extractContent(sections) {
|
|
290
288
|
// Map section aliases
|
|
291
|
-
const quickstart = sections.get("quick start") ??
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
"";
|
|
298
|
-
const configuration = sections.get("configuration") ??
|
|
299
|
-
sections.get("config") ??
|
|
300
|
-
sections.get("setup");
|
|
301
|
-
const environment = sections.get("environment") ??
|
|
302
|
-
sections.get("env") ??
|
|
303
|
-
sections.get("environment variables");
|
|
304
|
-
const examples = sections.get("examples") ??
|
|
305
|
-
sections.get("example") ??
|
|
306
|
-
sections.get("example usage");
|
|
307
|
-
const api = sections.get("api") ??
|
|
308
|
-
sections.get("api reference") ??
|
|
309
|
-
sections.get("reference");
|
|
289
|
+
const quickstart = sections.get("quick start") ?? sections.get("quickstart") ?? sections.get("getting started");
|
|
290
|
+
const usage = sections.get("usage") ?? sections.get("how to use") ?? sections.get("using this skill") ?? "";
|
|
291
|
+
const configuration = sections.get("configuration") ?? sections.get("config") ?? sections.get("setup");
|
|
292
|
+
const environment = sections.get("environment") ?? sections.get("env") ?? sections.get("environment variables");
|
|
293
|
+
const examples = sections.get("examples") ?? sections.get("example") ?? sections.get("example usage");
|
|
294
|
+
const api = sections.get("api") ?? sections.get("api reference") ?? sections.get("reference");
|
|
310
295
|
const troubleshooting = sections.get("troubleshooting") ??
|
|
311
296
|
sections.get("troubleshoot") ??
|
|
312
297
|
sections.get("common issues");
|
package/dist/skills/registry.js
CHANGED
|
@@ -86,8 +86,7 @@ export class SkillsRegistry extends EventEmitter {
|
|
|
86
86
|
skill.securityReport = await scanSkill(skill);
|
|
87
87
|
skill.verification = skill.securityReport.result;
|
|
88
88
|
// Auto-disable if strict security and failed
|
|
89
|
-
if (this.config.strictSecurity &&
|
|
90
|
-
skill.verification === "failed") {
|
|
89
|
+
if (this.config.strictSecurity && skill.verification === "failed") {
|
|
91
90
|
skill.enabled = false;
|
|
92
91
|
skill.status = "disabled";
|
|
93
92
|
}
|
|
@@ -303,11 +302,9 @@ export class SkillsRegistry extends EventEmitter {
|
|
|
303
302
|
const byVerification = {};
|
|
304
303
|
for (const skill of skills) {
|
|
305
304
|
// Count by category
|
|
306
|
-
byCategory[skill.metadata.category] =
|
|
307
|
-
(byCategory[skill.metadata.category] || 0) + 1;
|
|
305
|
+
byCategory[skill.metadata.category] = (byCategory[skill.metadata.category] || 0) + 1;
|
|
308
306
|
// Count by verification
|
|
309
|
-
byVerification[skill.verification] =
|
|
310
|
-
(byVerification[skill.verification] || 0) + 1;
|
|
307
|
+
byVerification[skill.verification] = (byVerification[skill.verification] || 0) + 1;
|
|
311
308
|
}
|
|
312
309
|
return {
|
|
313
310
|
total: skills.length,
|
package/dist/skills/security.js
CHANGED
|
@@ -147,7 +147,7 @@ export async function scanSkill(skill) {
|
|
|
147
147
|
});
|
|
148
148
|
}
|
|
149
149
|
// Check against patterns
|
|
150
|
-
for (const { type, severity, pattern, description, remediation
|
|
150
|
+
for (const { type, severity, pattern, description, remediation } of PATTERNS) {
|
|
151
151
|
if (pattern.test(line)) {
|
|
152
152
|
findings.push({
|
|
153
153
|
type,
|
|
@@ -288,13 +288,7 @@ export function formatFindings(findings) {
|
|
|
288
288
|
for (const finding of findings) {
|
|
289
289
|
bySeverity[finding.severity].push(finding);
|
|
290
290
|
}
|
|
291
|
-
for (const severity of [
|
|
292
|
-
"critical",
|
|
293
|
-
"high",
|
|
294
|
-
"medium",
|
|
295
|
-
"low",
|
|
296
|
-
"info",
|
|
297
|
-
]) {
|
|
291
|
+
for (const severity of ["critical", "high", "medium", "low", "info"]) {
|
|
298
292
|
const group = bySeverity[severity];
|
|
299
293
|
if (group.length === 0)
|
|
300
294
|
continue;
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
// Agent Swarm Service - Multi-agent coordination and task distribution
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { createSubsystemLogger } from "../logging/subsystem.js";
|
|
5
|
+
const log = createSubsystemLogger("swarm");
|
|
6
|
+
const POOLBOT_DIR = join(process.env.HOME ?? "/tmp", ".poolbot");
|
|
7
|
+
const SWARM_STATE_FILE = join(POOLBOT_DIR, "swarm-state.json");
|
|
8
|
+
export class SwarmService {
|
|
9
|
+
swarms;
|
|
10
|
+
persistEnabled;
|
|
11
|
+
constructor(persistEnabled = true) {
|
|
12
|
+
this.swarms = new Map();
|
|
13
|
+
this.persistEnabled = persistEnabled;
|
|
14
|
+
this.loadFromDisk();
|
|
15
|
+
}
|
|
16
|
+
ensureDir() {
|
|
17
|
+
if (!existsSync(POOLBOT_DIR)) {
|
|
18
|
+
mkdirSync(POOLBOT_DIR, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
loadFromDisk() {
|
|
22
|
+
if (!this.persistEnabled)
|
|
23
|
+
return;
|
|
24
|
+
try {
|
|
25
|
+
this.ensureDir();
|
|
26
|
+
if (existsSync(SWARM_STATE_FILE)) {
|
|
27
|
+
const data = readFileSync(SWARM_STATE_FILE, "utf-8");
|
|
28
|
+
const parsed = JSON.parse(data);
|
|
29
|
+
for (const [id, swarm] of Object.entries(parsed)) {
|
|
30
|
+
this.swarms.set(id, swarm);
|
|
31
|
+
}
|
|
32
|
+
log.info(`Loaded ${this.swarms.size} swarms from disk`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
log.error(`Failed to load swarm state: ${String(error)}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
saveToDisk() {
|
|
40
|
+
if (!this.persistEnabled)
|
|
41
|
+
return;
|
|
42
|
+
try {
|
|
43
|
+
this.ensureDir();
|
|
44
|
+
const data = {};
|
|
45
|
+
for (const [id, swarm] of this.swarms) {
|
|
46
|
+
data[id] = swarm;
|
|
47
|
+
}
|
|
48
|
+
writeFileSync(SWARM_STATE_FILE, JSON.stringify(data, null, 2));
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
log.error(`Failed to save swarm state: ${String(error)}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async list() {
|
|
55
|
+
const swarmList = Array.from(this.swarms.values()).map((swarm) => ({
|
|
56
|
+
id: swarm.id,
|
|
57
|
+
name: swarm.name,
|
|
58
|
+
status: swarm.status,
|
|
59
|
+
members: swarm.members.length,
|
|
60
|
+
tasks: swarm.tasks.length,
|
|
61
|
+
}));
|
|
62
|
+
return { swarms: swarmList };
|
|
63
|
+
}
|
|
64
|
+
async getStatus(swarmId) {
|
|
65
|
+
const swarm = this.swarms.get(swarmId);
|
|
66
|
+
return { swarm: swarm ?? null };
|
|
67
|
+
}
|
|
68
|
+
async create(params) {
|
|
69
|
+
const id = `swarm-${Date.now()}`;
|
|
70
|
+
const now = Date.now();
|
|
71
|
+
const swarm = {
|
|
72
|
+
id,
|
|
73
|
+
name: params.name,
|
|
74
|
+
description: params.description,
|
|
75
|
+
createdAt: now,
|
|
76
|
+
orchestratorAgentId: params.orchestratorAgentId,
|
|
77
|
+
members: [],
|
|
78
|
+
tasks: [],
|
|
79
|
+
strategy: params.strategy,
|
|
80
|
+
status: "active",
|
|
81
|
+
};
|
|
82
|
+
this.swarms.set(id, swarm);
|
|
83
|
+
this.saveToDisk();
|
|
84
|
+
log.info(`Created swarm: ${id} (${params.name})`);
|
|
85
|
+
return {
|
|
86
|
+
id,
|
|
87
|
+
name: params.name,
|
|
88
|
+
description: params.description,
|
|
89
|
+
strategy: params.strategy,
|
|
90
|
+
orchestratorAgentId: params.orchestratorAgentId,
|
|
91
|
+
createdAt: now,
|
|
92
|
+
status: "active",
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
async delete(swarmId) {
|
|
96
|
+
const deleted = this.swarms.delete(swarmId);
|
|
97
|
+
if (deleted) {
|
|
98
|
+
this.saveToDisk();
|
|
99
|
+
log.info(`Deleted swarm: ${swarmId}`);
|
|
100
|
+
}
|
|
101
|
+
return deleted;
|
|
102
|
+
}
|
|
103
|
+
async addMember(swarmId, member) {
|
|
104
|
+
const swarm = this.swarms.get(swarmId);
|
|
105
|
+
if (!swarm)
|
|
106
|
+
return false;
|
|
107
|
+
// Check if member already exists
|
|
108
|
+
const existingIndex = swarm.members.findIndex((m) => m.agentId === member.agentId);
|
|
109
|
+
if (existingIndex >= 0) {
|
|
110
|
+
swarm.members[existingIndex] = member;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
swarm.members.push(member);
|
|
114
|
+
}
|
|
115
|
+
this.saveToDisk();
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
async removeMember(swarmId, agentId) {
|
|
119
|
+
const swarm = this.swarms.get(swarmId);
|
|
120
|
+
if (!swarm)
|
|
121
|
+
return false;
|
|
122
|
+
const initialLength = swarm.members.length;
|
|
123
|
+
swarm.members = swarm.members.filter((m) => m.agentId !== agentId);
|
|
124
|
+
const removed = swarm.members.length < initialLength;
|
|
125
|
+
if (removed) {
|
|
126
|
+
this.saveToDisk();
|
|
127
|
+
}
|
|
128
|
+
return removed;
|
|
129
|
+
}
|
|
130
|
+
async addTask(swarmId, task) {
|
|
131
|
+
const swarm = this.swarms.get(swarmId);
|
|
132
|
+
if (!swarm)
|
|
133
|
+
return false;
|
|
134
|
+
swarm.tasks.push(task);
|
|
135
|
+
this.saveToDisk();
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
async removeTask(swarmId, taskId) {
|
|
139
|
+
const swarm = this.swarms.get(swarmId);
|
|
140
|
+
if (!swarm)
|
|
141
|
+
return false;
|
|
142
|
+
const initialLength = swarm.tasks.length;
|
|
143
|
+
swarm.tasks = swarm.tasks.filter((t) => t.id !== taskId);
|
|
144
|
+
const removed = swarm.tasks.length < initialLength;
|
|
145
|
+
if (removed) {
|
|
146
|
+
this.saveToDisk();
|
|
147
|
+
}
|
|
148
|
+
return removed;
|
|
149
|
+
}
|
|
150
|
+
async updateTaskStatus(swarmId, taskId, status, assignedTo) {
|
|
151
|
+
const swarm = this.swarms.get(swarmId);
|
|
152
|
+
if (!swarm)
|
|
153
|
+
return false;
|
|
154
|
+
const task = swarm.tasks.find((t) => t.id === taskId);
|
|
155
|
+
if (!task)
|
|
156
|
+
return false;
|
|
157
|
+
task.status = status;
|
|
158
|
+
if (assignedTo)
|
|
159
|
+
task.assignedTo = assignedTo;
|
|
160
|
+
if (status === "in_progress" && !task.startedAt)
|
|
161
|
+
task.startedAt = Date.now();
|
|
162
|
+
if (status === "completed" && !task.completedAt)
|
|
163
|
+
task.completedAt = Date.now();
|
|
164
|
+
this.saveToDisk();
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
async updateMemberStatus(swarmId, agentId, status, currentTaskId) {
|
|
168
|
+
const swarm = this.swarms.get(swarmId);
|
|
169
|
+
if (!swarm)
|
|
170
|
+
return false;
|
|
171
|
+
const member = swarm.members.find((m) => m.agentId === agentId);
|
|
172
|
+
if (!member)
|
|
173
|
+
return false;
|
|
174
|
+
member.status = status;
|
|
175
|
+
member.lastHeartbeatAt = Date.now();
|
|
176
|
+
if (currentTaskId !== undefined)
|
|
177
|
+
member.currentTaskId = currentTaskId;
|
|
178
|
+
if (status === "working" && currentTaskId) {
|
|
179
|
+
member.completedTasks++;
|
|
180
|
+
}
|
|
181
|
+
this.saveToDisk();
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
async updateSwarmStatus(swarmId, status) {
|
|
185
|
+
const swarm = this.swarms.get(swarmId);
|
|
186
|
+
if (!swarm)
|
|
187
|
+
return false;
|
|
188
|
+
swarm.status = status;
|
|
189
|
+
this.saveToDisk();
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
// Get or create default swarm for demo purposes
|
|
193
|
+
async getOrCreateDefaultSwarm() {
|
|
194
|
+
const defaultId = "swarm-default";
|
|
195
|
+
let swarm = this.swarms.get(defaultId);
|
|
196
|
+
if (!swarm) {
|
|
197
|
+
const now = Date.now();
|
|
198
|
+
swarm = {
|
|
199
|
+
id: defaultId,
|
|
200
|
+
name: "Default Swarm",
|
|
201
|
+
description: "Default agent swarm for task coordination",
|
|
202
|
+
createdAt: now,
|
|
203
|
+
orchestratorAgentId: "main",
|
|
204
|
+
members: [
|
|
205
|
+
{
|
|
206
|
+
agentId: "agent-1",
|
|
207
|
+
sessionKey: "swarm/agent-1",
|
|
208
|
+
status: "idle",
|
|
209
|
+
capabilities: ["typescript", "review", "coding"],
|
|
210
|
+
joinedAt: now,
|
|
211
|
+
lastHeartbeatAt: now,
|
|
212
|
+
completedTasks: 0,
|
|
213
|
+
failedTasks: 0,
|
|
214
|
+
},
|
|
215
|
+
],
|
|
216
|
+
tasks: [],
|
|
217
|
+
strategy: "capability_match",
|
|
218
|
+
status: "active",
|
|
219
|
+
};
|
|
220
|
+
this.swarms.set(defaultId, swarm);
|
|
221
|
+
this.saveToDisk();
|
|
222
|
+
}
|
|
223
|
+
return swarm;
|
|
224
|
+
}
|
|
225
|
+
// Get swarm statistics
|
|
226
|
+
async getStats(swarmId) {
|
|
227
|
+
const swarm = this.swarms.get(swarmId);
|
|
228
|
+
if (!swarm)
|
|
229
|
+
return null;
|
|
230
|
+
const activeMembers = swarm.members.filter((m) => m.status === "idle" || m.status === "working").length;
|
|
231
|
+
const completedTasks = swarm.tasks.filter((t) => t.status === "completed").length;
|
|
232
|
+
const failedTasks = swarm.tasks.filter((t) => t.status === "failed").length;
|
|
233
|
+
const pendingTasks = swarm.tasks.filter((t) => t.status === "pending").length;
|
|
234
|
+
const inProgressTasks = swarm.tasks.filter((t) => t.status === "in_progress").length;
|
|
235
|
+
return {
|
|
236
|
+
totalMembers: swarm.members.length,
|
|
237
|
+
activeMembers,
|
|
238
|
+
totalTasks: swarm.tasks.length,
|
|
239
|
+
completedTasks,
|
|
240
|
+
failedTasks,
|
|
241
|
+
pendingTasks,
|
|
242
|
+
inProgressTasks,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
// Singleton instance
|
|
247
|
+
export const swarmService = new SwarmService();
|