@timmeck/marketing-brain 0.2.0 → 0.3.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 +28 -13
- package/dist/cli/colors.d.ts +11 -24
- package/dist/cli/colors.js +3 -46
- package/dist/cli/colors.js.map +1 -1
- package/dist/cli/commands/dashboard.js +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/config.js +3 -3
- 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/hooks/post-tool-use.d.ts +2 -0
- package/dist/hooks/post-tool-use.js +182 -0
- package/dist/hooks/post-tool-use.js.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/ipc/__tests__/protocol.test.d.ts +1 -0
- package/dist/ipc/__tests__/protocol.test.js +129 -0
- package/dist/ipc/__tests__/protocol.test.js.map +1 -0
- package/dist/ipc/client.d.ts +1 -13
- package/dist/ipc/client.js +1 -92
- 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 -14
- package/dist/ipc/server.js +1 -129
- package/dist/ipc/server.js.map +1 -1
- package/dist/marketing-core.d.ts +1 -0
- package/dist/marketing-core.js +6 -1
- package/dist/marketing-core.js.map +1 -1
- package/dist/mcp/server.js +5 -60
- package/dist/mcp/server.js.map +1 -1
- 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 +30 -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 +63 -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/.mcp.json +0 -9
- package/src/api/server.ts +0 -86
- package/src/cli/colors.ts +0 -59
- package/src/cli/commands/campaign.ts +0 -66
- package/src/cli/commands/config.ts +0 -168
- package/src/cli/commands/dashboard.ts +0 -165
- package/src/cli/commands/doctor.ts +0 -110
- package/src/cli/commands/export.ts +0 -40
- package/src/cli/commands/import.ts +0 -84
- package/src/cli/commands/insights.ts +0 -44
- package/src/cli/commands/learn.ts +0 -24
- package/src/cli/commands/network.ts +0 -71
- package/src/cli/commands/post.ts +0 -47
- package/src/cli/commands/query.ts +0 -108
- package/src/cli/commands/rules.ts +0 -27
- package/src/cli/commands/start.ts +0 -100
- package/src/cli/commands/status.ts +0 -73
- package/src/cli/commands/stop.ts +0 -33
- package/src/cli/commands/suggest.ts +0 -64
- package/src/cli/ipc-helper.ts +0 -22
- package/src/cli/update-check.ts +0 -63
- package/src/config.ts +0 -110
- package/src/dashboard/renderer.ts +0 -136
- package/src/dashboard/server.ts +0 -140
- package/src/db/connection.ts +0 -22
- package/src/db/migrations/001_core_schema.ts +0 -63
- package/src/db/migrations/002_learning_schema.ts +0 -46
- package/src/db/migrations/003_synapse_schema.ts +0 -27
- package/src/db/migrations/004_insights_schema.ts +0 -38
- package/src/db/migrations/005_fts_indexes.ts +0 -77
- package/src/db/migrations/index.ts +0 -62
- package/src/db/repositories/audience.repository.ts +0 -53
- package/src/db/repositories/campaign.repository.ts +0 -72
- package/src/db/repositories/engagement.repository.ts +0 -108
- package/src/db/repositories/insight.repository.ts +0 -100
- package/src/db/repositories/post.repository.ts +0 -123
- package/src/db/repositories/rule.repository.ts +0 -87
- package/src/db/repositories/strategy.repository.ts +0 -82
- package/src/db/repositories/synapse.repository.ts +0 -148
- package/src/db/repositories/template.repository.ts +0 -76
- package/src/index.ts +0 -69
- package/src/ipc/client.ts +0 -110
- package/src/ipc/protocol.ts +0 -35
- package/src/ipc/router.ts +0 -126
- package/src/ipc/server.ts +0 -140
- package/src/learning/confidence-scorer.ts +0 -36
- package/src/learning/learning-engine.ts +0 -254
- package/src/marketing-core.ts +0 -285
- package/src/mcp/server.ts +0 -72
- package/src/mcp/tools.ts +0 -216
- package/src/research/research-engine.ts +0 -226
- package/src/services/analytics.service.ts +0 -73
- package/src/services/audience.service.ts +0 -40
- package/src/services/campaign.service.ts +0 -80
- package/src/services/insight.service.ts +0 -54
- package/src/services/post.service.ts +0 -116
- package/src/services/rule.service.ts +0 -90
- package/src/services/strategy.service.ts +0 -53
- package/src/services/synapse.service.ts +0 -32
- package/src/services/template.service.ts +0 -50
- package/src/synapses/activation.ts +0 -80
- package/src/synapses/decay.ts +0 -38
- package/src/synapses/hebbian.ts +0 -68
- package/src/synapses/pathfinder.ts +0 -81
- package/src/synapses/synapse-manager.ts +0 -115
- package/src/types/config.types.ts +0 -79
- package/src/types/ipc.types.ts +0 -8
- package/src/types/post.types.ts +0 -156
- package/src/types/synapse.types.ts +0 -43
- package/src/utils/events.ts +0 -44
- package/src/utils/hash.ts +0 -5
- package/src/utils/logger.ts +0 -48
- package/src/utils/paths.ts +0 -19
- package/tsconfig.json +0 -18
package/dist/mcp/server.js
CHANGED
|
@@ -1,67 +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(`Marketing 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: 'marketing-brain',
|
|
38
|
-
version: '0.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
try {
|
|
42
|
-
await ipc.connect();
|
|
43
|
-
}
|
|
44
|
-
catch {
|
|
45
|
-
process.stderr.write('Marketing Brain: Daemon not running, starting automatically...\n');
|
|
46
|
-
spawnDaemon();
|
|
47
|
-
try {
|
|
48
|
-
await connectWithRetry(ipc, 10, 500);
|
|
49
|
-
}
|
|
50
|
-
catch {
|
|
51
|
-
process.stderr.write('Marketing Brain: Could not connect to daemon after auto-start.\n');
|
|
52
|
-
process.exit(1);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
registerTools(server, ipc);
|
|
56
|
-
const transport = new StdioServerTransport();
|
|
57
|
-
await server.connect(transport);
|
|
58
|
-
process.on('SIGINT', () => {
|
|
59
|
-
ipc.disconnect();
|
|
60
|
-
process.exit(0);
|
|
61
|
-
});
|
|
62
|
-
process.on('SIGTERM', () => {
|
|
63
|
-
ipc.disconnect();
|
|
64
|
-
process.exit(0);
|
|
7
|
+
version: '0.3.0',
|
|
8
|
+
entryPoint: path.resolve(import.meta.dirname, '../index.ts'),
|
|
9
|
+
registerTools,
|
|
65
10
|
});
|
|
66
11
|
}
|
|
67
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,iBAAiB;QACvB,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"}
|
|
@@ -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,30 @@
|
|
|
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 result = sha256('hello');
|
|
6
|
+
expect(result).toHaveLength(64);
|
|
7
|
+
expect(result).toMatch(/^[0-9a-f]{64}$/);
|
|
8
|
+
});
|
|
9
|
+
it('produces correct hash for known input', () => {
|
|
10
|
+
// SHA-256 of "hello" is well-known
|
|
11
|
+
expect(sha256('hello')).toBe('2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824');
|
|
12
|
+
});
|
|
13
|
+
it('produces different hashes for different inputs', () => {
|
|
14
|
+
expect(sha256('foo')).not.toBe(sha256('bar'));
|
|
15
|
+
});
|
|
16
|
+
it('produces the same hash for the same input', () => {
|
|
17
|
+
expect(sha256('deterministic')).toBe(sha256('deterministic'));
|
|
18
|
+
});
|
|
19
|
+
it('handles empty string', () => {
|
|
20
|
+
const result = sha256('');
|
|
21
|
+
expect(result).toHaveLength(64);
|
|
22
|
+
expect(result).toBe('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855');
|
|
23
|
+
});
|
|
24
|
+
it('handles unicode input', () => {
|
|
25
|
+
const result = sha256('Hallo Welt! 🚀');
|
|
26
|
+
expect(result).toHaveLength(64);
|
|
27
|
+
expect(result).toMatch(/^[0-9a-f]{64}$/);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
//# 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,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;QAC/B,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QAChC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,mCAAmC;QACnC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAC1B,kEAAkE,CACnE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACxD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,MAAM,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC,CAAC;QAC1B,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QAChC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CACjB,kEAAkE,CACnE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAC/B,MAAM,MAAM,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;QAChC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import os from 'node:os';
|
|
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.txt')).toBe('C:/Users/test/file.txt');
|
|
8
|
+
});
|
|
9
|
+
it('leaves forward slashes unchanged', () => {
|
|
10
|
+
expect(normalizePath('/home/user/file.txt')).toBe('/home/user/file.txt');
|
|
11
|
+
});
|
|
12
|
+
it('handles mixed separators', () => {
|
|
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 separators', () => {
|
|
19
|
+
expect(normalizePath('file.txt')).toBe('file.txt');
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
describe('getDataDir', () => {
|
|
23
|
+
const originalEnv = process.env['MARKETING_BRAIN_DATA_DIR'];
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
if (originalEnv !== undefined) {
|
|
26
|
+
process.env['MARKETING_BRAIN_DATA_DIR'] = originalEnv;
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
delete process.env['MARKETING_BRAIN_DATA_DIR'];
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
it('returns env-based directory when MARKETING_BRAIN_DATA_DIR is set', () => {
|
|
33
|
+
process.env['MARKETING_BRAIN_DATA_DIR'] = '/custom/data';
|
|
34
|
+
const result = getDataDir();
|
|
35
|
+
expect(result).toBe(path.resolve('/custom/data'));
|
|
36
|
+
});
|
|
37
|
+
it('returns homedir-based directory when env is not set', () => {
|
|
38
|
+
delete process.env['MARKETING_BRAIN_DATA_DIR'];
|
|
39
|
+
const result = getDataDir();
|
|
40
|
+
expect(result).toBe(path.join(os.homedir(), '.marketing-brain'));
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe('getPipeName', () => {
|
|
44
|
+
it('uses default name when no argument is given', () => {
|
|
45
|
+
const result = getPipeName();
|
|
46
|
+
if (process.platform === 'win32') {
|
|
47
|
+
expect(result).toBe('\\\\.\\pipe\\marketing-brain');
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
expect(result).toBe(path.join(os.tmpdir(), 'marketing-brain.sock'));
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
it('uses custom name when provided', () => {
|
|
54
|
+
const result = getPipeName('my-app');
|
|
55
|
+
if (process.platform === 'win32') {
|
|
56
|
+
expect(result).toBe('\\\\.\\pipe\\my-app');
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
expect(result).toBe(path.join(os.tmpdir(), 'my-app.sock'));
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
//# 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 MarketingEvents = {
|
|
3
3
|
'post:created': {
|
|
4
4
|
postId: number;
|
|
5
5
|
campaignId: number | null;
|
|
@@ -46,12 +46,8 @@ export interface MarketingEvents {
|
|
|
46
46
|
synapseId: number;
|
|
47
47
|
newWeight: number;
|
|
48
48
|
};
|
|
49
|
-
}
|
|
49
|
+
};
|
|
50
50
|
export type MarketingEventName = keyof MarketingEvents;
|
|
51
|
-
export declare class TypedEventBus extends
|
|
52
|
-
emit<K extends MarketingEventName>(event: K, data: MarketingEvents[K]): boolean;
|
|
53
|
-
on<K extends MarketingEventName>(event: K, listener: (data: MarketingEvents[K]) => void): this;
|
|
54
|
-
once<K extends MarketingEventName>(event: K, listener: (data: MarketingEvents[K]) => void): this;
|
|
55
|
-
off<K extends MarketingEventName>(event: K, listener: (data: MarketingEvents[K]) => void): this;
|
|
51
|
+
export declare class TypedEventBus extends GenericEventBus<MarketingEvents> {
|
|
56
52
|
}
|
|
57
53
|
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;AAkBvE,MAAM,OAAO,aAAc,SAAQ,eAAgC;CAAG;AAEtE,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: 'MARKETING_BRAIN_LOG_LEVEL',
|
|
7
|
+
defaultFilename: 'marketing-brain.log',
|
|
8
|
+
dataDir: opts?.file ? undefined : getDataDir(),
|
|
9
|
+
});
|
|
38
10
|
}
|
|
11
|
+
export { getLogger, resetLogger };
|
|
39
12
|
//# sourceMappingURL=logger.js.map
|
package/dist/utils/logger.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,IAAI,gBAAgB,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAE/F,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,UAAU,YAAY,CAAC,IAA6E;IACxG,OAAO,gBAAgB,CAAC;QACtB,GAAG,IAAI;QACP,MAAM,EAAE,2BAA2B;QACnC,eAAe,EAAE,qBAAqB;QACtC,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,EAAE;KAC/C,CAAC,CAAC;AACL,CAAC;AAED,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC"}
|
package/dist/utils/paths.d.ts
CHANGED
package/dist/utils/paths.js
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
export function normalizePath(filePath) {
|
|
4
|
-
return filePath.replace(/\\/g, '/');
|
|
5
|
-
}
|
|
1
|
+
import { normalizePath, getDataDir as coreGetDataDir, getPipeName as coreGetPipeName } from '@timmeck/brain-core';
|
|
2
|
+
export { normalizePath };
|
|
6
3
|
export function getDataDir() {
|
|
7
|
-
|
|
8
|
-
if (envDir)
|
|
9
|
-
return path.resolve(envDir);
|
|
10
|
-
return path.join(os.homedir(), '.marketing-brain');
|
|
4
|
+
return coreGetDataDir('MARKETING_BRAIN_DATA_DIR', '.marketing-brain');
|
|
11
5
|
}
|
|
12
6
|
export function getPipeName(name = 'marketing-brain') {
|
|
13
|
-
|
|
14
|
-
return `\\\\.\\pipe\\${name}`;
|
|
15
|
-
}
|
|
16
|
-
return path.join(os.tmpdir(), `${name}.sock`);
|
|
7
|
+
return coreGetPipeName(name);
|
|
17
8
|
}
|
|
18
9
|
//# sourceMappingURL=paths.js.map
|
package/dist/utils/paths.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,IAAI,cAAc,EAAE,WAAW,IAAI,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAElH,OAAO,EAAE,aAAa,EAAE,CAAC;AAEzB,MAAM,UAAU,UAAU;IACxB,OAAO,cAAc,CAAC,0BAA0B,EAAE,kBAAkB,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,OAAe,iBAAiB;IAC1D,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@timmeck/marketing-brain",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Self-learning marketing intelligence system with Hebbian synapse network",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
28
|
+
"@timmeck/brain-core": "^1.1.0",
|
|
28
29
|
"better-sqlite3": "^11.7.0",
|
|
29
30
|
"chalk": "^5.6.2",
|
|
30
31
|
"commander": "^13.0.0",
|
package/.mcp.json
DELETED
package/src/api/server.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import http from 'node:http';
|
|
2
|
-
import { getLogger } from '../utils/logger.js';
|
|
3
|
-
import type { IpcRouter } from '../ipc/router.js';
|
|
4
|
-
|
|
5
|
-
interface ApiServerOptions {
|
|
6
|
-
port: number;
|
|
7
|
-
router: IpcRouter;
|
|
8
|
-
apiKey?: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export class ApiServer {
|
|
12
|
-
private server: http.Server | null = null;
|
|
13
|
-
private logger = getLogger();
|
|
14
|
-
|
|
15
|
-
constructor(private opts: ApiServerOptions) {}
|
|
16
|
-
|
|
17
|
-
start(): void {
|
|
18
|
-
this.server = http.createServer((req, res) => {
|
|
19
|
-
// CORS
|
|
20
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
21
|
-
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
22
|
-
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
|
|
23
|
-
|
|
24
|
-
if (req.method === 'OPTIONS') {
|
|
25
|
-
res.writeHead(204);
|
|
26
|
-
res.end();
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Auth check
|
|
31
|
-
if (this.opts.apiKey) {
|
|
32
|
-
const auth = req.headers.authorization;
|
|
33
|
-
if (!auth || auth !== `Bearer ${this.opts.apiKey}`) {
|
|
34
|
-
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
35
|
-
res.end(JSON.stringify({ error: 'Unauthorized' }));
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Health check
|
|
41
|
-
if (req.url === '/api/v1/health') {
|
|
42
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
43
|
-
res.end(JSON.stringify({ status: 'ok', service: 'marketing-brain' }));
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Methods list
|
|
48
|
-
if (req.url === '/api/v1/methods') {
|
|
49
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
50
|
-
res.end(JSON.stringify({ methods: this.opts.router.listMethods() }));
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// RPC endpoint
|
|
55
|
-
if (req.url === '/api/v1/rpc' && req.method === 'POST') {
|
|
56
|
-
let body = '';
|
|
57
|
-
req.on('data', chunk => { body += chunk; });
|
|
58
|
-
req.on('end', () => {
|
|
59
|
-
try {
|
|
60
|
-
const { method, params } = JSON.parse(body);
|
|
61
|
-
const result = this.opts.router.handle(method, params);
|
|
62
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
63
|
-
res.end(JSON.stringify({ result }));
|
|
64
|
-
} catch (err) {
|
|
65
|
-
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
66
|
-
res.end(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }));
|
|
67
|
-
}
|
|
68
|
-
});
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
73
|
-
res.end(JSON.stringify({ error: 'Not Found' }));
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
this.server.listen(this.opts.port, () => {
|
|
77
|
-
this.logger.info(`API server listening on port ${this.opts.port}`);
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
stop(): void {
|
|
82
|
-
this.server?.close();
|
|
83
|
-
this.server = null;
|
|
84
|
-
this.logger.info('API server stopped');
|
|
85
|
-
}
|
|
86
|
-
}
|
package/src/cli/colors.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
|
|
3
|
-
export const c = {
|
|
4
|
-
blue: chalk.hex('#5b9cff'),
|
|
5
|
-
purple: chalk.hex('#b47aff'),
|
|
6
|
-
cyan: chalk.hex('#47e5ff'),
|
|
7
|
-
green: chalk.hex('#3dffa0'),
|
|
8
|
-
red: chalk.hex('#ff5577'),
|
|
9
|
-
orange: chalk.hex('#ffb347'),
|
|
10
|
-
dim: chalk.hex('#8b8fb0'),
|
|
11
|
-
dimmer: chalk.hex('#4a4d6e'),
|
|
12
|
-
|
|
13
|
-
label: chalk.hex('#8b8fb0'),
|
|
14
|
-
value: chalk.white.bold,
|
|
15
|
-
heading: chalk.hex('#5b9cff').bold,
|
|
16
|
-
success: chalk.hex('#3dffa0').bold,
|
|
17
|
-
error: chalk.hex('#ff5577').bold,
|
|
18
|
-
warn: chalk.hex('#ffb347').bold,
|
|
19
|
-
info: chalk.hex('#47e5ff'),
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export const icons = {
|
|
23
|
-
megaphone: '📣',
|
|
24
|
-
check: '✓',
|
|
25
|
-
cross: '✗',
|
|
26
|
-
arrow: '→',
|
|
27
|
-
dot: '●',
|
|
28
|
-
bar: '█',
|
|
29
|
-
barLight: '░',
|
|
30
|
-
dash: '─',
|
|
31
|
-
star: '★',
|
|
32
|
-
bolt: '⚡',
|
|
33
|
-
chart: '📊',
|
|
34
|
-
post: '📝',
|
|
35
|
-
campaign: '🎯',
|
|
36
|
-
synapse: '🔗',
|
|
37
|
-
insight: '💡',
|
|
38
|
-
rule: '📏',
|
|
39
|
-
template: '📋',
|
|
40
|
-
warn: '⚠',
|
|
41
|
-
error: '❌',
|
|
42
|
-
ok: '✅',
|
|
43
|
-
clock: '⏱',
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export function header(title: string, icon?: string): string {
|
|
47
|
-
const prefix = icon ? `${icon} ` : '';
|
|
48
|
-
const line = c.dimmer(icons.dash.repeat(40));
|
|
49
|
-
return `\n${line}\n${prefix}${c.heading(title)}\n${line}`;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function keyValue(key: string, value: string | number, indent = 2): string {
|
|
53
|
-
const pad = ' '.repeat(indent);
|
|
54
|
-
return `${pad}${c.label(key + ':')} ${c.value(String(value))}`;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function divider(width = 40): string {
|
|
58
|
-
return c.dimmer(icons.dash.repeat(width));
|
|
59
|
-
}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { withIpc } from '../ipc-helper.js';
|
|
3
|
-
import { c, icons } from '../colors.js';
|
|
4
|
-
|
|
5
|
-
export function campaignCommand(): Command {
|
|
6
|
-
const cmd = new Command('campaign')
|
|
7
|
-
.description('Manage campaigns');
|
|
8
|
-
|
|
9
|
-
cmd.command('create')
|
|
10
|
-
.description('Create a new campaign')
|
|
11
|
-
.argument('<name>', 'Campaign name')
|
|
12
|
-
.option('-b, --brand <brand>', 'Brand name')
|
|
13
|
-
.option('-g, --goal <goal>', 'Campaign goal')
|
|
14
|
-
.option('-p, --platform <platform>', 'Target platform')
|
|
15
|
-
.action(async (name, opts) => {
|
|
16
|
-
await withIpc(async (client) => {
|
|
17
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
-
const result: any = await client.request('campaign.create', {
|
|
19
|
-
name,
|
|
20
|
-
brand: opts.brand,
|
|
21
|
-
goal: opts.goal,
|
|
22
|
-
platform: opts.platform,
|
|
23
|
-
});
|
|
24
|
-
console.log(`${icons.ok} ${c.success('Campaign created!')} ${c.dim(`#${result.id}: ${result.name}`)}`);
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
cmd.command('list')
|
|
29
|
-
.description('List all campaigns')
|
|
30
|
-
.action(async () => {
|
|
31
|
-
await withIpc(async (client) => {
|
|
32
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
33
|
-
const campaigns: any[] = await client.request('campaign.list') as any[];
|
|
34
|
-
if (campaigns.length === 0) {
|
|
35
|
-
console.log(`${c.dim('No campaigns yet.')}`);
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
for (const camp of campaigns) {
|
|
39
|
-
console.log(` ${icons.campaign} ${c.value(`#${camp.id}`)} ${camp.name} ${c.dim(camp.brand ?? '')} ${c.dim(camp.status)}`);
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
cmd.command('stats')
|
|
45
|
-
.description('Show campaign stats')
|
|
46
|
-
.argument('<id>', 'Campaign ID')
|
|
47
|
-
.action(async (id) => {
|
|
48
|
-
await withIpc(async (client) => {
|
|
49
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
50
|
-
const stats: any = await client.request('campaign.stats', { id: Number(id) });
|
|
51
|
-
if (!stats) {
|
|
52
|
-
console.log(`${c.dim('Campaign not found.')}`);
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
console.log(` ${icons.campaign} ${c.value(stats.campaign.name)}`);
|
|
56
|
-
console.log(` Posts: ${c.value(stats.postCount)}`);
|
|
57
|
-
console.log(` Likes: ${c.value(stats.totalLikes)}`);
|
|
58
|
-
console.log(` Comments: ${c.value(stats.totalComments)}`);
|
|
59
|
-
console.log(` Shares: ${c.value(stats.totalShares)}`);
|
|
60
|
-
console.log(` Impressions: ${c.value(stats.totalImpressions)}`);
|
|
61
|
-
console.log(` Avg Engagement: ${c.green(stats.avgEngagement.toFixed(0))}`);
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
return cmd;
|
|
66
|
-
}
|