@timmeck/brain-core 1.0.0

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.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +144 -0
  3. package/dist/api/server.d.ts +29 -0
  4. package/dist/api/server.js +183 -0
  5. package/dist/api/server.js.map +1 -0
  6. package/dist/cli/colors.d.ts +47 -0
  7. package/dist/cli/colors.js +93 -0
  8. package/dist/cli/colors.js.map +1 -0
  9. package/dist/db/connection.d.ts +2 -0
  10. package/dist/db/connection.js +19 -0
  11. package/dist/db/connection.js.map +1 -0
  12. package/dist/index.d.ts +18 -0
  13. package/dist/index.js +19 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/ipc/client.d.ts +16 -0
  16. package/dist/ipc/client.js +100 -0
  17. package/dist/ipc/client.js.map +1 -0
  18. package/dist/ipc/protocol.d.ts +8 -0
  19. package/dist/ipc/protocol.js +29 -0
  20. package/dist/ipc/protocol.js.map +1 -0
  21. package/dist/ipc/server.d.ts +22 -0
  22. package/dist/ipc/server.js +156 -0
  23. package/dist/ipc/server.js.map +1 -0
  24. package/dist/mcp/http-server.d.ts +23 -0
  25. package/dist/mcp/http-server.js +126 -0
  26. package/dist/mcp/http-server.js.map +1 -0
  27. package/dist/mcp/server.d.ts +15 -0
  28. package/dist/mcp/server.js +66 -0
  29. package/dist/mcp/server.js.map +1 -0
  30. package/dist/types/ipc.types.d.ts +11 -0
  31. package/dist/types/ipc.types.js +2 -0
  32. package/dist/types/ipc.types.js.map +1 -0
  33. package/dist/utils/events.d.ts +18 -0
  34. package/dist/utils/events.js +27 -0
  35. package/dist/utils/events.js.map +1 -0
  36. package/dist/utils/hash.d.ts +1 -0
  37. package/dist/utils/hash.js +5 -0
  38. package/dist/utils/hash.js.map +1 -0
  39. package/dist/utils/logger.d.ts +16 -0
  40. package/dist/utils/logger.js +43 -0
  41. package/dist/utils/logger.js.map +1 -0
  42. package/dist/utils/paths.d.ts +8 -0
  43. package/dist/utils/paths.js +23 -0
  44. package/dist/utils/paths.js.map +1 -0
  45. package/package.json +59 -0
  46. package/src/api/server.ts +210 -0
  47. package/src/cli/colors.ts +105 -0
  48. package/src/db/connection.ts +22 -0
  49. package/src/index.ts +31 -0
  50. package/src/ipc/client.ts +117 -0
  51. package/src/ipc/protocol.ts +35 -0
  52. package/src/ipc/server.ts +170 -0
  53. package/src/mcp/http-server.ts +148 -0
  54. package/src/mcp/server.ts +84 -0
  55. package/src/types/ipc.types.ts +8 -0
  56. package/src/utils/events.ts +30 -0
  57. package/src/utils/hash.ts +5 -0
  58. package/src/utils/logger.ts +67 -0
  59. package/src/utils/paths.ts +24 -0
  60. package/tsconfig.json +18 -0
@@ -0,0 +1,100 @@
1
+ import net from 'node:net';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { encodeMessage, MessageDecoder } from './protocol.js';
4
+ import { getPipeName } from '../utils/paths.js';
5
+ export class IpcClient {
6
+ pipeName;
7
+ timeout;
8
+ socket = null;
9
+ decoder = new MessageDecoder();
10
+ pending = new Map();
11
+ onNotification;
12
+ constructor(pipeName = getPipeName(), timeout = 5000) {
13
+ this.pipeName = pipeName;
14
+ this.timeout = timeout;
15
+ }
16
+ connect() {
17
+ return new Promise((resolve, reject) => {
18
+ this.socket = net.createConnection(this.pipeName, () => {
19
+ resolve();
20
+ });
21
+ this.socket.on('data', (chunk) => {
22
+ const messages = this.decoder.feed(chunk);
23
+ for (const msg of messages) {
24
+ this.handleMessage(msg);
25
+ }
26
+ });
27
+ this.socket.on('error', (err) => {
28
+ reject(err);
29
+ for (const [id, req] of this.pending) {
30
+ clearTimeout(req.timer);
31
+ req.reject(new Error(`Connection error: ${err.message}`));
32
+ this.pending.delete(id);
33
+ }
34
+ });
35
+ this.socket.on('close', () => {
36
+ for (const [id, req] of this.pending) {
37
+ clearTimeout(req.timer);
38
+ req.reject(new Error('Connection closed'));
39
+ this.pending.delete(id);
40
+ }
41
+ this.socket = null;
42
+ });
43
+ });
44
+ }
45
+ request(method, params) {
46
+ return new Promise((resolve, reject) => {
47
+ if (!this.socket || this.socket.destroyed) {
48
+ return reject(new Error('Not connected'));
49
+ }
50
+ const id = randomUUID();
51
+ const timer = setTimeout(() => {
52
+ this.pending.delete(id);
53
+ reject(new Error(`Request timeout: ${method} (${this.timeout}ms)`));
54
+ }, this.timeout);
55
+ this.pending.set(id, { resolve, reject, timer });
56
+ const msg = {
57
+ id,
58
+ type: 'request',
59
+ method,
60
+ params,
61
+ };
62
+ this.socket.write(encodeMessage(msg));
63
+ });
64
+ }
65
+ setNotificationHandler(handler) {
66
+ this.onNotification = handler;
67
+ }
68
+ disconnect() {
69
+ for (const [id, req] of this.pending) {
70
+ clearTimeout(req.timer);
71
+ req.reject(new Error('Client disconnecting'));
72
+ this.pending.delete(id);
73
+ }
74
+ this.socket?.destroy();
75
+ this.socket = null;
76
+ this.decoder.reset();
77
+ }
78
+ get connected() {
79
+ return this.socket !== null && !this.socket.destroyed;
80
+ }
81
+ handleMessage(msg) {
82
+ if (msg.type === 'response') {
83
+ const req = this.pending.get(msg.id);
84
+ if (!req)
85
+ return;
86
+ clearTimeout(req.timer);
87
+ this.pending.delete(msg.id);
88
+ if (msg.error) {
89
+ req.reject(new Error(msg.error.message));
90
+ }
91
+ else {
92
+ req.resolve(msg.result);
93
+ }
94
+ }
95
+ else if (msg.type === 'notification') {
96
+ this.onNotification?.(msg);
97
+ }
98
+ }
99
+ }
100
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/ipc/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import { Buffer } from 'node:buffer';
2
+ import type { IpcMessage } from '../types/ipc.types.js';
3
+ export declare function encodeMessage(msg: IpcMessage): Buffer;
4
+ export declare class MessageDecoder {
5
+ private buffer;
6
+ feed(chunk: Buffer): IpcMessage[];
7
+ reset(): void;
8
+ }
@@ -0,0 +1,29 @@
1
+ import { Buffer } from 'node:buffer';
2
+ export function encodeMessage(msg) {
3
+ const json = JSON.stringify(msg);
4
+ const payload = Buffer.from(json, 'utf8');
5
+ const frame = Buffer.alloc(4 + payload.length);
6
+ frame.writeUInt32BE(payload.length, 0);
7
+ payload.copy(frame, 4);
8
+ return frame;
9
+ }
10
+ export class MessageDecoder {
11
+ buffer = Buffer.alloc(0);
12
+ feed(chunk) {
13
+ this.buffer = Buffer.concat([this.buffer, chunk]);
14
+ const messages = [];
15
+ while (this.buffer.length >= 4) {
16
+ const length = this.buffer.readUInt32BE(0);
17
+ if (this.buffer.length < 4 + length)
18
+ break;
19
+ const json = this.buffer.subarray(4, 4 + length).toString('utf8');
20
+ this.buffer = this.buffer.subarray(4 + length);
21
+ messages.push(JSON.parse(json));
22
+ }
23
+ return messages;
24
+ }
25
+ reset() {
26
+ this.buffer = Buffer.alloc(0);
27
+ }
28
+ }
29
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/ipc/protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAGrC,MAAM,UAAU,aAAa,CAAC,GAAe;IAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC/C,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACvC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACvB,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,OAAO,cAAc;IACjB,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEjC,IAAI,CAAC,KAAa;QAChB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;QAClD,MAAM,QAAQ,GAAiB,EAAE,CAAC;QAElC,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;YAC3C,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,GAAG,MAAM;gBAAE,MAAM;YAE3C,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAClE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;YAC/C,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAe,CAAC,CAAC;QAChD,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC;CACF"}
@@ -0,0 +1,22 @@
1
+ import type { IpcMessage } from '../types/ipc.types.js';
2
+ export interface IpcRouter {
3
+ handle(method: string, params: unknown): unknown;
4
+ listMethods(): string[];
5
+ }
6
+ export declare class IpcServer {
7
+ private router;
8
+ private pipeName;
9
+ private daemonName;
10
+ private server;
11
+ private clients;
12
+ private logger;
13
+ constructor(router: IpcRouter, pipeName: string, daemonName?: string);
14
+ start(): void;
15
+ private createServer;
16
+ private listen;
17
+ private recoverStalePipe;
18
+ private handleMessage;
19
+ notify(clientId: string | null, notification: Omit<IpcMessage, 'id' | 'type'>): void;
20
+ getClientCount(): number;
21
+ stop(): void;
22
+ }
@@ -0,0 +1,156 @@
1
+ import net from 'node:net';
2
+ import fs from 'node:fs';
3
+ import { randomUUID } from 'node:crypto';
4
+ import { getLogger } from '../utils/logger.js';
5
+ import { encodeMessage, MessageDecoder } from './protocol.js';
6
+ export class IpcServer {
7
+ router;
8
+ pipeName;
9
+ daemonName;
10
+ server = null;
11
+ clients = new Map();
12
+ logger = getLogger();
13
+ constructor(router, pipeName, daemonName = 'brain') {
14
+ this.router = router;
15
+ this.pipeName = pipeName;
16
+ this.daemonName = daemonName;
17
+ }
18
+ start() {
19
+ this.createServer();
20
+ this.listen();
21
+ }
22
+ createServer() {
23
+ this.server = net.createServer((socket) => {
24
+ const clientId = randomUUID();
25
+ this.clients.set(clientId, socket);
26
+ const decoder = new MessageDecoder();
27
+ this.logger.info(`IPC client connected: ${clientId}`);
28
+ socket.on('data', (chunk) => {
29
+ const messages = decoder.feed(chunk);
30
+ for (const msg of messages) {
31
+ this.handleMessage(clientId, msg, socket);
32
+ }
33
+ });
34
+ socket.on('close', () => {
35
+ this.logger.info(`IPC client disconnected: ${clientId}`);
36
+ this.clients.delete(clientId);
37
+ });
38
+ socket.on('error', (err) => {
39
+ this.logger.error(`IPC client ${clientId} error:`, err);
40
+ this.clients.delete(clientId);
41
+ });
42
+ });
43
+ }
44
+ listen(retried = false) {
45
+ if (!this.server)
46
+ return;
47
+ this.server.on('error', (err) => {
48
+ if (err.code === 'EADDRINUSE' && !retried) {
49
+ this.logger.warn(`IPC pipe in use, attempting to recover stale pipe: ${this.pipeName}`);
50
+ this.recoverStalePipe();
51
+ }
52
+ else {
53
+ this.logger.error('IPC server error:', err);
54
+ }
55
+ });
56
+ this.server.listen(this.pipeName, () => {
57
+ this.logger.info(`IPC server listening on ${this.pipeName}`);
58
+ });
59
+ }
60
+ recoverStalePipe() {
61
+ const probe = net.createConnection(this.pipeName);
62
+ probe.on('connect', () => {
63
+ probe.destroy();
64
+ this.logger.error(`IPC pipe is held by another running daemon. Stop it first with: ${this.daemonName} stop`);
65
+ });
66
+ probe.on('error', () => {
67
+ probe.destroy();
68
+ this.logger.info('Stale IPC pipe detected, reclaiming...');
69
+ if (process.platform !== 'win32') {
70
+ try {
71
+ fs.unlinkSync(this.pipeName);
72
+ }
73
+ catch { /* ignore */ }
74
+ }
75
+ this.createServer();
76
+ this.server.on('error', (err) => {
77
+ this.logger.error('IPC server error after recovery:', err);
78
+ });
79
+ this.server.listen(this.pipeName, () => {
80
+ this.logger.info(`IPC server recovered and listening on ${this.pipeName}`);
81
+ });
82
+ });
83
+ probe.setTimeout(2000, () => {
84
+ probe.destroy();
85
+ this.logger.warn('IPC pipe probe timed out, treating as stale');
86
+ if (process.platform !== 'win32') {
87
+ try {
88
+ fs.unlinkSync(this.pipeName);
89
+ }
90
+ catch { /* ignore */ }
91
+ }
92
+ this.createServer();
93
+ this.server.on('error', (err) => {
94
+ this.logger.error('IPC server error after timeout recovery:', err);
95
+ });
96
+ this.server.listen(this.pipeName, () => {
97
+ this.logger.info(`IPC server recovered (timeout) and listening on ${this.pipeName}`);
98
+ });
99
+ });
100
+ }
101
+ handleMessage(_clientId, msg, socket) {
102
+ if (msg.type !== 'request' || !msg.method)
103
+ return;
104
+ try {
105
+ const result = this.router.handle(msg.method, msg.params);
106
+ const response = {
107
+ id: msg.id,
108
+ type: 'response',
109
+ result,
110
+ };
111
+ socket.write(encodeMessage(response));
112
+ }
113
+ catch (err) {
114
+ const response = {
115
+ id: msg.id,
116
+ type: 'response',
117
+ error: { code: -1, message: err instanceof Error ? err.message : String(err) },
118
+ };
119
+ socket.write(encodeMessage(response));
120
+ }
121
+ }
122
+ notify(clientId, notification) {
123
+ const msg = {
124
+ id: randomUUID(),
125
+ type: 'notification',
126
+ ...notification,
127
+ };
128
+ const encoded = encodeMessage(msg);
129
+ if (clientId) {
130
+ const socket = this.clients.get(clientId);
131
+ if (socket && !socket.destroyed) {
132
+ socket.write(encoded);
133
+ }
134
+ }
135
+ else {
136
+ for (const socket of this.clients.values()) {
137
+ if (!socket.destroyed) {
138
+ socket.write(encoded);
139
+ }
140
+ }
141
+ }
142
+ }
143
+ getClientCount() {
144
+ return this.clients.size;
145
+ }
146
+ stop() {
147
+ for (const socket of this.clients.values()) {
148
+ socket.destroy();
149
+ }
150
+ this.clients.clear();
151
+ this.server?.close();
152
+ this.server = null;
153
+ this.logger.info('IPC server stopped');
154
+ }
155
+ }
156
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/ipc/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { IpcRouter } from '../ipc/server.js';
3
+ export interface McpHttpServerOptions {
4
+ /** MCP server name */
5
+ name: string;
6
+ /** MCP server version */
7
+ version: string;
8
+ }
9
+ export declare class McpHttpServer {
10
+ private port;
11
+ private router;
12
+ private meta;
13
+ private registerToolsDirect;
14
+ private server;
15
+ private transports;
16
+ private logger;
17
+ constructor(port: number, router: IpcRouter, meta: McpHttpServerOptions, registerToolsDirect: (server: McpServer, router: IpcRouter) => void);
18
+ start(): void;
19
+ stop(): void;
20
+ getClientCount(): number;
21
+ private handleSSE;
22
+ private handleMessage;
23
+ }
@@ -0,0 +1,126 @@
1
+ import http from 'node:http';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
+ import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
5
+ import { getLogger } from '../utils/logger.js';
6
+ export class McpHttpServer {
7
+ port;
8
+ router;
9
+ meta;
10
+ registerToolsDirect;
11
+ server = null;
12
+ transports = new Map();
13
+ logger = getLogger();
14
+ constructor(port, router, meta, registerToolsDirect) {
15
+ this.port = port;
16
+ this.router = router;
17
+ this.meta = meta;
18
+ this.registerToolsDirect = registerToolsDirect;
19
+ }
20
+ start() {
21
+ this.server = http.createServer((req, res) => {
22
+ res.setHeader('Access-Control-Allow-Origin', '*');
23
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
24
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
25
+ if (req.method === 'OPTIONS') {
26
+ res.writeHead(204);
27
+ res.end();
28
+ return;
29
+ }
30
+ const url = new URL(req.url ?? '/', `http://localhost:${this.port}`);
31
+ if (url.pathname === '/sse' && req.method === 'GET') {
32
+ this.handleSSE(res);
33
+ return;
34
+ }
35
+ if (url.pathname === '/messages' && req.method === 'POST') {
36
+ this.handleMessage(req, res, url);
37
+ return;
38
+ }
39
+ if (url.pathname === '/' && req.method === 'GET') {
40
+ res.writeHead(200, { 'Content-Type': 'application/json' });
41
+ res.end(JSON.stringify({
42
+ name: this.meta.name,
43
+ version: this.meta.version,
44
+ protocol: 'MCP',
45
+ transport: 'sse',
46
+ endpoints: {
47
+ sse: '/sse',
48
+ messages: '/messages',
49
+ },
50
+ clients: this.transports.size,
51
+ }));
52
+ return;
53
+ }
54
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
55
+ res.end('Not Found');
56
+ });
57
+ this.server.listen(this.port, () => {
58
+ this.logger.info(`MCP HTTP server (SSE) started on http://localhost:${this.port}`);
59
+ });
60
+ }
61
+ stop() {
62
+ for (const transport of this.transports.values()) {
63
+ try {
64
+ transport.close?.();
65
+ }
66
+ catch { /* ignore */ }
67
+ }
68
+ this.transports.clear();
69
+ this.server?.close();
70
+ this.server = null;
71
+ this.logger.info('MCP HTTP server stopped');
72
+ }
73
+ getClientCount() {
74
+ return this.transports.size;
75
+ }
76
+ async handleSSE(res) {
77
+ try {
78
+ const transport = new SSEServerTransport('/messages', res);
79
+ const sessionId = transport.sessionId ?? randomUUID();
80
+ this.transports.set(sessionId, transport);
81
+ const server = new McpServer({
82
+ name: this.meta.name,
83
+ version: this.meta.version,
84
+ });
85
+ this.registerToolsDirect(server, this.router);
86
+ res.on('close', () => {
87
+ this.transports.delete(sessionId);
88
+ this.logger.debug(`MCP SSE client disconnected: ${sessionId}`);
89
+ });
90
+ await server.connect(transport);
91
+ this.logger.info(`MCP SSE client connected: ${sessionId}`);
92
+ }
93
+ catch (err) {
94
+ this.logger.error('MCP SSE connection error:', err);
95
+ if (!res.headersSent) {
96
+ res.writeHead(500, { 'Content-Type': 'text/plain' });
97
+ res.end('Internal Server Error');
98
+ }
99
+ }
100
+ }
101
+ async handleMessage(req, res, url) {
102
+ try {
103
+ const sessionId = url.searchParams.get('sessionId');
104
+ if (!sessionId) {
105
+ res.writeHead(400, { 'Content-Type': 'text/plain' });
106
+ res.end('Missing sessionId parameter');
107
+ return;
108
+ }
109
+ const transport = this.transports.get(sessionId);
110
+ if (!transport) {
111
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
112
+ res.end('Session not found. Connect to /sse first.');
113
+ return;
114
+ }
115
+ await transport.handlePostMessage(req, res);
116
+ }
117
+ catch (err) {
118
+ this.logger.error('MCP message error:', err);
119
+ if (!res.headersSent) {
120
+ res.writeHead(500, { 'Content-Type': 'text/plain' });
121
+ res.end('Internal Server Error');
122
+ }
123
+ }
124
+ }
125
+ }
126
+ //# sourceMappingURL=http-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-server.js","sourceRoot":"","sources":["../../src/mcp/http-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { IpcClient } from '../ipc/client.js';
3
+ export interface McpServerOptions {
4
+ /** MCP server name (e.g. 'brain', 'trading-brain') */
5
+ name: string;
6
+ /** MCP server version */
7
+ version: string;
8
+ /** Pipe name for IPC connection */
9
+ pipeName?: string;
10
+ /** Path to the CLI entry point for auto-starting the daemon */
11
+ entryPoint: string;
12
+ /** Register MCP tools on the server */
13
+ registerTools: (server: McpServer, ipc: IpcClient) => void;
14
+ }
15
+ export declare function startMcpServer(opts: McpServerOptions): Promise<void>;
@@ -0,0 +1,66 @@
1
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { spawn } from 'node:child_process';
4
+ import path from 'node:path';
5
+ import { IpcClient } from '../ipc/client.js';
6
+ import { getPipeName } from '../utils/paths.js';
7
+ function spawnDaemon(opts) {
8
+ const child = spawn(process.execPath, [
9
+ '--import', 'tsx',
10
+ opts.entryPoint, 'daemon',
11
+ ], {
12
+ detached: true,
13
+ stdio: 'ignore',
14
+ cwd: path.resolve(path.dirname(opts.entryPoint), '..'),
15
+ });
16
+ child.unref();
17
+ process.stderr.write(`${opts.name}: Auto-started daemon (PID: ${child.pid})\n`);
18
+ }
19
+ async function connectWithRetry(ipc, retries, delayMs) {
20
+ for (let i = 0; i < retries; i++) {
21
+ try {
22
+ await ipc.connect();
23
+ return;
24
+ }
25
+ catch {
26
+ if (i < retries - 1) {
27
+ await new Promise(r => setTimeout(r, delayMs));
28
+ }
29
+ }
30
+ }
31
+ throw new Error('Could not connect to daemon after retries');
32
+ }
33
+ export async function startMcpServer(opts) {
34
+ const server = new McpServer({
35
+ name: opts.name,
36
+ version: opts.version,
37
+ });
38
+ const pipeName = opts.pipeName ?? getPipeName(opts.name);
39
+ const ipc = new IpcClient(pipeName);
40
+ try {
41
+ await ipc.connect();
42
+ }
43
+ catch {
44
+ process.stderr.write(`${opts.name}: Daemon not running, starting automatically...\n`);
45
+ spawnDaemon(opts);
46
+ try {
47
+ await connectWithRetry(ipc, 10, 500);
48
+ }
49
+ catch {
50
+ process.stderr.write(`${opts.name}: Could not connect to daemon after auto-start. Check logs.\n`);
51
+ process.exit(1);
52
+ }
53
+ }
54
+ opts.registerTools(server, ipc);
55
+ const transport = new StdioServerTransport();
56
+ await server.connect(transport);
57
+ process.on('SIGINT', () => {
58
+ ipc.disconnect();
59
+ process.exit(0);
60
+ });
61
+ process.on('SIGTERM', () => {
62
+ ipc.disconnect();
63
+ process.exit(0);
64
+ });
65
+ }
66
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export interface IpcMessage {
2
+ id: string;
3
+ type: 'request' | 'response' | 'notification';
4
+ method?: string;
5
+ params?: unknown;
6
+ result?: unknown;
7
+ error?: {
8
+ code: number;
9
+ message: string;
10
+ };
11
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ipc.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ipc.types.js","sourceRoot":"","sources":["../../src/types/ipc.types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,18 @@
1
+ import { EventEmitter } from 'node:events';
2
+ /**
3
+ * Generic typed event bus. Each brain defines its own events interface:
4
+ *
5
+ * ```ts
6
+ * interface MyEvents {
7
+ * 'trade:recorded': { tradeId: number; win: boolean };
8
+ * 'rule:learned': { ruleId: number; pattern: string };
9
+ * }
10
+ * const bus = new TypedEventBus<MyEvents>();
11
+ * ```
12
+ */
13
+ export declare class TypedEventBus<T extends Record<string, unknown>> extends EventEmitter {
14
+ emit<K extends keyof T & string>(event: K, data: T[K]): boolean;
15
+ on<K extends keyof T & string>(event: K, listener: (data: T[K]) => void): this;
16
+ once<K extends keyof T & string>(event: K, listener: (data: T[K]) => void): this;
17
+ off<K extends keyof T & string>(event: K, listener: (data: T[K]) => void): this;
18
+ }
@@ -0,0 +1,27 @@
1
+ import { EventEmitter } from 'node:events';
2
+ /**
3
+ * Generic typed event bus. Each brain defines its own events interface:
4
+ *
5
+ * ```ts
6
+ * interface MyEvents {
7
+ * 'trade:recorded': { tradeId: number; win: boolean };
8
+ * 'rule:learned': { ruleId: number; pattern: string };
9
+ * }
10
+ * const bus = new TypedEventBus<MyEvents>();
11
+ * ```
12
+ */
13
+ export class TypedEventBus extends EventEmitter {
14
+ emit(event, data) {
15
+ return super.emit(event, data);
16
+ }
17
+ on(event, listener) {
18
+ return super.on(event, listener);
19
+ }
20
+ once(event, listener) {
21
+ return super.once(event, listener);
22
+ }
23
+ off(event, listener) {
24
+ return super.off(event, listener);
25
+ }
26
+ }
27
+ //# sourceMappingURL=events.js.map