@timmeck/brain 1.9.0 → 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/README.md +19 -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 +1 -0
- package/dist/brain.js +6 -1
- 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/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/package.json +2 -1
- package/BRAIN_PLAN.md +0 -3324
- package/reddit_post.md +0 -45
- package/src/api/server.ts +0 -395
- package/src/brain.ts +0 -313
- 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 -105
- 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/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 {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { normalizePath, getDataDir, getPipeName } from '../paths.js';
|
|
5
|
+
describe('normalizePath', () => {
|
|
6
|
+
it('converts backslashes to forward slashes', () => {
|
|
7
|
+
expect(normalizePath('C:\\Users\\test\\file.ts')).toBe('C:/Users/test/file.ts');
|
|
8
|
+
});
|
|
9
|
+
it('leaves forward slashes unchanged', () => {
|
|
10
|
+
expect(normalizePath('/home/user/file.ts')).toBe('/home/user/file.ts');
|
|
11
|
+
});
|
|
12
|
+
it('handles mixed slashes', () => {
|
|
13
|
+
expect(normalizePath('src\\utils/hash.ts')).toBe('src/utils/hash.ts');
|
|
14
|
+
});
|
|
15
|
+
it('handles empty string', () => {
|
|
16
|
+
expect(normalizePath('')).toBe('');
|
|
17
|
+
});
|
|
18
|
+
it('handles path with no slashes', () => {
|
|
19
|
+
expect(normalizePath('file.ts')).toBe('file.ts');
|
|
20
|
+
});
|
|
21
|
+
it('handles multiple consecutive backslashes', () => {
|
|
22
|
+
expect(normalizePath('a\\\\b\\\\c')).toBe('a//b//c');
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
describe('getDataDir', () => {
|
|
26
|
+
const originalEnv = process.env['BRAIN_DATA_DIR'];
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
if (originalEnv !== undefined) {
|
|
29
|
+
process.env['BRAIN_DATA_DIR'] = originalEnv;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
delete process.env['BRAIN_DATA_DIR'];
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
it('returns BRAIN_DATA_DIR when env variable is set', () => {
|
|
36
|
+
process.env['BRAIN_DATA_DIR'] = '/tmp/brain-test';
|
|
37
|
+
const dir = getDataDir();
|
|
38
|
+
expect(dir).toBe(path.resolve('/tmp/brain-test'));
|
|
39
|
+
});
|
|
40
|
+
it('resolves relative BRAIN_DATA_DIR to absolute path', () => {
|
|
41
|
+
process.env['BRAIN_DATA_DIR'] = './data';
|
|
42
|
+
const dir = getDataDir();
|
|
43
|
+
expect(path.isAbsolute(dir)).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
it('returns ~/.brain when env variable is not set', () => {
|
|
46
|
+
delete process.env['BRAIN_DATA_DIR'];
|
|
47
|
+
const dir = getDataDir();
|
|
48
|
+
expect(dir).toBe(path.join(os.homedir(), '.brain'));
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe('getPipeName', () => {
|
|
52
|
+
it('uses default name "brain"', () => {
|
|
53
|
+
const pipe = getPipeName();
|
|
54
|
+
if (process.platform === 'win32') {
|
|
55
|
+
expect(pipe).toBe('\\\\.\\pipe\\brain');
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
expect(pipe).toBe(path.join(os.tmpdir(), 'brain.sock'));
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
it('accepts a custom name', () => {
|
|
62
|
+
const pipe = getPipeName('custom');
|
|
63
|
+
if (process.platform === 'win32') {
|
|
64
|
+
expect(pipe).toBe('\\\\.\\pipe\\custom');
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
expect(pipe).toBe(path.join(os.tmpdir(), 'custom.sock'));
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
it('returns a string containing the name', () => {
|
|
71
|
+
const pipe = getPipeName('myservice');
|
|
72
|
+
expect(pipe).toContain('myservice');
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
//# sourceMappingURL=paths.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.test.js","sourceRoot":"","sources":["../../../src/utils/__tests__/paths.test.ts"],"names":[],"mappings":"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"}
|
package/dist/utils/events.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export
|
|
1
|
+
import { TypedEventBus as GenericEventBus } from '@timmeck/brain-core';
|
|
2
|
+
export type BrainEvents = {
|
|
3
3
|
'error:reported': {
|
|
4
4
|
errorId: number;
|
|
5
5
|
projectId: number;
|
|
@@ -48,12 +48,8 @@ export interface BrainEvents {
|
|
|
48
48
|
'terminal:disconnected': {
|
|
49
49
|
terminalId: number;
|
|
50
50
|
};
|
|
51
|
-
}
|
|
51
|
+
};
|
|
52
52
|
export type BrainEventName = keyof BrainEvents;
|
|
53
|
-
export declare class TypedEventBus extends
|
|
54
|
-
emit<K extends BrainEventName>(event: K, data: BrainEvents[K]): boolean;
|
|
55
|
-
on<K extends BrainEventName>(event: K, listener: (data: BrainEvents[K]) => void): this;
|
|
56
|
-
once<K extends BrainEventName>(event: K, listener: (data: BrainEvents[K]) => void): this;
|
|
57
|
-
off<K extends BrainEventName>(event: K, listener: (data: BrainEvents[K]) => void): this;
|
|
53
|
+
export declare class TypedEventBus extends GenericEventBus<BrainEvents> {
|
|
58
54
|
}
|
|
59
55
|
export declare function getEventBus(): TypedEventBus;
|
package/dist/utils/events.js
CHANGED
|
@@ -1,17 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export class TypedEventBus extends
|
|
3
|
-
emit(event, data) {
|
|
4
|
-
return super.emit(event, data);
|
|
5
|
-
}
|
|
6
|
-
on(event, listener) {
|
|
7
|
-
return super.on(event, listener);
|
|
8
|
-
}
|
|
9
|
-
once(event, listener) {
|
|
10
|
-
return super.once(event, listener);
|
|
11
|
-
}
|
|
12
|
-
off(event, listener) {
|
|
13
|
-
return super.off(event, listener);
|
|
14
|
-
}
|
|
1
|
+
import { TypedEventBus as GenericEventBus } from '@timmeck/brain-core';
|
|
2
|
+
export class TypedEventBus extends GenericEventBus {
|
|
15
3
|
}
|
|
16
4
|
let busInstance = null;
|
|
17
5
|
export function getEventBus() {
|
package/dist/utils/events.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/utils/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/utils/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,IAAI,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAmBvE,MAAM,OAAO,aAAc,SAAQ,eAA4B;CAAG;AAElE,IAAI,WAAW,GAAyB,IAAI,CAAC;AAE7C,MAAM,UAAU,WAAW;IACzB,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,WAAW,GAAG,IAAI,aAAa,EAAE,CAAC;IACpC,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC"}
|
package/dist/utils/hash.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export { sha256 } from '@timmeck/brain-core';
|
package/dist/utils/hash.js
CHANGED
package/dist/utils/hash.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/utils/hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/utils/hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/utils/logger.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { getLogger, resetLogger } from '@timmeck/brain-core';
|
|
2
|
+
import type winston from 'winston';
|
|
2
3
|
export declare function createLogger(opts?: {
|
|
3
4
|
level?: string;
|
|
4
5
|
file?: string;
|
|
5
6
|
maxSize?: number;
|
|
6
7
|
maxFiles?: number;
|
|
7
8
|
}): winston.Logger;
|
|
8
|
-
export
|
|
9
|
+
export { getLogger, resetLogger };
|
package/dist/utils/logger.js
CHANGED
|
@@ -1,39 +1,12 @@
|
|
|
1
|
-
import
|
|
2
|
-
import path from 'node:path';
|
|
1
|
+
import { createLogger as coreCreateLogger, getLogger, resetLogger } from '@timmeck/brain-core';
|
|
3
2
|
import { getDataDir } from './paths.js';
|
|
4
|
-
const { combine, timestamp, printf, colorize } = winston.format;
|
|
5
|
-
const logFormat = printf(({ level, message, timestamp, ...meta }) => {
|
|
6
|
-
const metaStr = Object.keys(meta).length ? ` ${JSON.stringify(meta)}` : '';
|
|
7
|
-
return `${timestamp} [${level}]${metaStr} ${message}`;
|
|
8
|
-
});
|
|
9
|
-
let loggerInstance = null;
|
|
10
3
|
export function createLogger(opts) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const transports = [
|
|
18
|
-
new winston.transports.File({
|
|
19
|
-
filename: logFile,
|
|
20
|
-
maxsize: maxSize,
|
|
21
|
-
maxFiles,
|
|
22
|
-
format: combine(timestamp(), logFormat),
|
|
23
|
-
}),
|
|
24
|
-
];
|
|
25
|
-
if (process.env['NODE_ENV'] !== 'production') {
|
|
26
|
-
transports.push(new winston.transports.Console({
|
|
27
|
-
format: combine(colorize(), timestamp(), logFormat),
|
|
28
|
-
}));
|
|
29
|
-
}
|
|
30
|
-
loggerInstance = winston.createLogger({ level, transports });
|
|
31
|
-
return loggerInstance;
|
|
32
|
-
}
|
|
33
|
-
export function getLogger() {
|
|
34
|
-
if (!loggerInstance) {
|
|
35
|
-
return createLogger();
|
|
36
|
-
}
|
|
37
|
-
return loggerInstance;
|
|
4
|
+
return coreCreateLogger({
|
|
5
|
+
...opts,
|
|
6
|
+
envVar: 'BRAIN_LOG_LEVEL',
|
|
7
|
+
defaultFilename: 'brain.log',
|
|
8
|
+
dataDir: opts?.file ? undefined : getDataDir(),
|
|
9
|
+
});
|
|
38
10
|
}
|
|
11
|
+
export { getLogger, resetLogger };
|
|
39
12
|
//# sourceMappingURL=logger.js.map
|