@timmeck/brain-core 2.36.11 → 2.36.14
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/dist/cross-brain/__tests__/borg-sync-engine.test.d.ts +1 -0
- package/dist/cross-brain/__tests__/borg-sync-engine.test.js +240 -0
- package/dist/cross-brain/__tests__/borg-sync-engine.test.js.map +1 -0
- package/dist/cross-brain/borg-sync-engine.d.ts +62 -0
- package/dist/cross-brain/borg-sync-engine.js +215 -0
- package/dist/cross-brain/borg-sync-engine.js.map +1 -0
- package/dist/cross-brain/borg-types.d.ts +37 -0
- package/dist/cross-brain/borg-types.js +9 -0
- package/dist/cross-brain/borg-types.js.map +1 -0
- package/dist/embeddings/engine.js +2 -1
- package/dist/embeddings/engine.js.map +1 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/llm/__tests__/anthropic-provider.test.d.ts +1 -0
- package/dist/llm/__tests__/anthropic-provider.test.js +121 -0
- package/dist/llm/__tests__/anthropic-provider.test.js.map +1 -0
- package/dist/llm/__tests__/llm-service.test.js +181 -40
- package/dist/llm/__tests__/llm-service.test.js.map +1 -1
- package/dist/llm/__tests__/ollama-embedding.test.d.ts +1 -0
- package/dist/llm/__tests__/ollama-embedding.test.js +128 -0
- package/dist/llm/__tests__/ollama-embedding.test.js.map +1 -0
- package/dist/llm/__tests__/ollama-provider.test.d.ts +1 -0
- package/dist/llm/__tests__/ollama-provider.test.js +213 -0
- package/dist/llm/__tests__/ollama-provider.test.js.map +1 -0
- package/dist/llm/__tests__/provider.test.d.ts +1 -0
- package/dist/llm/__tests__/provider.test.js +126 -0
- package/dist/llm/__tests__/provider.test.js.map +1 -0
- package/dist/llm/anthropic-provider.d.ts +41 -0
- package/dist/llm/anthropic-provider.js +86 -0
- package/dist/llm/anthropic-provider.js.map +1 -0
- package/dist/llm/index.d.ts +9 -1
- package/dist/llm/index.js +4 -0
- package/dist/llm/index.js.map +1 -1
- package/dist/llm/llm-service.d.ts +55 -7
- package/dist/llm/llm-service.js +184 -82
- package/dist/llm/llm-service.js.map +1 -1
- package/dist/llm/ollama-embedding.d.ts +46 -0
- package/dist/llm/ollama-embedding.js +93 -0
- package/dist/llm/ollama-embedding.js.map +1 -0
- package/dist/llm/ollama-provider.d.ts +80 -0
- package/dist/llm/ollama-provider.js +178 -0
- package/dist/llm/ollama-provider.js.map +1 -0
- package/dist/llm/provider.d.ts +120 -0
- package/dist/llm/provider.js +104 -0
- package/dist/llm/provider.js.map +1 -0
- package/dist/missions/mission-engine.d.ts +4 -0
- package/dist/missions/mission-engine.js +30 -8
- package/dist/missions/mission-engine.js.map +1 -1
- package/dist/notifications/__tests__/notification-service.test.d.ts +1 -0
- package/dist/notifications/__tests__/notification-service.test.js +176 -0
- package/dist/notifications/__tests__/notification-service.test.js.map +1 -0
- package/dist/notifications/discord-provider.d.ts +30 -0
- package/dist/notifications/discord-provider.js +89 -0
- package/dist/notifications/discord-provider.js.map +1 -0
- package/dist/notifications/email-provider.d.ts +41 -0
- package/dist/notifications/email-provider.js +101 -0
- package/dist/notifications/email-provider.js.map +1 -0
- package/dist/notifications/index.d.ts +8 -0
- package/dist/notifications/index.js +5 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/notifications/notification-provider.d.ts +75 -0
- package/dist/notifications/notification-provider.js +47 -0
- package/dist/notifications/notification-provider.js.map +1 -0
- package/dist/notifications/notification-service.d.ts +85 -0
- package/dist/notifications/notification-service.js +184 -0
- package/dist/notifications/notification-service.js.map +1 -0
- package/dist/notifications/telegram-provider.d.ts +30 -0
- package/dist/notifications/telegram-provider.js +78 -0
- package/dist/notifications/telegram-provider.js.map +1 -0
- package/dist/plugin/__tests__/plugin-registry.test.d.ts +1 -0
- package/dist/plugin/__tests__/plugin-registry.test.js +166 -0
- package/dist/plugin/__tests__/plugin-registry.test.js.map +1 -0
- package/dist/plugin/plugin-registry.d.ts +38 -0
- package/dist/plugin/plugin-registry.js +185 -0
- package/dist/plugin/plugin-registry.js.map +1 -0
- package/dist/plugin/types.d.ts +59 -0
- package/dist/plugin/types.js +2 -0
- package/dist/plugin/types.js.map +1 -0
- package/dist/research/adapters/__tests__/web-adapters.test.d.ts +1 -0
- package/dist/research/adapters/__tests__/web-adapters.test.js +106 -0
- package/dist/research/adapters/__tests__/web-adapters.test.js.map +1 -0
- package/dist/research/adapters/firecrawl-adapter.d.ts +57 -0
- package/dist/research/adapters/firecrawl-adapter.js +137 -0
- package/dist/research/adapters/firecrawl-adapter.js.map +1 -0
- package/dist/research/adapters/index.d.ts +3 -0
- package/dist/research/adapters/index.js +2 -0
- package/dist/research/adapters/index.js.map +1 -1
- package/dist/research/adapters/playwright-adapter.d.ts +54 -0
- package/dist/research/adapters/playwright-adapter.js +130 -0
- package/dist/research/adapters/playwright-adapter.js.map +1 -0
- package/dist/research/research-orchestrator.d.ts +3 -0
- package/dist/research/research-orchestrator.js +19 -1
- package/dist/research/research-orchestrator.js.map +1 -1
- package/dist/self-modification/self-modification-engine.js +28 -4
- package/dist/self-modification/self-modification-engine.js.map +1 -1
- package/dist/techradar/__tests__/techradar-engine.test.d.ts +1 -0
- package/dist/techradar/__tests__/techradar-engine.test.js +246 -0
- package/dist/techradar/__tests__/techradar-engine.test.js.map +1 -0
- package/dist/techradar/daily-digest.d.ts +18 -0
- package/dist/techradar/daily-digest.js +100 -0
- package/dist/techradar/daily-digest.js.map +1 -0
- package/dist/techradar/index.d.ts +5 -0
- package/dist/techradar/index.js +5 -0
- package/dist/techradar/index.js.map +1 -0
- package/dist/techradar/relevance-scorer.d.ts +29 -0
- package/dist/techradar/relevance-scorer.js +139 -0
- package/dist/techradar/relevance-scorer.js.map +1 -0
- package/dist/techradar/repo-watcher.d.ts +24 -0
- package/dist/techradar/repo-watcher.js +87 -0
- package/dist/techradar/repo-watcher.js.map +1 -0
- package/dist/techradar/techradar-engine.d.ts +69 -0
- package/dist/techradar/techradar-engine.js +382 -0
- package/dist/techradar/techradar-engine.js.map +1 -0
- package/dist/techradar/types.d.ts +87 -0
- package/dist/techradar/types.js +5 -0
- package/dist/techradar/types.js.map +1 -0
- package/dist/watchdog/__tests__/watchdog-service.test.d.ts +1 -0
- package/dist/watchdog/__tests__/watchdog-service.test.js +113 -0
- package/dist/watchdog/__tests__/watchdog-service.test.js.map +1 -0
- package/dist/watchdog/watchdog-service.d.ts +60 -0
- package/dist/watchdog/watchdog-service.js +275 -0
- package/dist/watchdog/watchdog-service.js.map +1 -0
- package/dist/watchdog/windows-service.d.ts +39 -0
- package/dist/watchdog/windows-service.js +179 -0
- package/dist/watchdog/windows-service.js.map +1 -0
- package/package.json +20 -2
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram Notification Provider — Bot Messages via grammy
|
|
3
|
+
*
|
|
4
|
+
* Einrichten:
|
|
5
|
+
* 1. Bot erstellen: https://t.me/BotFather → /newbot → Token kopieren
|
|
6
|
+
* 2. Chat ID finden: Nachricht an Bot senden, dann
|
|
7
|
+
* https://api.telegram.org/bot<TOKEN>/getUpdates → chat.id
|
|
8
|
+
* 3. In .env:
|
|
9
|
+
* TELEGRAM_BOT_TOKEN=...
|
|
10
|
+
* TELEGRAM_CHAT_ID=...
|
|
11
|
+
*/
|
|
12
|
+
import { getLogger } from '../utils/logger.js';
|
|
13
|
+
const PRIORITY_EMOJI = {
|
|
14
|
+
critical: '🔴',
|
|
15
|
+
high: '🟠',
|
|
16
|
+
medium: '🟡',
|
|
17
|
+
low: '🟢',
|
|
18
|
+
};
|
|
19
|
+
export class TelegramProvider {
|
|
20
|
+
name = 'telegram';
|
|
21
|
+
botToken;
|
|
22
|
+
chatId;
|
|
23
|
+
log = getLogger();
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
25
|
+
bot = null;
|
|
26
|
+
sentCount = 0;
|
|
27
|
+
constructor(config = {}) {
|
|
28
|
+
this.botToken = config.botToken ?? process.env.TELEGRAM_BOT_TOKEN ?? null;
|
|
29
|
+
this.chatId = config.chatId ?? process.env.TELEGRAM_CHAT_ID ?? null;
|
|
30
|
+
}
|
|
31
|
+
async isAvailable() {
|
|
32
|
+
if (!this.botToken || !this.chatId)
|
|
33
|
+
return false;
|
|
34
|
+
try {
|
|
35
|
+
await this.getBot();
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async send(notification) {
|
|
43
|
+
try {
|
|
44
|
+
const bot = await this.getBot();
|
|
45
|
+
const emoji = PRIORITY_EMOJI[notification.priority] ?? '⚪';
|
|
46
|
+
const text = [
|
|
47
|
+
`${emoji} *${this.escapeMarkdown(notification.title)}*`,
|
|
48
|
+
'',
|
|
49
|
+
this.escapeMarkdown(notification.message),
|
|
50
|
+
'',
|
|
51
|
+
`_${notification.event}_`,
|
|
52
|
+
].join('\n');
|
|
53
|
+
await bot.api.sendMessage(this.chatId, text, { parse_mode: 'MarkdownV2' });
|
|
54
|
+
this.sentCount++;
|
|
55
|
+
return { provider: this.name, success: true };
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
return { provider: this.name, success: false, error: err.message };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async shutdown() {
|
|
62
|
+
this.bot = null;
|
|
63
|
+
}
|
|
64
|
+
escapeMarkdown(text) {
|
|
65
|
+
return text.replace(/[_*[\]()~`>#+\-=|{}.!\\]/g, '\\$&');
|
|
66
|
+
}
|
|
67
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
68
|
+
async getBot() {
|
|
69
|
+
if (this.bot)
|
|
70
|
+
return this.bot;
|
|
71
|
+
// Dynamic import — grammy is optional (variable path avoids TS module resolution)
|
|
72
|
+
const modulePath = 'grammy';
|
|
73
|
+
const { Bot } = await import(/* webpackIgnore: true */ modulePath);
|
|
74
|
+
this.bot = new Bot(this.botToken);
|
|
75
|
+
return this.bot;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=telegram-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telegram-provider.js","sourceRoot":"","sources":["../../src/notifications/telegram-provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import { PluginRegistry } from '../plugin-registry.js';
|
|
6
|
+
function createMockContext(name) {
|
|
7
|
+
return {
|
|
8
|
+
dataDir: path.join(os.tmpdir(), 'brain-test-plugins', name),
|
|
9
|
+
log: {
|
|
10
|
+
info: vi.fn(),
|
|
11
|
+
warn: vi.fn(),
|
|
12
|
+
error: vi.fn(),
|
|
13
|
+
debug: vi.fn(),
|
|
14
|
+
},
|
|
15
|
+
callBrain: vi.fn().mockResolvedValue({}),
|
|
16
|
+
notify: vi.fn().mockResolvedValue(undefined),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
describe('PluginRegistry', () => {
|
|
20
|
+
let tmpDir;
|
|
21
|
+
let registry;
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
tmpDir = path.join(os.tmpdir(), `brain-plugin-test-${Date.now()}`);
|
|
24
|
+
fs.mkdirSync(tmpDir, { recursive: true });
|
|
25
|
+
registry = new PluginRegistry(tmpDir);
|
|
26
|
+
});
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
29
|
+
});
|
|
30
|
+
it('creates plugin directory if it does not exist', () => {
|
|
31
|
+
const newDir = path.join(tmpDir, 'subdir');
|
|
32
|
+
const reg = new PluginRegistry(newDir);
|
|
33
|
+
expect(fs.existsSync(newDir)).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
it('starts with no plugins', () => {
|
|
36
|
+
expect(registry.size).toBe(0);
|
|
37
|
+
expect(registry.list()).toEqual([]);
|
|
38
|
+
expect(registry.getRoutes()).toEqual([]);
|
|
39
|
+
expect(registry.getTools()).toEqual([]);
|
|
40
|
+
});
|
|
41
|
+
it('has() returns false for unknown plugin', () => {
|
|
42
|
+
expect(registry.has('nonexistent')).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
it('get() returns null for unknown plugin', () => {
|
|
45
|
+
expect(registry.get('nonexistent')).toBeNull();
|
|
46
|
+
});
|
|
47
|
+
it('loadAll with empty directory loads nothing', async () => {
|
|
48
|
+
await registry.loadAll((name) => createMockContext(name));
|
|
49
|
+
expect(registry.size).toBe(0);
|
|
50
|
+
});
|
|
51
|
+
it('loadAll skips directories without package.json', async () => {
|
|
52
|
+
fs.mkdirSync(path.join(tmpDir, 'empty-dir'));
|
|
53
|
+
await registry.loadAll((name) => createMockContext(name));
|
|
54
|
+
expect(registry.size).toBe(0);
|
|
55
|
+
});
|
|
56
|
+
it('loadAll skips packages without brainPlugin flag', async () => {
|
|
57
|
+
const pluginDir = path.join(tmpDir, 'normal-pkg');
|
|
58
|
+
fs.mkdirSync(pluginDir);
|
|
59
|
+
fs.writeFileSync(path.join(pluginDir, 'package.json'), JSON.stringify({
|
|
60
|
+
name: 'normal-pkg',
|
|
61
|
+
version: '1.0.0',
|
|
62
|
+
}));
|
|
63
|
+
fs.writeFileSync(path.join(pluginDir, 'index.js'), 'module.exports = {}');
|
|
64
|
+
await registry.loadAll((name) => createMockContext(name));
|
|
65
|
+
expect(registry.size).toBe(0);
|
|
66
|
+
});
|
|
67
|
+
it('unloadPlugin returns false for unknown plugin', async () => {
|
|
68
|
+
expect(await registry.unloadPlugin('nonexistent')).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
it('runCycle does nothing with no plugins', async () => {
|
|
71
|
+
await expect(registry.runCycle(1)).resolves.toBeUndefined();
|
|
72
|
+
});
|
|
73
|
+
it('list returns empty array when no plugins loaded', () => {
|
|
74
|
+
expect(registry.list()).toEqual([]);
|
|
75
|
+
});
|
|
76
|
+
it('getRoutes returns plugin-prefixed routes', () => {
|
|
77
|
+
// Manually inject a plugin for testing
|
|
78
|
+
const plugin = {
|
|
79
|
+
name: 'test-plugin',
|
|
80
|
+
version: '1.0.0',
|
|
81
|
+
routes: [
|
|
82
|
+
{ method: 'test.hello', handler: () => ({ hello: 'world' }) },
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
// Access internal map (testing only)
|
|
86
|
+
registry.plugins.set('test-plugin', {
|
|
87
|
+
plugin,
|
|
88
|
+
path: '/fake',
|
|
89
|
+
loadedAt: new Date().toISOString(),
|
|
90
|
+
error: null,
|
|
91
|
+
});
|
|
92
|
+
const routes = registry.getRoutes();
|
|
93
|
+
expect(routes).toHaveLength(1);
|
|
94
|
+
expect(routes[0].plugin).toBe('test-plugin');
|
|
95
|
+
expect(routes[0].method).toBe('test.hello');
|
|
96
|
+
});
|
|
97
|
+
it('getTools returns plugin-prefixed tools', () => {
|
|
98
|
+
const plugin = {
|
|
99
|
+
name: 'test-plugin',
|
|
100
|
+
version: '1.0.0',
|
|
101
|
+
tools: [
|
|
102
|
+
{ name: 'test_tool', description: 'A test', schema: {}, handler: () => 'ok' },
|
|
103
|
+
],
|
|
104
|
+
};
|
|
105
|
+
registry.plugins.set('test-plugin', {
|
|
106
|
+
plugin,
|
|
107
|
+
path: '/fake',
|
|
108
|
+
loadedAt: new Date().toISOString(),
|
|
109
|
+
error: null,
|
|
110
|
+
});
|
|
111
|
+
const tools = registry.getTools();
|
|
112
|
+
expect(tools).toHaveLength(1);
|
|
113
|
+
expect(tools[0].plugin).toBe('test-plugin');
|
|
114
|
+
expect(tools[0].name).toBe('test_tool');
|
|
115
|
+
});
|
|
116
|
+
it('runCycle calls onCycle for loaded plugins', async () => {
|
|
117
|
+
const onCycle = vi.fn();
|
|
118
|
+
const plugin = {
|
|
119
|
+
name: 'cycle-plugin',
|
|
120
|
+
version: '1.0.0',
|
|
121
|
+
onCycle,
|
|
122
|
+
};
|
|
123
|
+
registry.plugins.set('cycle-plugin', {
|
|
124
|
+
plugin,
|
|
125
|
+
path: '/fake',
|
|
126
|
+
loadedAt: new Date().toISOString(),
|
|
127
|
+
error: null,
|
|
128
|
+
});
|
|
129
|
+
await registry.runCycle(42);
|
|
130
|
+
expect(onCycle).toHaveBeenCalledWith(42);
|
|
131
|
+
});
|
|
132
|
+
it('runCycle catches errors from plugins', async () => {
|
|
133
|
+
const plugin = {
|
|
134
|
+
name: 'bad-plugin',
|
|
135
|
+
version: '1.0.0',
|
|
136
|
+
onCycle: () => { throw new Error('boom'); },
|
|
137
|
+
};
|
|
138
|
+
registry.plugins.set('bad-plugin', {
|
|
139
|
+
plugin,
|
|
140
|
+
path: '/fake',
|
|
141
|
+
loadedAt: new Date().toISOString(),
|
|
142
|
+
error: null,
|
|
143
|
+
});
|
|
144
|
+
// Should not throw
|
|
145
|
+
await expect(registry.runCycle(1)).resolves.toBeUndefined();
|
|
146
|
+
});
|
|
147
|
+
it('unloadPlugin calls onUnload and removes', async () => {
|
|
148
|
+
const onUnload = vi.fn();
|
|
149
|
+
const plugin = {
|
|
150
|
+
name: 'unload-test',
|
|
151
|
+
version: '1.0.0',
|
|
152
|
+
onUnload,
|
|
153
|
+
};
|
|
154
|
+
registry.plugins.set('unload-test', {
|
|
155
|
+
plugin,
|
|
156
|
+
path: '/fake',
|
|
157
|
+
loadedAt: new Date().toISOString(),
|
|
158
|
+
error: null,
|
|
159
|
+
});
|
|
160
|
+
expect(registry.has('unload-test')).toBe(true);
|
|
161
|
+
await registry.unloadPlugin('unload-test');
|
|
162
|
+
expect(registry.has('unload-test')).toBe(false);
|
|
163
|
+
expect(onUnload).toHaveBeenCalled();
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
//# sourceMappingURL=plugin-registry.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-registry.test.js","sourceRoot":"","sources":["../../../src/plugin/__tests__/plugin-registry.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { BrainPlugin, PluginContext, PluginRecord, PluginRouteDefinition, PluginToolDefinition } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* PluginRegistry — discovers, loads, and manages Brain plugins.
|
|
4
|
+
* Plugins are npm packages with `brainPlugin: true` in package.json,
|
|
5
|
+
* or local directories under ~/.brain/plugins/.
|
|
6
|
+
*/
|
|
7
|
+
export declare class PluginRegistry {
|
|
8
|
+
private plugins;
|
|
9
|
+
private logger;
|
|
10
|
+
private pluginDir;
|
|
11
|
+
constructor(pluginDir: string);
|
|
12
|
+
/** Load all plugins from the plugin directory. */
|
|
13
|
+
loadAll(contextFactory: (name: string) => PluginContext): Promise<void>;
|
|
14
|
+
/** Load a single plugin by directory path. */
|
|
15
|
+
loadPlugin(pluginPath: string, context: PluginContext): Promise<boolean>;
|
|
16
|
+
/** Unload a plugin by name. */
|
|
17
|
+
unloadPlugin(name: string): Promise<boolean>;
|
|
18
|
+
/** Run onCycle for all loaded plugins. */
|
|
19
|
+
runCycle(cycleCount: number): Promise<void>;
|
|
20
|
+
/** Get all registered IPC routes from plugins. */
|
|
21
|
+
getRoutes(): Array<{
|
|
22
|
+
plugin: string;
|
|
23
|
+
} & PluginRouteDefinition>;
|
|
24
|
+
/** Get all registered MCP tools from plugins. */
|
|
25
|
+
getTools(): Array<{
|
|
26
|
+
plugin: string;
|
|
27
|
+
} & PluginToolDefinition>;
|
|
28
|
+
/** List all plugins (loaded + discovered). */
|
|
29
|
+
list(): PluginRecord[];
|
|
30
|
+
/** Get a loaded plugin by name. */
|
|
31
|
+
get(name: string): BrainPlugin | null;
|
|
32
|
+
/** Check if a plugin is loaded. */
|
|
33
|
+
has(name: string): boolean;
|
|
34
|
+
/** Get plugin count. */
|
|
35
|
+
get size(): number;
|
|
36
|
+
/** Discover plugins in the plugin directory. */
|
|
37
|
+
private discoverPlugins;
|
|
38
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { getLogger } from '../utils/logger.js';
|
|
4
|
+
/**
|
|
5
|
+
* PluginRegistry — discovers, loads, and manages Brain plugins.
|
|
6
|
+
* Plugins are npm packages with `brainPlugin: true` in package.json,
|
|
7
|
+
* or local directories under ~/.brain/plugins/.
|
|
8
|
+
*/
|
|
9
|
+
export class PluginRegistry {
|
|
10
|
+
plugins = new Map();
|
|
11
|
+
logger = getLogger();
|
|
12
|
+
pluginDir;
|
|
13
|
+
constructor(pluginDir) {
|
|
14
|
+
this.pluginDir = pluginDir;
|
|
15
|
+
fs.mkdirSync(pluginDir, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
/** Load all plugins from the plugin directory. */
|
|
18
|
+
async loadAll(contextFactory) {
|
|
19
|
+
const entries = this.discoverPlugins();
|
|
20
|
+
for (const entry of entries) {
|
|
21
|
+
try {
|
|
22
|
+
await this.loadPlugin(entry.path, contextFactory(entry.name));
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
this.logger.error(`Failed to load plugin ${entry.name}: ${err.message}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
this.logger.info(`Loaded ${this.plugins.size}/${entries.length} plugins`);
|
|
29
|
+
}
|
|
30
|
+
/** Load a single plugin by directory path. */
|
|
31
|
+
async loadPlugin(pluginPath, context) {
|
|
32
|
+
try {
|
|
33
|
+
const manifestPath = path.join(pluginPath, 'package.json');
|
|
34
|
+
if (!fs.existsSync(manifestPath)) {
|
|
35
|
+
this.logger.warn(`No package.json in ${pluginPath}`);
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
39
|
+
if (!manifest.brainPlugin) {
|
|
40
|
+
this.logger.debug(`${manifest.name} is not a brain plugin (missing brainPlugin flag)`);
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
const mainFile = path.join(pluginPath, manifest.main || 'index.js');
|
|
44
|
+
if (!fs.existsSync(mainFile)) {
|
|
45
|
+
this.logger.error(`Plugin entry point not found: ${mainFile}`);
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
// Dynamic import
|
|
49
|
+
const mod = await import(`file://${mainFile.replace(/\\/g, '/')}`);
|
|
50
|
+
const plugin = mod.default ?? mod;
|
|
51
|
+
if (!plugin.name || !plugin.version) {
|
|
52
|
+
this.logger.error(`Plugin at ${pluginPath} missing name or version`);
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
// Prevent duplicate
|
|
56
|
+
if (this.plugins.has(plugin.name)) {
|
|
57
|
+
this.logger.warn(`Plugin ${plugin.name} already loaded, skipping duplicate`);
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
// Lifecycle: onLoad
|
|
61
|
+
if (plugin.onLoad) {
|
|
62
|
+
await plugin.onLoad(context);
|
|
63
|
+
}
|
|
64
|
+
this.plugins.set(plugin.name, {
|
|
65
|
+
plugin,
|
|
66
|
+
path: pluginPath,
|
|
67
|
+
loadedAt: new Date().toISOString(),
|
|
68
|
+
error: null,
|
|
69
|
+
});
|
|
70
|
+
this.logger.info(`Plugin loaded: ${plugin.name} v${plugin.version}`);
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
this.logger.error(`Plugin load failed at ${pluginPath}: ${err.message}`);
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Unload a plugin by name. */
|
|
79
|
+
async unloadPlugin(name) {
|
|
80
|
+
const loaded = this.plugins.get(name);
|
|
81
|
+
if (!loaded)
|
|
82
|
+
return false;
|
|
83
|
+
try {
|
|
84
|
+
if (loaded.plugin.onUnload) {
|
|
85
|
+
await loaded.plugin.onUnload();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
this.logger.error(`Error during ${name} unload: ${err.message}`);
|
|
90
|
+
}
|
|
91
|
+
this.plugins.delete(name);
|
|
92
|
+
this.logger.info(`Plugin unloaded: ${name}`);
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
/** Run onCycle for all loaded plugins. */
|
|
96
|
+
async runCycle(cycleCount) {
|
|
97
|
+
for (const [name, loaded] of this.plugins) {
|
|
98
|
+
if (loaded.plugin.onCycle) {
|
|
99
|
+
try {
|
|
100
|
+
await loaded.plugin.onCycle(cycleCount);
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
this.logger.error(`Plugin ${name} cycle error: ${err.message}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/** Get all registered IPC routes from plugins. */
|
|
109
|
+
getRoutes() {
|
|
110
|
+
const routes = [];
|
|
111
|
+
for (const [name, loaded] of this.plugins) {
|
|
112
|
+
if (loaded.plugin.routes) {
|
|
113
|
+
for (const route of loaded.plugin.routes) {
|
|
114
|
+
routes.push({ plugin: name, ...route });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return routes;
|
|
119
|
+
}
|
|
120
|
+
/** Get all registered MCP tools from plugins. */
|
|
121
|
+
getTools() {
|
|
122
|
+
const tools = [];
|
|
123
|
+
for (const [name, loaded] of this.plugins) {
|
|
124
|
+
if (loaded.plugin.tools) {
|
|
125
|
+
for (const tool of loaded.plugin.tools) {
|
|
126
|
+
tools.push({ plugin: name, ...tool });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return tools;
|
|
131
|
+
}
|
|
132
|
+
/** List all plugins (loaded + discovered). */
|
|
133
|
+
list() {
|
|
134
|
+
const records = [];
|
|
135
|
+
for (const [, loaded] of this.plugins) {
|
|
136
|
+
records.push({
|
|
137
|
+
name: loaded.plugin.name,
|
|
138
|
+
version: loaded.plugin.version,
|
|
139
|
+
description: loaded.plugin.description ?? '',
|
|
140
|
+
enabled: true,
|
|
141
|
+
loadedAt: loaded.loadedAt,
|
|
142
|
+
error: loaded.error,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
return records;
|
|
146
|
+
}
|
|
147
|
+
/** Get a loaded plugin by name. */
|
|
148
|
+
get(name) {
|
|
149
|
+
return this.plugins.get(name)?.plugin ?? null;
|
|
150
|
+
}
|
|
151
|
+
/** Check if a plugin is loaded. */
|
|
152
|
+
has(name) {
|
|
153
|
+
return this.plugins.has(name);
|
|
154
|
+
}
|
|
155
|
+
/** Get plugin count. */
|
|
156
|
+
get size() {
|
|
157
|
+
return this.plugins.size;
|
|
158
|
+
}
|
|
159
|
+
/** Discover plugins in the plugin directory. */
|
|
160
|
+
discoverPlugins() {
|
|
161
|
+
const results = [];
|
|
162
|
+
if (!fs.existsSync(this.pluginDir))
|
|
163
|
+
return results;
|
|
164
|
+
const entries = fs.readdirSync(this.pluginDir, { withFileTypes: true });
|
|
165
|
+
for (const entry of entries) {
|
|
166
|
+
if (!entry.isDirectory())
|
|
167
|
+
continue;
|
|
168
|
+
const pluginPath = path.join(this.pluginDir, entry.name);
|
|
169
|
+
const manifestPath = path.join(pluginPath, 'package.json');
|
|
170
|
+
if (fs.existsSync(manifestPath)) {
|
|
171
|
+
try {
|
|
172
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
|
|
173
|
+
if (manifest.brainPlugin) {
|
|
174
|
+
results.push({ name: manifest.name || entry.name, path: pluginPath });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// Invalid manifest, skip
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return results;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=plugin-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-registry.js","sourceRoot":"","sources":["../../src/plugin/plugin-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export interface BrainPlugin {
|
|
2
|
+
/** Unique plugin name (e.g. 'weather-brain'). */
|
|
3
|
+
name: string;
|
|
4
|
+
/** SemVer version string. */
|
|
5
|
+
version: string;
|
|
6
|
+
/** Human-readable description. */
|
|
7
|
+
description?: string;
|
|
8
|
+
/** Called when plugin is loaded. Return services/state. */
|
|
9
|
+
onLoad?(context: PluginContext): Promise<void> | void;
|
|
10
|
+
/** Called when plugin is unloaded. Clean up resources. */
|
|
11
|
+
onUnload?(): Promise<void> | void;
|
|
12
|
+
/** Called on each learning cycle. */
|
|
13
|
+
onCycle?(cycleCount: number): Promise<void> | void;
|
|
14
|
+
/** MCP tools to register. */
|
|
15
|
+
tools?: PluginToolDefinition[];
|
|
16
|
+
/** IPC routes to register. */
|
|
17
|
+
routes?: PluginRouteDefinition[];
|
|
18
|
+
}
|
|
19
|
+
export interface PluginContext {
|
|
20
|
+
/** Data directory for this plugin (e.g. ~/.brain/plugins/weather-brain/). */
|
|
21
|
+
dataDir: string;
|
|
22
|
+
/** Logger scoped to this plugin. */
|
|
23
|
+
log: PluginLogger;
|
|
24
|
+
/** IPC client to communicate with Brain. */
|
|
25
|
+
callBrain(method: string, params?: unknown): Promise<unknown>;
|
|
26
|
+
/** Notify other brains via cross-brain. */
|
|
27
|
+
notify(event: string, data: unknown): Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
export interface PluginLogger {
|
|
30
|
+
info(msg: string): void;
|
|
31
|
+
warn(msg: string): void;
|
|
32
|
+
error(msg: string): void;
|
|
33
|
+
debug(msg: string): void;
|
|
34
|
+
}
|
|
35
|
+
export interface PluginToolDefinition {
|
|
36
|
+
name: string;
|
|
37
|
+
description: string;
|
|
38
|
+
schema: Record<string, unknown>;
|
|
39
|
+
handler: (params: Record<string, unknown>) => Promise<unknown> | unknown;
|
|
40
|
+
}
|
|
41
|
+
export interface PluginRouteDefinition {
|
|
42
|
+
method: string;
|
|
43
|
+
handler: (params: unknown) => Promise<unknown> | unknown;
|
|
44
|
+
}
|
|
45
|
+
export interface PluginManifest {
|
|
46
|
+
name: string;
|
|
47
|
+
version: string;
|
|
48
|
+
description?: string;
|
|
49
|
+
main: string;
|
|
50
|
+
brainPlugin: true;
|
|
51
|
+
}
|
|
52
|
+
export interface PluginRecord {
|
|
53
|
+
name: string;
|
|
54
|
+
version: string;
|
|
55
|
+
description: string;
|
|
56
|
+
enabled: boolean;
|
|
57
|
+
loadedAt: string | null;
|
|
58
|
+
error: string | null;
|
|
59
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/plugin/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|