@timmeck/brain 1.8.5 → 2.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.
- package/.dockerignore +11 -0
- package/Dockerfile +21 -0
- package/README.md +19 -0
- package/assets/brain-avatar-256.png +0 -0
- package/assets/brain-avatar-512.png +0 -0
- package/assets/brain-avatar.png +0 -0
- package/brain.log +1164 -0
- package/{src/cli/commands/dashboard.ts → dashboard.html} +688 -807
- package/dist/api/server.d.ts +4 -18
- package/dist/api/server.js +4 -173
- package/dist/api/server.js.map +1 -1
- package/dist/brain.d.ts +6 -0
- package/dist/brain.js +56 -4
- package/dist/brain.js.map +1 -1
- package/dist/cli/colors.d.ts +4 -25
- package/dist/cli/colors.js +3 -89
- package/dist/cli/colors.js.map +1 -1
- package/dist/cli/commands/peers.d.ts +2 -0
- package/dist/cli/commands/peers.js +38 -0
- package/dist/cli/commands/peers.js.map +1 -0
- package/dist/cli/commands/start.js +54 -17
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/db/connection.d.ts +1 -2
- package/dist/db/connection.js +1 -18
- package/dist/db/connection.js.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/ipc/__tests__/protocol.test.d.ts +1 -0
- package/dist/ipc/__tests__/protocol.test.js +117 -0
- package/dist/ipc/__tests__/protocol.test.js.map +1 -0
- package/dist/ipc/client.d.ts +1 -16
- package/dist/ipc/client.js +1 -100
- package/dist/ipc/client.js.map +1 -1
- package/dist/ipc/protocol.d.ts +1 -8
- package/dist/ipc/protocol.js +1 -28
- package/dist/ipc/protocol.js.map +1 -1
- package/dist/ipc/router.js +8 -0
- package/dist/ipc/router.js.map +1 -1
- package/dist/ipc/server.d.ts +1 -22
- package/dist/ipc/server.js +1 -163
- package/dist/ipc/server.js.map +1 -1
- package/dist/mcp/http-server.d.ts +1 -7
- package/dist/mcp/http-server.js +6 -117
- package/dist/mcp/http-server.js.map +1 -1
- package/dist/mcp/server.js +5 -61
- package/dist/mcp/server.js.map +1 -1
- package/dist/signals/__tests__/fingerprint.test.d.ts +1 -0
- package/dist/signals/__tests__/fingerprint.test.js +118 -0
- package/dist/signals/__tests__/fingerprint.test.js.map +1 -0
- package/dist/types/ipc.types.d.ts +1 -11
- package/dist/utils/__tests__/hash.test.d.ts +1 -0
- package/dist/utils/__tests__/hash.test.js +32 -0
- package/dist/utils/__tests__/hash.test.js.map +1 -0
- package/dist/utils/__tests__/paths.test.d.ts +1 -0
- package/dist/utils/__tests__/paths.test.js +75 -0
- package/dist/utils/__tests__/paths.test.js.map +1 -0
- package/dist/utils/events.d.ts +4 -8
- package/dist/utils/events.js +2 -14
- package/dist/utils/events.js.map +1 -1
- package/dist/utils/hash.d.ts +1 -1
- package/dist/utils/hash.js +1 -4
- package/dist/utils/hash.js.map +1 -1
- package/dist/utils/logger.d.ts +3 -2
- package/dist/utils/logger.js +8 -35
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/paths.d.ts +2 -1
- package/dist/utils/paths.js +4 -13
- package/dist/utils/paths.js.map +1 -1
- package/gen_avatar.py +142 -0
- package/package.json +2 -1
- package/BRAIN_PLAN.md +0 -3324
- package/src/api/server.ts +0 -395
- package/src/brain.ts +0 -266
- package/src/cli/colors.ts +0 -116
- package/src/cli/commands/config.ts +0 -169
- package/src/cli/commands/doctor.ts +0 -124
- package/src/cli/commands/explain.ts +0 -83
- package/src/cli/commands/export.ts +0 -31
- package/src/cli/commands/import.ts +0 -199
- package/src/cli/commands/insights.ts +0 -65
- package/src/cli/commands/learn.ts +0 -24
- package/src/cli/commands/modules.ts +0 -53
- package/src/cli/commands/network.ts +0 -67
- package/src/cli/commands/projects.ts +0 -42
- package/src/cli/commands/query.ts +0 -120
- package/src/cli/commands/start.ts +0 -62
- package/src/cli/commands/status.ts +0 -75
- package/src/cli/commands/stop.ts +0 -34
- package/src/cli/ipc-helper.ts +0 -22
- package/src/cli/update-check.ts +0 -63
- package/src/code/analyzer.ts +0 -117
- package/src/code/fingerprint.ts +0 -87
- package/src/code/matcher.ts +0 -129
- package/src/code/parsers/generic.ts +0 -29
- package/src/code/parsers/python.ts +0 -54
- package/src/code/parsers/typescript.ts +0 -65
- package/src/code/registry.ts +0 -60
- package/src/code/scorer.ts +0 -120
- package/src/config.ts +0 -135
- package/src/dashboard/server.ts +0 -142
- package/src/db/connection.ts +0 -22
- package/src/db/migrations/001_core_schema.ts +0 -120
- package/src/db/migrations/002_learning_schema.ts +0 -38
- package/src/db/migrations/003_code_schema.ts +0 -53
- package/src/db/migrations/004_synapses_schema.ts +0 -57
- package/src/db/migrations/005_fts_indexes.ts +0 -78
- package/src/db/migrations/006_synapses_phase3.ts +0 -17
- package/src/db/migrations/007_feedback.ts +0 -13
- package/src/db/migrations/008_git_integration.ts +0 -38
- package/src/db/migrations/009_embeddings.ts +0 -8
- package/src/db/migrations/index.ts +0 -70
- package/src/db/repositories/antipattern.repository.ts +0 -66
- package/src/db/repositories/code-module.repository.ts +0 -142
- package/src/db/repositories/error.repository.ts +0 -189
- package/src/db/repositories/insight.repository.ts +0 -99
- package/src/db/repositories/notification.repository.ts +0 -66
- package/src/db/repositories/project.repository.ts +0 -93
- package/src/db/repositories/rule.repository.ts +0 -108
- package/src/db/repositories/solution.repository.ts +0 -154
- package/src/db/repositories/synapse.repository.ts +0 -163
- package/src/db/repositories/terminal.repository.ts +0 -101
- package/src/embeddings/engine.ts +0 -238
- package/src/hooks/post-tool-use.ts +0 -92
- package/src/hooks/post-write.ts +0 -129
- package/src/index.ts +0 -63
- package/src/ipc/client.ts +0 -118
- package/src/ipc/protocol.ts +0 -35
- package/src/ipc/router.ts +0 -133
- package/src/ipc/server.ts +0 -176
- package/src/learning/confidence-scorer.ts +0 -80
- package/src/learning/decay.ts +0 -46
- package/src/learning/learning-engine.ts +0 -170
- package/src/learning/pattern-extractor.ts +0 -90
- package/src/learning/rule-generator.ts +0 -74
- package/src/main.rs:10:5 +0 -0
- package/src/matching/error-matcher.ts +0 -166
- package/src/matching/fingerprint.ts +0 -34
- package/src/matching/similarity.ts +0 -61
- package/src/matching/tfidf.ts +0 -74
- package/src/matching/tokenizer.ts +0 -41
- package/src/mcp/auto-detect.ts +0 -93
- package/src/mcp/http-server.ts +0 -140
- package/src/mcp/server.ts +0 -73
- package/src/mcp/tools.ts +0 -328
- package/src/parsing/error-parser.ts +0 -28
- package/src/parsing/parsers/compiler.ts +0 -93
- package/src/parsing/parsers/generic.ts +0 -28
- package/src/parsing/parsers/go.ts +0 -97
- package/src/parsing/parsers/node.ts +0 -69
- package/src/parsing/parsers/python.ts +0 -62
- package/src/parsing/parsers/rust.ts +0 -50
- package/src/parsing/parsers/shell.ts +0 -42
- package/src/parsing/types.ts +0 -47
- package/src/research/gap-analyzer.ts +0 -135
- package/src/research/insight-generator.ts +0 -123
- package/src/research/research-engine.ts +0 -116
- package/src/research/synergy-detector.ts +0 -126
- package/src/research/template-extractor.ts +0 -130
- package/src/research/trend-analyzer.ts +0 -127
- package/src/services/analytics.service.ts +0 -226
- package/src/services/code.service.ts +0 -271
- package/src/services/error.service.ts +0 -266
- package/src/services/git.service.ts +0 -132
- package/src/services/notification.service.ts +0 -41
- package/src/services/prevention.service.ts +0 -159
- package/src/services/research.service.ts +0 -98
- package/src/services/solution.service.ts +0 -174
- package/src/services/synapse.service.ts +0 -59
- package/src/services/terminal.service.ts +0 -81
- package/src/synapses/activation.ts +0 -80
- package/src/synapses/decay.ts +0 -38
- package/src/synapses/hebbian.ts +0 -69
- package/src/synapses/pathfinder.ts +0 -81
- package/src/synapses/synapse-manager.ts +0 -113
- package/src/types/code.types.ts +0 -52
- package/src/types/config.types.ts +0 -103
- package/src/types/error.types.ts +0 -67
- package/src/types/ipc.types.ts +0 -8
- package/src/types/mcp.types.ts +0 -53
- package/src/types/research.types.ts +0 -28
- package/src/types/solution.types.ts +0 -30
- package/src/types/synapse.types.ts +0 -50
- package/src/utils/events.ts +0 -45
- package/src/utils/hash.ts +0 -5
- package/src/utils/logger.ts +0 -48
- package/src/utils/paths.ts +0 -19
- package/tests/e2e/test_code_intelligence.py +0 -1015
- package/tests/e2e/test_error_memory.py +0 -451
- package/tests/e2e/test_full_integration.py +0 -534
- package/tests/fixtures/code-modules/modules.ts +0 -83
- package/tests/fixtures/errors/go.ts +0 -9
- package/tests/fixtures/errors/node.ts +0 -24
- package/tests/fixtures/errors/python.ts +0 -21
- package/tests/fixtures/errors/rust.ts +0 -25
- package/tests/fixtures/errors/shell.ts +0 -15
- package/tests/fixtures/solutions/solutions.ts +0 -27
- package/tests/helpers/setup-db.ts +0 -52
- package/tests/integration/code-flow.test.ts +0 -86
- package/tests/integration/error-flow.test.ts +0 -83
- package/tests/integration/ipc-flow.test.ts +0 -166
- package/tests/integration/learning-cycle.test.ts +0 -82
- package/tests/integration/synapse-flow.test.ts +0 -117
- package/tests/unit/code/analyzer.test.ts +0 -58
- package/tests/unit/code/fingerprint.test.ts +0 -51
- package/tests/unit/code/scorer.test.ts +0 -55
- package/tests/unit/learning/confidence-scorer.test.ts +0 -60
- package/tests/unit/learning/decay.test.ts +0 -45
- package/tests/unit/learning/pattern-extractor.test.ts +0 -50
- package/tests/unit/matching/error-matcher.test.ts +0 -69
- package/tests/unit/matching/fingerprint.test.ts +0 -47
- package/tests/unit/matching/similarity.test.ts +0 -65
- package/tests/unit/matching/tfidf.test.ts +0 -71
- package/tests/unit/matching/tokenizer.test.ts +0 -83
- package/tests/unit/parsing/parsers.test.ts +0 -113
- package/tests/unit/research/gap-analyzer.test.ts +0 -45
- package/tests/unit/research/trend-analyzer.test.ts +0 -45
- package/tests/unit/synapses/activation.test.ts +0 -80
- package/tests/unit/synapses/decay.test.ts +0 -27
- package/tests/unit/synapses/hebbian.test.ts +0 -96
- package/tests/unit/synapses/pathfinder.test.ts +0 -72
- package/tsconfig.json +0 -18
package/dist/ipc/server.js
CHANGED
|
@@ -1,164 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import { randomUUID } from 'node:crypto';
|
|
4
|
-
import { getLogger } from '../utils/logger.js';
|
|
5
|
-
const logger = getLogger();
|
|
6
|
-
import { encodeMessage, MessageDecoder } from './protocol.js';
|
|
7
|
-
export class IpcServer {
|
|
8
|
-
router;
|
|
9
|
-
pipeName;
|
|
10
|
-
server = null;
|
|
11
|
-
clients = new Map();
|
|
12
|
-
constructor(router, pipeName) {
|
|
13
|
-
this.router = router;
|
|
14
|
-
this.pipeName = pipeName;
|
|
15
|
-
}
|
|
16
|
-
start() {
|
|
17
|
-
this.createServer();
|
|
18
|
-
this.listen();
|
|
19
|
-
}
|
|
20
|
-
createServer() {
|
|
21
|
-
this.server = net.createServer((socket) => {
|
|
22
|
-
const clientId = randomUUID();
|
|
23
|
-
this.clients.set(clientId, socket);
|
|
24
|
-
const decoder = new MessageDecoder();
|
|
25
|
-
logger.info(`IPC client connected: ${clientId}`);
|
|
26
|
-
socket.on('data', (chunk) => {
|
|
27
|
-
const messages = decoder.feed(chunk);
|
|
28
|
-
for (const msg of messages) {
|
|
29
|
-
this.handleMessage(clientId, msg, socket);
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
socket.on('close', () => {
|
|
33
|
-
logger.info(`IPC client disconnected: ${clientId}`);
|
|
34
|
-
this.clients.delete(clientId);
|
|
35
|
-
});
|
|
36
|
-
socket.on('error', (err) => {
|
|
37
|
-
logger.error(`IPC client ${clientId} error:`, err);
|
|
38
|
-
this.clients.delete(clientId);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
listen(retried = false) {
|
|
43
|
-
if (!this.server)
|
|
44
|
-
return;
|
|
45
|
-
this.server.on('error', (err) => {
|
|
46
|
-
if (err.code === 'EADDRINUSE' && !retried) {
|
|
47
|
-
logger.warn(`IPC pipe in use, attempting to recover stale pipe: ${this.pipeName}`);
|
|
48
|
-
this.recoverStalePipe();
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
logger.error('IPC server error:', err);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
this.server.listen(this.pipeName, () => {
|
|
55
|
-
logger.info(`IPC server listening on ${this.pipeName}`);
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* On Windows, named pipes can remain after a crashed daemon.
|
|
60
|
-
* Try to connect as a client — if it fails, the pipe is stale and we can reclaim it.
|
|
61
|
-
* On Unix, simply unlink the socket file and retry.
|
|
62
|
-
*/
|
|
63
|
-
recoverStalePipe() {
|
|
64
|
-
const probe = net.createConnection(this.pipeName);
|
|
65
|
-
probe.on('connect', () => {
|
|
66
|
-
// Pipe is alive — another daemon is actually running
|
|
67
|
-
probe.destroy();
|
|
68
|
-
logger.error('IPC pipe is held by another running daemon. Stop it first with: brain stop');
|
|
69
|
-
});
|
|
70
|
-
probe.on('error', () => {
|
|
71
|
-
// Pipe is stale — no one is listening. Clean up and retry.
|
|
72
|
-
probe.destroy();
|
|
73
|
-
logger.info('Stale IPC pipe detected, reclaiming...');
|
|
74
|
-
// On Unix, unlink the socket file
|
|
75
|
-
if (process.platform !== 'win32') {
|
|
76
|
-
try {
|
|
77
|
-
fs.unlinkSync(this.pipeName);
|
|
78
|
-
}
|
|
79
|
-
catch { /* ignore */ }
|
|
80
|
-
}
|
|
81
|
-
// Recreate server and retry (Windows auto-reclaims dead named pipes on re-listen)
|
|
82
|
-
this.createServer();
|
|
83
|
-
this.server.on('error', (err) => {
|
|
84
|
-
logger.error('IPC server error after recovery:', err);
|
|
85
|
-
});
|
|
86
|
-
this.server.listen(this.pipeName, () => {
|
|
87
|
-
logger.info(`IPC server recovered and listening on ${this.pipeName}`);
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
// Timeout: if probe hangs, treat pipe as stale
|
|
91
|
-
probe.setTimeout(2000, () => {
|
|
92
|
-
probe.destroy();
|
|
93
|
-
logger.warn('IPC pipe probe timed out, treating as stale');
|
|
94
|
-
if (process.platform !== 'win32') {
|
|
95
|
-
try {
|
|
96
|
-
fs.unlinkSync(this.pipeName);
|
|
97
|
-
}
|
|
98
|
-
catch { /* ignore */ }
|
|
99
|
-
}
|
|
100
|
-
this.createServer();
|
|
101
|
-
this.server.on('error', (err) => {
|
|
102
|
-
logger.error('IPC server error after timeout recovery:', err);
|
|
103
|
-
});
|
|
104
|
-
this.server.listen(this.pipeName, () => {
|
|
105
|
-
logger.info(`IPC server recovered (timeout) and listening on ${this.pipeName}`);
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
handleMessage(clientId, msg, socket) {
|
|
110
|
-
if (msg.type !== 'request' || !msg.method)
|
|
111
|
-
return;
|
|
112
|
-
try {
|
|
113
|
-
const result = this.router.handle(msg.method, msg.params);
|
|
114
|
-
const response = {
|
|
115
|
-
id: msg.id,
|
|
116
|
-
type: 'response',
|
|
117
|
-
result,
|
|
118
|
-
};
|
|
119
|
-
socket.write(encodeMessage(response));
|
|
120
|
-
}
|
|
121
|
-
catch (err) {
|
|
122
|
-
const response = {
|
|
123
|
-
id: msg.id,
|
|
124
|
-
type: 'response',
|
|
125
|
-
error: { code: -1, message: err instanceof Error ? err.message : String(err) },
|
|
126
|
-
};
|
|
127
|
-
socket.write(encodeMessage(response));
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
notify(clientId, notification) {
|
|
131
|
-
const msg = {
|
|
132
|
-
id: randomUUID(),
|
|
133
|
-
type: 'notification',
|
|
134
|
-
...notification,
|
|
135
|
-
};
|
|
136
|
-
const encoded = encodeMessage(msg);
|
|
137
|
-
if (clientId) {
|
|
138
|
-
const socket = this.clients.get(clientId);
|
|
139
|
-
if (socket && !socket.destroyed) {
|
|
140
|
-
socket.write(encoded);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
144
|
-
for (const socket of this.clients.values()) {
|
|
145
|
-
if (!socket.destroyed) {
|
|
146
|
-
socket.write(encoded);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
getClientCount() {
|
|
152
|
-
return this.clients.size;
|
|
153
|
-
}
|
|
154
|
-
stop() {
|
|
155
|
-
for (const socket of this.clients.values()) {
|
|
156
|
-
socket.destroy();
|
|
157
|
-
}
|
|
158
|
-
this.clients.clear();
|
|
159
|
-
this.server?.close();
|
|
160
|
-
this.server = null;
|
|
161
|
-
logger.info('IPC server stopped');
|
|
162
|
-
}
|
|
163
|
-
}
|
|
1
|
+
export { IpcServer } from '@timmeck/brain-core';
|
|
164
2
|
//# sourceMappingURL=server.js.map
|
package/dist/ipc/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/ipc/server.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/ipc/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -1,14 +1,8 @@
|
|
|
1
1
|
import type { IpcRouter } from '../ipc/router.js';
|
|
2
2
|
export declare class McpHttpServer {
|
|
3
|
-
private
|
|
4
|
-
private router;
|
|
5
|
-
private server;
|
|
6
|
-
private transports;
|
|
7
|
-
private logger;
|
|
3
|
+
private inner;
|
|
8
4
|
constructor(port: number, router: IpcRouter);
|
|
9
5
|
start(): void;
|
|
10
6
|
stop(): void;
|
|
11
7
|
getClientCount(): number;
|
|
12
|
-
private handleSSE;
|
|
13
|
-
private handleMessage;
|
|
14
8
|
}
|
package/dist/mcp/http-server.js
CHANGED
|
@@ -1,123 +1,12 @@
|
|
|
1
|
-
import
|
|
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';
|
|
1
|
+
import { McpHttpServer as CoreMcpHttpServer } from '@timmeck/brain-core';
|
|
6
2
|
import { registerToolsDirect } from './tools.js';
|
|
7
3
|
export class McpHttpServer {
|
|
8
|
-
|
|
9
|
-
router;
|
|
10
|
-
server = null;
|
|
11
|
-
transports = new Map();
|
|
12
|
-
logger = getLogger();
|
|
4
|
+
inner;
|
|
13
5
|
constructor(port, router) {
|
|
14
|
-
this.
|
|
15
|
-
this.router = router;
|
|
16
|
-
}
|
|
17
|
-
start() {
|
|
18
|
-
this.server = http.createServer((req, res) => {
|
|
19
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
20
|
-
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
|
|
21
|
-
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
22
|
-
if (req.method === 'OPTIONS') {
|
|
23
|
-
res.writeHead(204);
|
|
24
|
-
res.end();
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
const url = new URL(req.url ?? '/', `http://localhost:${this.port}`);
|
|
28
|
-
if (url.pathname === '/sse' && req.method === 'GET') {
|
|
29
|
-
this.handleSSE(res);
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
if (url.pathname === '/messages' && req.method === 'POST') {
|
|
33
|
-
this.handleMessage(req, res, url);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
if (url.pathname === '/' && req.method === 'GET') {
|
|
37
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
38
|
-
res.end(JSON.stringify({
|
|
39
|
-
name: 'brain',
|
|
40
|
-
version: '1.8.0',
|
|
41
|
-
protocol: 'MCP',
|
|
42
|
-
transport: 'sse',
|
|
43
|
-
endpoints: {
|
|
44
|
-
sse: '/sse',
|
|
45
|
-
messages: '/messages',
|
|
46
|
-
},
|
|
47
|
-
clients: this.transports.size,
|
|
48
|
-
}));
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
52
|
-
res.end('Not Found');
|
|
53
|
-
});
|
|
54
|
-
this.server.listen(this.port, () => {
|
|
55
|
-
this.logger.info(`MCP HTTP server (SSE) started on http://localhost:${this.port}`);
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
stop() {
|
|
59
|
-
for (const transport of this.transports.values()) {
|
|
60
|
-
try {
|
|
61
|
-
transport.close?.();
|
|
62
|
-
}
|
|
63
|
-
catch { /* ignore */ }
|
|
64
|
-
}
|
|
65
|
-
this.transports.clear();
|
|
66
|
-
this.server?.close();
|
|
67
|
-
this.server = null;
|
|
68
|
-
this.logger.info('MCP HTTP server stopped');
|
|
69
|
-
}
|
|
70
|
-
getClientCount() {
|
|
71
|
-
return this.transports.size;
|
|
72
|
-
}
|
|
73
|
-
async handleSSE(res) {
|
|
74
|
-
try {
|
|
75
|
-
const transport = new SSEServerTransport('/messages', res);
|
|
76
|
-
const sessionId = transport.sessionId ?? randomUUID();
|
|
77
|
-
this.transports.set(sessionId, transport);
|
|
78
|
-
const server = new McpServer({
|
|
79
|
-
name: 'brain',
|
|
80
|
-
version: '1.8.0',
|
|
81
|
-
});
|
|
82
|
-
registerToolsDirect(server, this.router);
|
|
83
|
-
res.on('close', () => {
|
|
84
|
-
this.transports.delete(sessionId);
|
|
85
|
-
this.logger.debug(`MCP SSE client disconnected: ${sessionId}`);
|
|
86
|
-
});
|
|
87
|
-
await server.connect(transport);
|
|
88
|
-
this.logger.info(`MCP SSE client connected: ${sessionId}`);
|
|
89
|
-
}
|
|
90
|
-
catch (err) {
|
|
91
|
-
this.logger.error('MCP SSE connection error:', err);
|
|
92
|
-
if (!res.headersSent) {
|
|
93
|
-
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
|
94
|
-
res.end('Internal Server Error');
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
async handleMessage(req, res, url) {
|
|
99
|
-
try {
|
|
100
|
-
const sessionId = url.searchParams.get('sessionId');
|
|
101
|
-
if (!sessionId) {
|
|
102
|
-
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
103
|
-
res.end('Missing sessionId parameter');
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
const transport = this.transports.get(sessionId);
|
|
107
|
-
if (!transport) {
|
|
108
|
-
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
109
|
-
res.end('Session not found. Connect to /sse first.');
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
await transport.handlePostMessage(req, res);
|
|
113
|
-
}
|
|
114
|
-
catch (err) {
|
|
115
|
-
this.logger.error('MCP message error:', err);
|
|
116
|
-
if (!res.headersSent) {
|
|
117
|
-
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
|
118
|
-
res.end('Internal Server Error');
|
|
119
|
-
}
|
|
120
|
-
}
|
|
6
|
+
this.inner = new CoreMcpHttpServer(port, router, { name: 'brain', version: '2.0.0' }, (server, _r) => registerToolsDirect(server, router));
|
|
121
7
|
}
|
|
8
|
+
start() { this.inner.start(); }
|
|
9
|
+
stop() { this.inner.stop(); }
|
|
10
|
+
getClientCount() { return this.inner.getClientCount(); }
|
|
122
11
|
}
|
|
123
12
|
//# sourceMappingURL=http-server.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-server.js","sourceRoot":"","sources":["../../src/mcp/http-server.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"http-server.js","sourceRoot":"","sources":["../../src/mcp/http-server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,IAAI,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAEzE,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,OAAO,aAAa;IAChB,KAAK,CAAoB;IAEjC,YAAY,IAAY,EAAE,MAAiB;QACzC,IAAI,CAAC,KAAK,GAAG,IAAI,iBAAiB,CAChC,IAAI,EACJ,MAAM,EACN,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,EACnC,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,CACpD,CAAC;IACJ,CAAC;IAED,KAAK,KAAW,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACrC,IAAI,KAAW,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACnC,cAAc,KAAa,OAAO,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC;CACjE"}
|
package/dist/mcp/server.js
CHANGED
|
@@ -1,68 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
-
import { spawn } from 'node:child_process';
|
|
1
|
+
import { startMcpServer as coreStartMcpServer } from '@timmeck/brain-core';
|
|
4
2
|
import path from 'node:path';
|
|
5
|
-
import { IpcClient } from '../ipc/client.js';
|
|
6
|
-
import { getPipeName } from '../utils/paths.js';
|
|
7
3
|
import { registerTools } from './tools.js';
|
|
8
|
-
function spawnDaemon() {
|
|
9
|
-
const entryPoint = path.resolve(import.meta.dirname, '../index.ts');
|
|
10
|
-
const child = spawn(process.execPath, [
|
|
11
|
-
'--import', 'tsx',
|
|
12
|
-
entryPoint, 'daemon',
|
|
13
|
-
], {
|
|
14
|
-
detached: true,
|
|
15
|
-
stdio: 'ignore',
|
|
16
|
-
cwd: path.resolve(import.meta.dirname, '../..'),
|
|
17
|
-
});
|
|
18
|
-
child.unref();
|
|
19
|
-
process.stderr.write(`Brain: Auto-started daemon (PID: ${child.pid})\n`);
|
|
20
|
-
}
|
|
21
|
-
async function connectWithRetry(ipc, retries, delayMs) {
|
|
22
|
-
for (let i = 0; i < retries; i++) {
|
|
23
|
-
try {
|
|
24
|
-
await ipc.connect();
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
catch {
|
|
28
|
-
if (i < retries - 1) {
|
|
29
|
-
await new Promise(r => setTimeout(r, delayMs));
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
throw new Error('Could not connect to daemon after retries');
|
|
34
|
-
}
|
|
35
4
|
export async function startMcpServer() {
|
|
36
|
-
|
|
5
|
+
await coreStartMcpServer({
|
|
37
6
|
name: 'brain',
|
|
38
|
-
version: '
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
try {
|
|
42
|
-
await ipc.connect();
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
// Daemon not running — auto-start it
|
|
46
|
-
process.stderr.write('Brain: Daemon not running, starting automatically...\n');
|
|
47
|
-
spawnDaemon();
|
|
48
|
-
try {
|
|
49
|
-
await connectWithRetry(ipc, 10, 500);
|
|
50
|
-
}
|
|
51
|
-
catch {
|
|
52
|
-
process.stderr.write('Brain: Could not connect to daemon after auto-start. Check logs.\n');
|
|
53
|
-
process.exit(1);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
registerTools(server, ipc);
|
|
57
|
-
const transport = new StdioServerTransport();
|
|
58
|
-
await server.connect(transport);
|
|
59
|
-
process.on('SIGINT', () => {
|
|
60
|
-
ipc.disconnect();
|
|
61
|
-
process.exit(0);
|
|
62
|
-
});
|
|
63
|
-
process.on('SIGTERM', () => {
|
|
64
|
-
ipc.disconnect();
|
|
65
|
-
process.exit(0);
|
|
7
|
+
version: '2.0.0',
|
|
8
|
+
entryPoint: path.resolve(import.meta.dirname, '../index.ts'),
|
|
9
|
+
registerTools,
|
|
66
10
|
});
|
|
67
11
|
}
|
|
68
12
|
//# sourceMappingURL=server.js.map
|
package/dist/mcp/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,IAAI,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC3E,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,kBAAkB,CAAC;QACvB,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,OAAO;QAChB,UAAU,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC;QAC5D,aAAa;KACd,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { templateMessage, generateFingerprint } from '../../matching/fingerprint.js';
|
|
3
|
+
function frame(overrides = {}) {
|
|
4
|
+
return {
|
|
5
|
+
function_name: null,
|
|
6
|
+
file_path: null,
|
|
7
|
+
line_number: null,
|
|
8
|
+
column_number: null,
|
|
9
|
+
normalized: null,
|
|
10
|
+
...overrides,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
describe('templateMessage', () => {
|
|
14
|
+
it('replaces Unix file paths with <PATH>', () => {
|
|
15
|
+
const result = templateMessage('Error in /app/src/main.ts');
|
|
16
|
+
expect(result).toContain('<PATH>');
|
|
17
|
+
expect(result).not.toContain('/app/src/main.ts');
|
|
18
|
+
});
|
|
19
|
+
it('replaces Windows file paths with <PATH>', () => {
|
|
20
|
+
const result = templateMessage('Error in C:\\Users\\test\\file.ts');
|
|
21
|
+
expect(result).toContain('<PATH>');
|
|
22
|
+
expect(result).not.toContain('C:\\Users\\test\\file.ts');
|
|
23
|
+
});
|
|
24
|
+
it('replaces line:col references with <LINE>:<COL>', () => {
|
|
25
|
+
const result = templateMessage('at file.ts:42:10');
|
|
26
|
+
expect(result).toContain('<LINE>:<COL>');
|
|
27
|
+
expect(result).not.toContain(':42:10');
|
|
28
|
+
});
|
|
29
|
+
it('replaces "line N" with "line <LINE>"', () => {
|
|
30
|
+
const result = templateMessage('Syntax error on line 55');
|
|
31
|
+
expect(result).toContain('line <LINE>');
|
|
32
|
+
expect(result).not.toContain('line 55');
|
|
33
|
+
});
|
|
34
|
+
it('replaces hex addresses with <ADDR>', () => {
|
|
35
|
+
const result = templateMessage('Segfault at 0x7fff5fbff8c0');
|
|
36
|
+
expect(result).toContain('<ADDR>');
|
|
37
|
+
expect(result).not.toContain('0x7fff5fbff8c0');
|
|
38
|
+
});
|
|
39
|
+
it('replaces UUIDs with <UUID>', () => {
|
|
40
|
+
const result = templateMessage('Request 550e8400-e29b-41d4-a716-446655440000 failed');
|
|
41
|
+
expect(result).toContain('<UUID>');
|
|
42
|
+
expect(result).not.toContain('550e8400-e29b-41d4-a716-446655440000');
|
|
43
|
+
});
|
|
44
|
+
it('normalizes timestamps (line:col regex fires first on HH:MM:SS)', () => {
|
|
45
|
+
const result = templateMessage('Event at 2024-01-15T10:30:00 crashed');
|
|
46
|
+
// The :30:00 portion is caught by the line:col regex before the timestamp regex,
|
|
47
|
+
// so the timestamp is still normalized -- just not as <TIMESTAMP>
|
|
48
|
+
expect(result).not.toContain('2024-01-15T10:30:00');
|
|
49
|
+
expect(result).toContain('<LINE>:<COL>');
|
|
50
|
+
});
|
|
51
|
+
it('normalizes property access patterns', () => {
|
|
52
|
+
const a = templateMessage("Cannot read properties of undefined (reading 'map')");
|
|
53
|
+
const b = templateMessage("Cannot read properties of undefined (reading 'forEach')");
|
|
54
|
+
expect(a).toBe(b);
|
|
55
|
+
});
|
|
56
|
+
it('normalizes quoted identifiers', () => {
|
|
57
|
+
const a = templateMessage("'myVariable' is not defined");
|
|
58
|
+
const b = templateMessage("'otherVariable' is not defined");
|
|
59
|
+
expect(a).toBe(b);
|
|
60
|
+
});
|
|
61
|
+
it('produces identical templates for errors differing only in paths and lines', () => {
|
|
62
|
+
const a = templateMessage("TypeError at /home/user/app.js:10:5");
|
|
63
|
+
const b = templateMessage("TypeError at /var/deploy/app.js:99:12");
|
|
64
|
+
expect(a).toBe(b);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe('generateFingerprint', () => {
|
|
68
|
+
it('returns a 64-char hex string', () => {
|
|
69
|
+
const fp = generateFingerprint('Error', 'test message', []);
|
|
70
|
+
expect(fp).toMatch(/^[a-f0-9]{64}$/);
|
|
71
|
+
});
|
|
72
|
+
it('is deterministic for the same input', () => {
|
|
73
|
+
const frames = [frame({ function_name: 'foo', file_path: '/a/b.ts' })];
|
|
74
|
+
const fp1 = generateFingerprint('TypeError', 'msg', frames);
|
|
75
|
+
const fp2 = generateFingerprint('TypeError', 'msg', frames);
|
|
76
|
+
expect(fp1).toBe(fp2);
|
|
77
|
+
});
|
|
78
|
+
it('differs by error type', () => {
|
|
79
|
+
const fp1 = generateFingerprint('TypeError', 'msg', []);
|
|
80
|
+
const fp2 = generateFingerprint('RangeError', 'msg', []);
|
|
81
|
+
expect(fp1).not.toBe(fp2);
|
|
82
|
+
});
|
|
83
|
+
it('differs by message content', () => {
|
|
84
|
+
const fp1 = generateFingerprint('Error', 'cannot read property', []);
|
|
85
|
+
const fp2 = generateFingerprint('Error', 'null pointer exception', []);
|
|
86
|
+
expect(fp1).not.toBe(fp2);
|
|
87
|
+
});
|
|
88
|
+
it('uses only top 3 stack frames', () => {
|
|
89
|
+
const manyFrames = Array.from({ length: 10 }, (_, i) => frame({ function_name: `fn${i}`, file_path: `/src/file${i}.ts` }));
|
|
90
|
+
const threeFrames = manyFrames.slice(0, 3);
|
|
91
|
+
const fpMany = generateFingerprint('Error', 'msg', manyFrames);
|
|
92
|
+
const fpThree = generateFingerprint('Error', 'msg', threeFrames);
|
|
93
|
+
expect(fpMany).toBe(fpThree);
|
|
94
|
+
});
|
|
95
|
+
it('handles frames with null function names', () => {
|
|
96
|
+
const frames = [frame({ function_name: null, file_path: '/src/app.ts' })];
|
|
97
|
+
const fp = generateFingerprint('Error', 'msg', frames);
|
|
98
|
+
expect(fp).toMatch(/^[a-f0-9]{64}$/);
|
|
99
|
+
});
|
|
100
|
+
it('handles frames with null file paths', () => {
|
|
101
|
+
const frames = [frame({ function_name: 'foo', file_path: null })];
|
|
102
|
+
const fp = generateFingerprint('Error', 'msg', frames);
|
|
103
|
+
expect(fp).toMatch(/^[a-f0-9]{64}$/);
|
|
104
|
+
});
|
|
105
|
+
it('same error at different file locations produces the same fingerprint', () => {
|
|
106
|
+
const frames1 = [frame({ function_name: 'handler', file_path: '/app/v1/server.ts' })];
|
|
107
|
+
const frames2 = [frame({ function_name: 'handler', file_path: '/deploy/v2/server.ts' })];
|
|
108
|
+
const fp1 = generateFingerprint('TypeError', "Cannot read properties of undefined (reading 'name')", frames1);
|
|
109
|
+
const fp2 = generateFingerprint('TypeError', "Cannot read properties of undefined (reading 'name')", frames2);
|
|
110
|
+
// basename of file_path is 'server.ts' in both cases, so fingerprints should match
|
|
111
|
+
expect(fp1).toBe(fp2);
|
|
112
|
+
});
|
|
113
|
+
it('empty frames still produces a valid fingerprint', () => {
|
|
114
|
+
const fp = generateFingerprint('SyntaxError', 'Unexpected token', []);
|
|
115
|
+
expect(fp).toMatch(/^[a-f0-9]{64}$/);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
//# sourceMappingURL=fingerprint.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fingerprint.test.js","sourceRoot":"","sources":["../../../src/signals/__tests__/fingerprint.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,11 +1 @@
|
|
|
1
|
-
export
|
|
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
|
-
}
|
|
1
|
+
export type { IpcMessage } from '@timmeck/brain-core';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { sha256 } from '../hash.js';
|
|
3
|
+
describe('sha256', () => {
|
|
4
|
+
it('returns a 64-character hex string', () => {
|
|
5
|
+
const hash = sha256('hello');
|
|
6
|
+
expect(hash).toHaveLength(64);
|
|
7
|
+
expect(hash).toMatch(/^[a-f0-9]{64}$/);
|
|
8
|
+
});
|
|
9
|
+
it('produces consistent output for same input', () => {
|
|
10
|
+
expect(sha256('test')).toBe(sha256('test'));
|
|
11
|
+
});
|
|
12
|
+
it('produces different output for different inputs', () => {
|
|
13
|
+
expect(sha256('a')).not.toBe(sha256('b'));
|
|
14
|
+
});
|
|
15
|
+
it('matches known SHA-256 digest for empty string', () => {
|
|
16
|
+
// SHA-256 of "" is well-known
|
|
17
|
+
expect(sha256('')).toBe('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855');
|
|
18
|
+
});
|
|
19
|
+
it('matches known SHA-256 digest for "hello"', () => {
|
|
20
|
+
expect(sha256('hello')).toBe('2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824');
|
|
21
|
+
});
|
|
22
|
+
it('handles unicode input', () => {
|
|
23
|
+
const hash = sha256('\u00fc\u00f6\u00e4');
|
|
24
|
+
expect(hash).toMatch(/^[a-f0-9]{64}$/);
|
|
25
|
+
});
|
|
26
|
+
it('handles long input', () => {
|
|
27
|
+
const long = 'x'.repeat(10_000);
|
|
28
|
+
const hash = sha256(long);
|
|
29
|
+
expect(hash).toMatch(/^[a-f0-9]{64}$/);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
//# sourceMappingURL=hash.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.test.js","sourceRoot":"","sources":["../../../src/utils/__tests__/hash.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpC,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;IACtB,EAAE,CAAC,mCAAmC,EAAE,GAAG,EAAE;QAC3C,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,8BAA8B;QAC9B,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CACrB,kEAAkE,CACnE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,GAAG,EAAE;QAClD,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAC1B,kEAAkE,CACnE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,oBAAoB,CAAC,CAAC;QAC1C,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAC5B,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAChC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|