@timmeck/marketing-brain 0.2.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/.mcp.json +9 -0
- package/README.md +342 -0
- package/dashboard.html +666 -0
- package/dist/api/server.d.ts +15 -0
- package/dist/api/server.js +73 -0
- package/dist/api/server.js.map +1 -0
- package/dist/cli/colors.d.ts +43 -0
- package/dist/cli/colors.js +54 -0
- package/dist/cli/colors.js.map +1 -0
- package/dist/cli/commands/campaign.d.ts +2 -0
- package/dist/cli/commands/campaign.js +62 -0
- package/dist/cli/commands/campaign.js.map +1 -0
- package/dist/cli/commands/config.d.ts +2 -0
- package/dist/cli/commands/config.js +164 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/dashboard.d.ts +2 -0
- package/dist/cli/commands/dashboard.js +147 -0
- package/dist/cli/commands/dashboard.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +2 -0
- package/dist/cli/commands/doctor.js +111 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/export.d.ts +2 -0
- package/dist/cli/commands/export.js +37 -0
- package/dist/cli/commands/export.js.map +1 -0
- package/dist/cli/commands/import.d.ts +2 -0
- package/dist/cli/commands/import.js +76 -0
- package/dist/cli/commands/import.js.map +1 -0
- package/dist/cli/commands/insights.d.ts +2 -0
- package/dist/cli/commands/insights.js +41 -0
- package/dist/cli/commands/insights.js.map +1 -0
- package/dist/cli/commands/learn.d.ts +2 -0
- package/dist/cli/commands/learn.js +22 -0
- package/dist/cli/commands/learn.js.map +1 -0
- package/dist/cli/commands/network.d.ts +2 -0
- package/dist/cli/commands/network.js +66 -0
- package/dist/cli/commands/network.js.map +1 -0
- package/dist/cli/commands/post.d.ts +2 -0
- package/dist/cli/commands/post.js +45 -0
- package/dist/cli/commands/post.js.map +1 -0
- package/dist/cli/commands/query.d.ts +2 -0
- package/dist/cli/commands/query.js +96 -0
- package/dist/cli/commands/query.js.map +1 -0
- package/dist/cli/commands/rules.d.ts +2 -0
- package/dist/cli/commands/rules.js +25 -0
- package/dist/cli/commands/rules.js.map +1 -0
- package/dist/cli/commands/start.d.ts +2 -0
- package/dist/cli/commands/start.js +91 -0
- package/dist/cli/commands/start.js.map +1 -0
- package/dist/cli/commands/status.d.ts +2 -0
- package/dist/cli/commands/status.js +63 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/stop.d.ts +2 -0
- package/dist/cli/commands/stop.js +34 -0
- package/dist/cli/commands/stop.js.map +1 -0
- package/dist/cli/commands/suggest.d.ts +2 -0
- package/dist/cli/commands/suggest.js +57 -0
- package/dist/cli/commands/suggest.js.map +1 -0
- package/dist/cli/ipc-helper.d.ts +2 -0
- package/dist/cli/ipc-helper.js +26 -0
- package/dist/cli/ipc-helper.js.map +1 -0
- package/dist/cli/update-check.d.ts +2 -0
- package/dist/cli/update-check.js +58 -0
- package/dist/cli/update-check.js.map +1 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +111 -0
- package/dist/config.js.map +1 -0
- package/dist/dashboard/renderer.d.ts +11 -0
- package/dist/dashboard/renderer.js +112 -0
- package/dist/dashboard/renderer.js.map +1 -0
- package/dist/dashboard/server.d.ts +15 -0
- package/dist/dashboard/server.js +122 -0
- package/dist/dashboard/server.js.map +1 -0
- package/dist/db/connection.d.ts +2 -0
- package/dist/db/connection.js +19 -0
- package/dist/db/connection.js.map +1 -0
- package/dist/db/migrations/001_core_schema.d.ts +2 -0
- package/dist/db/migrations/001_core_schema.js +62 -0
- package/dist/db/migrations/001_core_schema.js.map +1 -0
- package/dist/db/migrations/002_learning_schema.d.ts +2 -0
- package/dist/db/migrations/002_learning_schema.js +45 -0
- package/dist/db/migrations/002_learning_schema.js.map +1 -0
- package/dist/db/migrations/003_synapse_schema.d.ts +2 -0
- package/dist/db/migrations/003_synapse_schema.js +26 -0
- package/dist/db/migrations/003_synapse_schema.js.map +1 -0
- package/dist/db/migrations/004_insights_schema.d.ts +2 -0
- package/dist/db/migrations/004_insights_schema.js +37 -0
- package/dist/db/migrations/004_insights_schema.js.map +1 -0
- package/dist/db/migrations/005_fts_indexes.d.ts +2 -0
- package/dist/db/migrations/005_fts_indexes.js +76 -0
- package/dist/db/migrations/005_fts_indexes.js.map +1 -0
- package/dist/db/migrations/index.d.ts +2 -0
- package/dist/db/migrations/index.js +47 -0
- package/dist/db/migrations/index.js.map +1 -0
- package/dist/db/repositories/audience.repository.d.ts +18 -0
- package/dist/db/repositories/audience.repository.js +45 -0
- package/dist/db/repositories/audience.repository.js.map +1 -0
- package/dist/db/repositories/campaign.repository.d.ts +15 -0
- package/dist/db/repositories/campaign.repository.js +58 -0
- package/dist/db/repositories/campaign.repository.js.map +1 -0
- package/dist/db/repositories/engagement.repository.d.ts +26 -0
- package/dist/db/repositories/engagement.repository.js +83 -0
- package/dist/db/repositories/engagement.repository.js.map +1 -0
- package/dist/db/repositories/insight.repository.d.ts +18 -0
- package/dist/db/repositories/insight.repository.js +87 -0
- package/dist/db/repositories/insight.repository.js.map +1 -0
- package/dist/db/repositories/post.repository.d.ts +21 -0
- package/dist/db/repositories/post.repository.js +105 -0
- package/dist/db/repositories/post.repository.js.map +1 -0
- package/dist/db/repositories/rule.repository.d.ts +16 -0
- package/dist/db/repositories/rule.repository.js +71 -0
- package/dist/db/repositories/rule.repository.js.map +1 -0
- package/dist/db/repositories/strategy.repository.d.ts +16 -0
- package/dist/db/repositories/strategy.repository.js +69 -0
- package/dist/db/repositories/strategy.repository.js.map +1 -0
- package/dist/db/repositories/synapse.repository.d.ts +25 -0
- package/dist/db/repositories/synapse.repository.js +115 -0
- package/dist/db/repositories/synapse.repository.js.map +1 -0
- package/dist/db/repositories/template.repository.d.ts +16 -0
- package/dist/db/repositories/template.repository.js +61 -0
- package/dist/db/repositories/template.repository.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +62 -0
- package/dist/index.js.map +1 -0
- package/dist/ipc/client.d.ts +13 -0
- package/dist/ipc/client.js +93 -0
- package/dist/ipc/client.js.map +1 -0
- package/dist/ipc/protocol.d.ts +8 -0
- package/dist/ipc/protocol.js +29 -0
- package/dist/ipc/protocol.js.map +1 -0
- package/dist/ipc/router.d.ts +30 -0
- package/dist/ipc/router.js +88 -0
- package/dist/ipc/router.js.map +1 -0
- package/dist/ipc/server.d.ts +14 -0
- package/dist/ipc/server.js +130 -0
- package/dist/ipc/server.js.map +1 -0
- package/dist/learning/confidence-scorer.d.ts +17 -0
- package/dist/learning/confidence-scorer.js +26 -0
- package/dist/learning/confidence-scorer.js.map +1 -0
- package/dist/learning/learning-engine.d.ts +33 -0
- package/dist/learning/learning-engine.js +211 -0
- package/dist/learning/learning-engine.js.map +1 -0
- package/dist/marketing-core.d.ts +17 -0
- package/dist/marketing-core.js +233 -0
- package/dist/marketing-core.js.map +1 -0
- package/dist/mcp/server.d.ts +1 -0
- package/dist/mcp/server.js +67 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +3 -0
- package/dist/mcp/tools.js +138 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/research/research-engine.d.ts +28 -0
- package/dist/research/research-engine.js +211 -0
- package/dist/research/research-engine.js.map +1 -0
- package/dist/services/analytics.service.d.ts +116 -0
- package/dist/services/analytics.service.js +69 -0
- package/dist/services/analytics.service.js.map +1 -0
- package/dist/services/audience.service.d.ts +20 -0
- package/dist/services/audience.service.js +30 -0
- package/dist/services/audience.service.js.map +1 -0
- package/dist/services/campaign.service.d.ts +27 -0
- package/dist/services/campaign.service.js +65 -0
- package/dist/services/campaign.service.js.map +1 -0
- package/dist/services/insight.service.d.ts +18 -0
- package/dist/services/insight.service.js +40 -0
- package/dist/services/insight.service.js.map +1 -0
- package/dist/services/post.service.d.ts +48 -0
- package/dist/services/post.service.js +93 -0
- package/dist/services/post.service.js.map +1 -0
- package/dist/services/rule.service.d.ts +29 -0
- package/dist/services/rule.service.js +67 -0
- package/dist/services/rule.service.js.map +1 -0
- package/dist/services/strategy.service.d.ts +17 -0
- package/dist/services/strategy.service.js +39 -0
- package/dist/services/strategy.service.js.map +1 -0
- package/dist/services/synapse.service.d.ts +22 -0
- package/dist/services/synapse.service.js +22 -0
- package/dist/services/synapse.service.js.map +1 -0
- package/dist/services/template.service.d.ts +17 -0
- package/dist/services/template.service.js +37 -0
- package/dist/services/template.service.js.map +1 -0
- package/dist/synapses/activation.d.ts +13 -0
- package/dist/synapses/activation.js +50 -0
- package/dist/synapses/activation.js.map +1 -0
- package/dist/synapses/decay.d.ts +11 -0
- package/dist/synapses/decay.js +27 -0
- package/dist/synapses/decay.js.map +1 -0
- package/dist/synapses/hebbian.d.ts +13 -0
- package/dist/synapses/hebbian.js +35 -0
- package/dist/synapses/hebbian.js.map +1 -0
- package/dist/synapses/pathfinder.d.ts +14 -0
- package/dist/synapses/pathfinder.js +50 -0
- package/dist/synapses/pathfinder.js.map +1 -0
- package/dist/synapses/synapse-manager.d.ts +32 -0
- package/dist/synapses/synapse-manager.js +76 -0
- package/dist/synapses/synapse-manager.js.map +1 -0
- package/dist/types/config.types.d.ts +69 -0
- package/dist/types/config.types.js +2 -0
- package/dist/types/config.types.js.map +1 -0
- package/dist/types/ipc.types.d.ts +11 -0
- package/dist/types/ipc.types.js +2 -0
- package/dist/types/ipc.types.js.map +1 -0
- package/dist/types/post.types.d.ts +141 -0
- package/dist/types/post.types.js +2 -0
- package/dist/types/post.types.js.map +1 -0
- package/dist/types/synapse.types.d.ts +23 -0
- package/dist/types/synapse.types.js +2 -0
- package/dist/types/synapse.types.js.map +1 -0
- package/dist/utils/events.d.ts +57 -0
- package/dist/utils/events.js +23 -0
- package/dist/utils/events.js.map +1 -0
- package/dist/utils/hash.d.ts +1 -0
- package/dist/utils/hash.js +5 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/logger.d.ts +8 -0
- package/dist/utils/logger.js +39 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/paths.d.ts +3 -0
- package/dist/utils/paths.js +18 -0
- package/dist/utils/paths.js.map +1 -0
- package/package.json +40 -0
- package/seed-data.json +78 -0
- package/src/api/server.ts +86 -0
- package/src/cli/colors.ts +59 -0
- package/src/cli/commands/campaign.ts +66 -0
- package/src/cli/commands/config.ts +168 -0
- package/src/cli/commands/dashboard.ts +165 -0
- package/src/cli/commands/doctor.ts +110 -0
- package/src/cli/commands/export.ts +40 -0
- package/src/cli/commands/import.ts +84 -0
- package/src/cli/commands/insights.ts +44 -0
- package/src/cli/commands/learn.ts +24 -0
- package/src/cli/commands/network.ts +71 -0
- package/src/cli/commands/post.ts +47 -0
- package/src/cli/commands/query.ts +108 -0
- package/src/cli/commands/rules.ts +27 -0
- package/src/cli/commands/start.ts +100 -0
- package/src/cli/commands/status.ts +73 -0
- package/src/cli/commands/stop.ts +33 -0
- package/src/cli/commands/suggest.ts +64 -0
- package/src/cli/ipc-helper.ts +22 -0
- package/src/cli/update-check.ts +63 -0
- package/src/config.ts +110 -0
- package/src/dashboard/renderer.ts +136 -0
- package/src/dashboard/server.ts +140 -0
- package/src/db/connection.ts +22 -0
- package/src/db/migrations/001_core_schema.ts +63 -0
- package/src/db/migrations/002_learning_schema.ts +46 -0
- package/src/db/migrations/003_synapse_schema.ts +27 -0
- package/src/db/migrations/004_insights_schema.ts +38 -0
- package/src/db/migrations/005_fts_indexes.ts +77 -0
- package/src/db/migrations/index.ts +62 -0
- package/src/db/repositories/audience.repository.ts +53 -0
- package/src/db/repositories/campaign.repository.ts +72 -0
- package/src/db/repositories/engagement.repository.ts +108 -0
- package/src/db/repositories/insight.repository.ts +100 -0
- package/src/db/repositories/post.repository.ts +123 -0
- package/src/db/repositories/rule.repository.ts +87 -0
- package/src/db/repositories/strategy.repository.ts +82 -0
- package/src/db/repositories/synapse.repository.ts +148 -0
- package/src/db/repositories/template.repository.ts +76 -0
- package/src/index.ts +69 -0
- package/src/ipc/client.ts +110 -0
- package/src/ipc/protocol.ts +35 -0
- package/src/ipc/router.ts +126 -0
- package/src/ipc/server.ts +140 -0
- package/src/learning/confidence-scorer.ts +36 -0
- package/src/learning/learning-engine.ts +254 -0
- package/src/marketing-core.ts +285 -0
- package/src/mcp/server.ts +72 -0
- package/src/mcp/tools.ts +216 -0
- package/src/research/research-engine.ts +226 -0
- package/src/services/analytics.service.ts +73 -0
- package/src/services/audience.service.ts +40 -0
- package/src/services/campaign.service.ts +80 -0
- package/src/services/insight.service.ts +54 -0
- package/src/services/post.service.ts +116 -0
- package/src/services/rule.service.ts +90 -0
- package/src/services/strategy.service.ts +53 -0
- package/src/services/synapse.service.ts +32 -0
- package/src/services/template.service.ts +50 -0
- package/src/synapses/activation.ts +80 -0
- package/src/synapses/decay.ts +38 -0
- package/src/synapses/hebbian.ts +68 -0
- package/src/synapses/pathfinder.ts +81 -0
- package/src/synapses/synapse-manager.ts +115 -0
- package/src/types/config.types.ts +79 -0
- package/src/types/ipc.types.ts +8 -0
- package/src/types/post.types.ts +156 -0
- package/src/types/synapse.types.ts +43 -0
- package/src/utils/events.ts +44 -0
- package/src/utils/hash.ts +5 -0
- package/src/utils/logger.ts +48 -0
- package/src/utils/paths.ts +19 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import type Database from 'better-sqlite3';
|
|
4
|
+
import { loadConfig } from './config.js';
|
|
5
|
+
import type { MarketingBrainConfig } from './types/config.types.js';
|
|
6
|
+
import { createLogger, getLogger } from './utils/logger.js';
|
|
7
|
+
import { getEventBus } from './utils/events.js';
|
|
8
|
+
import { createConnection } from './db/connection.js';
|
|
9
|
+
import { runMigrations } from './db/migrations/index.js';
|
|
10
|
+
|
|
11
|
+
// Repositories
|
|
12
|
+
import { PostRepository } from './db/repositories/post.repository.js';
|
|
13
|
+
import { EngagementRepository } from './db/repositories/engagement.repository.js';
|
|
14
|
+
import { CampaignRepository } from './db/repositories/campaign.repository.js';
|
|
15
|
+
import { StrategyRepository } from './db/repositories/strategy.repository.js';
|
|
16
|
+
import { RuleRepository } from './db/repositories/rule.repository.js';
|
|
17
|
+
import { TemplateRepository } from './db/repositories/template.repository.js';
|
|
18
|
+
import { AudienceRepository } from './db/repositories/audience.repository.js';
|
|
19
|
+
import { SynapseRepository } from './db/repositories/synapse.repository.js';
|
|
20
|
+
import { InsightRepository } from './db/repositories/insight.repository.js';
|
|
21
|
+
|
|
22
|
+
// Services
|
|
23
|
+
import { PostService } from './services/post.service.js';
|
|
24
|
+
import { CampaignService } from './services/campaign.service.js';
|
|
25
|
+
import { StrategyService } from './services/strategy.service.js';
|
|
26
|
+
import { TemplateService } from './services/template.service.js';
|
|
27
|
+
import { RuleService } from './services/rule.service.js';
|
|
28
|
+
import { AudienceService } from './services/audience.service.js';
|
|
29
|
+
import { SynapseService } from './services/synapse.service.js';
|
|
30
|
+
import { AnalyticsService } from './services/analytics.service.js';
|
|
31
|
+
import { InsightService } from './services/insight.service.js';
|
|
32
|
+
|
|
33
|
+
// Synapses
|
|
34
|
+
import { SynapseManager } from './synapses/synapse-manager.js';
|
|
35
|
+
|
|
36
|
+
// Engines
|
|
37
|
+
import { LearningEngine } from './learning/learning-engine.js';
|
|
38
|
+
import { ResearchEngine } from './research/research-engine.js';
|
|
39
|
+
|
|
40
|
+
// IPC
|
|
41
|
+
import { IpcRouter, type Services } from './ipc/router.js';
|
|
42
|
+
import { IpcServer } from './ipc/server.js';
|
|
43
|
+
|
|
44
|
+
// API
|
|
45
|
+
import { ApiServer } from './api/server.js';
|
|
46
|
+
|
|
47
|
+
// Dashboard
|
|
48
|
+
import { DashboardServer } from './dashboard/server.js';
|
|
49
|
+
import { renderDashboard } from './dashboard/renderer.js';
|
|
50
|
+
|
|
51
|
+
export class MarketingCore {
|
|
52
|
+
private db: Database.Database | null = null;
|
|
53
|
+
private ipcServer: IpcServer | null = null;
|
|
54
|
+
private apiServer: ApiServer | null = null;
|
|
55
|
+
private dashboardServer: DashboardServer | null = null;
|
|
56
|
+
private learningEngine: LearningEngine | null = null;
|
|
57
|
+
private researchEngine: ResearchEngine | null = null;
|
|
58
|
+
private config: MarketingBrainConfig | null = null;
|
|
59
|
+
private configPath?: string;
|
|
60
|
+
private restarting = false;
|
|
61
|
+
|
|
62
|
+
start(configPath?: string): void {
|
|
63
|
+
this.configPath = configPath;
|
|
64
|
+
// 1. Config
|
|
65
|
+
this.config = loadConfig(configPath);
|
|
66
|
+
const config = this.config;
|
|
67
|
+
|
|
68
|
+
// 2. Ensure data dir
|
|
69
|
+
fs.mkdirSync(path.dirname(config.dbPath), { recursive: true });
|
|
70
|
+
|
|
71
|
+
// 3. Logger
|
|
72
|
+
createLogger({
|
|
73
|
+
level: config.log.level,
|
|
74
|
+
file: config.log.file,
|
|
75
|
+
maxSize: config.log.maxSize,
|
|
76
|
+
maxFiles: config.log.maxFiles,
|
|
77
|
+
});
|
|
78
|
+
const logger = getLogger();
|
|
79
|
+
|
|
80
|
+
// 4. Database
|
|
81
|
+
this.db = createConnection(config.dbPath);
|
|
82
|
+
runMigrations(this.db);
|
|
83
|
+
logger.info(`Database initialized: ${config.dbPath}`);
|
|
84
|
+
|
|
85
|
+
// 5. Repositories
|
|
86
|
+
const postRepo = new PostRepository(this.db);
|
|
87
|
+
const engagementRepo = new EngagementRepository(this.db);
|
|
88
|
+
const campaignRepo = new CampaignRepository(this.db);
|
|
89
|
+
const strategyRepo = new StrategyRepository(this.db);
|
|
90
|
+
const ruleRepo = new RuleRepository(this.db);
|
|
91
|
+
const templateRepo = new TemplateRepository(this.db);
|
|
92
|
+
const audienceRepo = new AudienceRepository(this.db);
|
|
93
|
+
const synapseRepo = new SynapseRepository(this.db);
|
|
94
|
+
const insightRepo = new InsightRepository(this.db);
|
|
95
|
+
|
|
96
|
+
// 6. Synapse Manager
|
|
97
|
+
const synapseManager = new SynapseManager(synapseRepo, config.synapses);
|
|
98
|
+
|
|
99
|
+
// 7. Services
|
|
100
|
+
const services: Services = {
|
|
101
|
+
post: new PostService(postRepo, engagementRepo, synapseManager),
|
|
102
|
+
campaign: new CampaignService(campaignRepo, postRepo, engagementRepo, synapseManager),
|
|
103
|
+
strategy: new StrategyService(strategyRepo, synapseManager),
|
|
104
|
+
template: new TemplateService(templateRepo, synapseManager),
|
|
105
|
+
rule: new RuleService(ruleRepo, synapseManager),
|
|
106
|
+
audience: new AudienceService(audienceRepo, synapseManager),
|
|
107
|
+
synapse: new SynapseService(synapseManager),
|
|
108
|
+
analytics: new AnalyticsService(
|
|
109
|
+
postRepo, engagementRepo, campaignRepo,
|
|
110
|
+
strategyRepo, ruleRepo, templateRepo,
|
|
111
|
+
insightRepo, synapseManager,
|
|
112
|
+
),
|
|
113
|
+
insight: new InsightService(insightRepo, synapseManager),
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// 8. Learning Engine
|
|
117
|
+
this.learningEngine = new LearningEngine(
|
|
118
|
+
config.learning, postRepo, engagementRepo,
|
|
119
|
+
ruleRepo, strategyRepo, synapseManager,
|
|
120
|
+
);
|
|
121
|
+
this.learningEngine.start();
|
|
122
|
+
logger.info(`Learning engine started (interval: ${config.learning.intervalMs}ms)`);
|
|
123
|
+
|
|
124
|
+
// 9. Research Engine
|
|
125
|
+
this.researchEngine = new ResearchEngine(
|
|
126
|
+
config.research, postRepo, engagementRepo,
|
|
127
|
+
campaignRepo, templateRepo, insightRepo, synapseManager,
|
|
128
|
+
);
|
|
129
|
+
this.researchEngine.start();
|
|
130
|
+
logger.info(`Research engine started (interval: ${config.research.intervalMs}ms)`);
|
|
131
|
+
|
|
132
|
+
// Expose learning engine to IPC
|
|
133
|
+
services.learning = this.learningEngine;
|
|
134
|
+
|
|
135
|
+
// 10. IPC Server
|
|
136
|
+
const router = new IpcRouter(services);
|
|
137
|
+
this.ipcServer = new IpcServer(router, config.ipc.pipeName);
|
|
138
|
+
this.ipcServer.start();
|
|
139
|
+
|
|
140
|
+
// 11. REST API Server
|
|
141
|
+
if (config.api.enabled) {
|
|
142
|
+
this.apiServer = new ApiServer({
|
|
143
|
+
port: config.api.port,
|
|
144
|
+
router,
|
|
145
|
+
apiKey: config.api.apiKey,
|
|
146
|
+
});
|
|
147
|
+
this.apiServer.start();
|
|
148
|
+
logger.info(`REST API enabled on port ${config.api.port}`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// 12. Dashboard Server (SSE)
|
|
152
|
+
if (config.dashboard.enabled) {
|
|
153
|
+
const dashboardHtmlPath = path.resolve(
|
|
154
|
+
path.dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Z]:)/, '$1')),
|
|
155
|
+
'../dashboard.html',
|
|
156
|
+
);
|
|
157
|
+
const dashServices = {
|
|
158
|
+
analytics: services.analytics,
|
|
159
|
+
insight: services.insight,
|
|
160
|
+
rule: services.rule,
|
|
161
|
+
synapse: services.synapse,
|
|
162
|
+
};
|
|
163
|
+
this.dashboardServer = new DashboardServer({
|
|
164
|
+
port: config.dashboard.port,
|
|
165
|
+
getDashboardHtml: () => {
|
|
166
|
+
try {
|
|
167
|
+
const template = fs.readFileSync(dashboardHtmlPath, 'utf-8');
|
|
168
|
+
return renderDashboard(template, dashServices);
|
|
169
|
+
} catch {
|
|
170
|
+
return '<html><body><h1>Dashboard HTML not found</h1></body></html>';
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
getStats: () => services.analytics.getSummary(),
|
|
174
|
+
});
|
|
175
|
+
this.dashboardServer.start();
|
|
176
|
+
logger.info(`Dashboard server enabled on port ${config.dashboard.port}`);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// 13. Event listeners (synapse wiring)
|
|
180
|
+
this.setupEventListeners(synapseManager);
|
|
181
|
+
|
|
182
|
+
// 14. PID file
|
|
183
|
+
const pidPath = path.join(path.dirname(config.dbPath), 'marketing-brain.pid');
|
|
184
|
+
fs.writeFileSync(pidPath, String(process.pid));
|
|
185
|
+
|
|
186
|
+
// 15. Graceful shutdown
|
|
187
|
+
process.on('SIGINT', () => this.stop());
|
|
188
|
+
process.on('SIGTERM', () => this.stop());
|
|
189
|
+
|
|
190
|
+
// 16. Crash recovery — auto-restart on uncaught errors
|
|
191
|
+
process.on('uncaughtException', (err) => {
|
|
192
|
+
logger.error('Uncaught exception — restarting', { error: err.message, stack: err.stack });
|
|
193
|
+
this.logCrash('uncaughtException', err);
|
|
194
|
+
this.restart();
|
|
195
|
+
});
|
|
196
|
+
process.on('unhandledRejection', (reason) => {
|
|
197
|
+
logger.error('Unhandled rejection — restarting', { reason: String(reason) });
|
|
198
|
+
this.logCrash('unhandledRejection', reason instanceof Error ? reason : new Error(String(reason)));
|
|
199
|
+
this.restart();
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
logger.info(`Marketing Brain daemon started (PID: ${process.pid})`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
private logCrash(type: string, err: Error): void {
|
|
206
|
+
if (!this.config) return;
|
|
207
|
+
const crashLog = path.join(path.dirname(this.config.dbPath), 'crashes.log');
|
|
208
|
+
const entry = `[${new Date().toISOString()}] ${type}: ${err.message}\n${err.stack ?? ''}\n\n`;
|
|
209
|
+
try { fs.appendFileSync(crashLog, entry); } catch { /* best effort */ }
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
private cleanup(): void {
|
|
213
|
+
this.researchEngine?.stop();
|
|
214
|
+
this.learningEngine?.stop();
|
|
215
|
+
this.dashboardServer?.stop();
|
|
216
|
+
this.apiServer?.stop();
|
|
217
|
+
this.ipcServer?.stop();
|
|
218
|
+
this.db?.close();
|
|
219
|
+
|
|
220
|
+
this.db = null;
|
|
221
|
+
this.ipcServer = null;
|
|
222
|
+
this.apiServer = null;
|
|
223
|
+
this.dashboardServer = null;
|
|
224
|
+
this.learningEngine = null;
|
|
225
|
+
this.researchEngine = null;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
restart(): void {
|
|
229
|
+
if (this.restarting) return;
|
|
230
|
+
this.restarting = true;
|
|
231
|
+
|
|
232
|
+
const logger = getLogger();
|
|
233
|
+
logger.info('Restarting Marketing Brain daemon...');
|
|
234
|
+
|
|
235
|
+
try { this.cleanup(); } catch { /* best effort cleanup */ }
|
|
236
|
+
|
|
237
|
+
this.restarting = false;
|
|
238
|
+
this.start(this.configPath);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
stop(): void {
|
|
242
|
+
const logger = getLogger();
|
|
243
|
+
logger.info('Shutting down...');
|
|
244
|
+
|
|
245
|
+
this.cleanup();
|
|
246
|
+
|
|
247
|
+
if (this.config) {
|
|
248
|
+
const pidPath = path.join(path.dirname(this.config.dbPath), 'marketing-brain.pid');
|
|
249
|
+
try { fs.unlinkSync(pidPath); } catch { /* ignore */ }
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
logger.info('Marketing Brain daemon stopped');
|
|
253
|
+
process.exit(0);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
private setupEventListeners(synapseManager: SynapseManager): void {
|
|
257
|
+
const bus = getEventBus();
|
|
258
|
+
|
|
259
|
+
bus.on('post:created', ({ postId, campaignId }) => {
|
|
260
|
+
if (campaignId) {
|
|
261
|
+
synapseManager.strengthen(
|
|
262
|
+
{ type: 'post', id: postId },
|
|
263
|
+
{ type: 'campaign', id: campaignId },
|
|
264
|
+
'belongs_to',
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
bus.on('strategy:reported', ({ strategyId, postId }) => {
|
|
270
|
+
synapseManager.strengthen(
|
|
271
|
+
{ type: 'strategy', id: strategyId },
|
|
272
|
+
{ type: 'post', id: postId },
|
|
273
|
+
'improves',
|
|
274
|
+
);
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
bus.on('rule:learned', ({ ruleId, pattern }) => {
|
|
278
|
+
getLogger().info(`New rule #${ruleId} learned: ${pattern}`);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
bus.on('insight:created', ({ insightId, type }) => {
|
|
282
|
+
getLogger().info(`New insight #${insightId} (${type})`);
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { IpcClient } from '../ipc/client.js';
|
|
6
|
+
import { getPipeName } from '../utils/paths.js';
|
|
7
|
+
import { registerTools } from './tools.js';
|
|
8
|
+
|
|
9
|
+
function spawnDaemon(): void {
|
|
10
|
+
const entryPoint = path.resolve(import.meta.dirname, '../index.ts');
|
|
11
|
+
const child = spawn(process.execPath, [
|
|
12
|
+
'--import', 'tsx',
|
|
13
|
+
entryPoint, 'daemon',
|
|
14
|
+
], {
|
|
15
|
+
detached: true,
|
|
16
|
+
stdio: 'ignore',
|
|
17
|
+
cwd: path.resolve(import.meta.dirname, '../..'),
|
|
18
|
+
});
|
|
19
|
+
child.unref();
|
|
20
|
+
process.stderr.write(`Marketing Brain: Auto-started daemon (PID: ${child.pid})\n`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function connectWithRetry(ipc: IpcClient, retries: number, delayMs: number): Promise<void> {
|
|
24
|
+
for (let i = 0; i < retries; i++) {
|
|
25
|
+
try {
|
|
26
|
+
await ipc.connect();
|
|
27
|
+
return;
|
|
28
|
+
} catch {
|
|
29
|
+
if (i < retries - 1) {
|
|
30
|
+
await new Promise(r => setTimeout(r, delayMs));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
throw new Error('Could not connect to daemon after retries');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function startMcpServer(): Promise<void> {
|
|
38
|
+
const server = new McpServer({
|
|
39
|
+
name: 'marketing-brain',
|
|
40
|
+
version: '0.1.0',
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const ipc = new IpcClient(getPipeName());
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
await ipc.connect();
|
|
47
|
+
} catch {
|
|
48
|
+
process.stderr.write('Marketing Brain: Daemon not running, starting automatically...\n');
|
|
49
|
+
spawnDaemon();
|
|
50
|
+
try {
|
|
51
|
+
await connectWithRetry(ipc, 10, 500);
|
|
52
|
+
} catch {
|
|
53
|
+
process.stderr.write('Marketing Brain: Could not connect to daemon after auto-start.\n');
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
registerTools(server, ipc);
|
|
59
|
+
|
|
60
|
+
const transport = new StdioServerTransport();
|
|
61
|
+
await server.connect(transport);
|
|
62
|
+
|
|
63
|
+
process.on('SIGINT', () => {
|
|
64
|
+
ipc.disconnect();
|
|
65
|
+
process.exit(0);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
process.on('SIGTERM', () => {
|
|
69
|
+
ipc.disconnect();
|
|
70
|
+
process.exit(0);
|
|
71
|
+
});
|
|
72
|
+
}
|
package/src/mcp/tools.ts
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { IpcClient } from '../ipc/client.js';
|
|
4
|
+
|
|
5
|
+
export function registerTools(server: McpServer, ipc: IpcClient): void {
|
|
6
|
+
|
|
7
|
+
// 1. post.draft — Check a post draft against rules
|
|
8
|
+
server.tool(
|
|
9
|
+
'marketing_post_draft',
|
|
10
|
+
'Check a post draft against learned marketing rules before publishing. Returns violations and recommendations.',
|
|
11
|
+
{
|
|
12
|
+
content: z.string().describe('The post content/text to check'),
|
|
13
|
+
platform: z.string().describe('Target platform (x, reddit, linkedin, bluesky)'),
|
|
14
|
+
},
|
|
15
|
+
async ({ content, platform }) => {
|
|
16
|
+
const result = await ipc.request('rule.check', { content, platform });
|
|
17
|
+
return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
|
|
18
|
+
},
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
// 2. post.report — Report a published post
|
|
22
|
+
server.tool(
|
|
23
|
+
'marketing_post_report',
|
|
24
|
+
'Report a published post to track in the marketing brain. Stores content, platform, format, and creates synapse connections.',
|
|
25
|
+
{
|
|
26
|
+
platform: z.string().describe('Platform (x, reddit, linkedin, bluesky)'),
|
|
27
|
+
content: z.string().describe('Post content/text'),
|
|
28
|
+
format: z.string().optional().describe('Post format (text, image, video, thread, article)'),
|
|
29
|
+
url: z.string().optional().describe('Post URL'),
|
|
30
|
+
hashtags: z.string().optional().describe('Hashtags (comma-separated)'),
|
|
31
|
+
campaign: z.string().optional().describe('Campaign name (creates if not exists)'),
|
|
32
|
+
},
|
|
33
|
+
async ({ platform, content, format, url, hashtags, campaign }) => {
|
|
34
|
+
let campaignId: number | null = null;
|
|
35
|
+
if (campaign) {
|
|
36
|
+
const camp = await ipc.request('campaign.create', { name: campaign }) as { id: number };
|
|
37
|
+
campaignId = camp.id;
|
|
38
|
+
}
|
|
39
|
+
const result = await ipc.request('post.report', {
|
|
40
|
+
platform, content, format: format ?? 'text',
|
|
41
|
+
url, hashtags, campaign_id: campaignId,
|
|
42
|
+
status: 'published', published_at: new Date().toISOString(),
|
|
43
|
+
});
|
|
44
|
+
return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// 3. post.engagement — Update engagement metrics
|
|
49
|
+
server.tool(
|
|
50
|
+
'marketing_post_engagement',
|
|
51
|
+
'Update engagement metrics for a tracked post. Provide post ID and current metrics.',
|
|
52
|
+
{
|
|
53
|
+
post_id: z.number().describe('Post ID'),
|
|
54
|
+
likes: z.number().optional().describe('Current likes count'),
|
|
55
|
+
comments: z.number().optional().describe('Current comments count'),
|
|
56
|
+
shares: z.number().optional().describe('Current shares/retweets count'),
|
|
57
|
+
impressions: z.number().optional().describe('Current impressions count'),
|
|
58
|
+
clicks: z.number().optional().describe('Current clicks count'),
|
|
59
|
+
saves: z.number().optional().describe('Current saves/bookmarks count'),
|
|
60
|
+
reach: z.number().optional().describe('Current reach count'),
|
|
61
|
+
},
|
|
62
|
+
async (params) => {
|
|
63
|
+
const result = await ipc.request('post.engagement', params);
|
|
64
|
+
return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
|
|
65
|
+
},
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
// 4. post.similar — Find similar posts
|
|
69
|
+
server.tool(
|
|
70
|
+
'marketing_post_similar',
|
|
71
|
+
'Find posts similar to a given post using synapse network spreading activation.',
|
|
72
|
+
{
|
|
73
|
+
post_id: z.number().describe('Post ID to find similar posts for'),
|
|
74
|
+
},
|
|
75
|
+
async ({ post_id }) => {
|
|
76
|
+
const result = await ipc.request('post.similar', { id: post_id });
|
|
77
|
+
return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
|
|
78
|
+
},
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
// 5. campaign.create — Create a new campaign
|
|
82
|
+
server.tool(
|
|
83
|
+
'marketing_campaign_create',
|
|
84
|
+
'Create a new marketing campaign to group and track related posts.',
|
|
85
|
+
{
|
|
86
|
+
name: z.string().describe('Campaign name'),
|
|
87
|
+
brand: z.string().optional().describe('Brand name (e.g., REPOSIGNAL, Brain)'),
|
|
88
|
+
goal: z.string().optional().describe('Campaign goal'),
|
|
89
|
+
platform: z.string().optional().describe('Primary platform'),
|
|
90
|
+
},
|
|
91
|
+
async (params) => {
|
|
92
|
+
const result = await ipc.request('campaign.create', params);
|
|
93
|
+
return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
|
|
94
|
+
},
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// 6. campaign.stats — Campaign performance
|
|
98
|
+
server.tool(
|
|
99
|
+
'marketing_campaign_stats',
|
|
100
|
+
'Get performance statistics for a campaign including total engagement and post count.',
|
|
101
|
+
{
|
|
102
|
+
campaign_id: z.number().describe('Campaign ID'),
|
|
103
|
+
},
|
|
104
|
+
async ({ campaign_id }) => {
|
|
105
|
+
const result = await ipc.request('campaign.stats', { id: campaign_id });
|
|
106
|
+
return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
|
|
107
|
+
},
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
// 7. strategy.report — Report a successful strategy
|
|
111
|
+
server.tool(
|
|
112
|
+
'marketing_strategy_report',
|
|
113
|
+
'Report a marketing strategy that worked. The brain will learn from it and suggest similar strategies later.',
|
|
114
|
+
{
|
|
115
|
+
description: z.string().describe('Strategy description'),
|
|
116
|
+
approach: z.string().optional().describe('Approach taken'),
|
|
117
|
+
outcome: z.string().optional().describe('Outcome/result'),
|
|
118
|
+
post_id: z.number().optional().describe('Related post ID'),
|
|
119
|
+
},
|
|
120
|
+
async (params) => {
|
|
121
|
+
const result = await ipc.request('strategy.report', params);
|
|
122
|
+
return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
|
|
123
|
+
},
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
// 8. strategy.suggest — Get strategy suggestions
|
|
127
|
+
server.tool(
|
|
128
|
+
'marketing_strategy_suggest',
|
|
129
|
+
'Get strategy suggestions based on a topic or context. Searches past successful strategies.',
|
|
130
|
+
{
|
|
131
|
+
query: z.string().describe('Topic or context to search for'),
|
|
132
|
+
limit: z.number().optional().describe('Max results (default: 5)'),
|
|
133
|
+
},
|
|
134
|
+
async ({ query, limit }) => {
|
|
135
|
+
const result = await ipc.request('strategy.suggest', { query, limit: limit ?? 5 });
|
|
136
|
+
return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
|
|
137
|
+
},
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
// 9. template.find — Find content templates
|
|
141
|
+
server.tool(
|
|
142
|
+
'marketing_template_find',
|
|
143
|
+
'Find reusable content templates that match a query or platform.',
|
|
144
|
+
{
|
|
145
|
+
query: z.string().optional().describe('Search query'),
|
|
146
|
+
platform: z.string().optional().describe('Filter by platform'),
|
|
147
|
+
limit: z.number().optional().describe('Max results'),
|
|
148
|
+
},
|
|
149
|
+
async ({ query, platform, limit }) => {
|
|
150
|
+
let result;
|
|
151
|
+
if (platform) {
|
|
152
|
+
result = await ipc.request('template.byPlatform', { platform, limit: limit ?? 10 });
|
|
153
|
+
} else {
|
|
154
|
+
result = await ipc.request('template.find', { query: query ?? '', limit: limit ?? 10 });
|
|
155
|
+
}
|
|
156
|
+
return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
|
|
157
|
+
},
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
// 10. rule.check — Check draft against rules
|
|
161
|
+
server.tool(
|
|
162
|
+
'marketing_rule_check',
|
|
163
|
+
'Check a post draft against all learned marketing rules. Returns violations and recommendations.',
|
|
164
|
+
{
|
|
165
|
+
content: z.string().describe('Post content to check'),
|
|
166
|
+
platform: z.string().describe('Target platform'),
|
|
167
|
+
},
|
|
168
|
+
async ({ content, platform }) => {
|
|
169
|
+
const result = await ipc.request('rule.check', { content, platform });
|
|
170
|
+
return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
|
|
171
|
+
},
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
// 11. insight.list — List current insights
|
|
175
|
+
server.tool(
|
|
176
|
+
'marketing_insight_list',
|
|
177
|
+
'Get current active marketing insights (trends, gaps, synergies, optimizations).',
|
|
178
|
+
{
|
|
179
|
+
type: z.string().optional().describe('Filter by type: trend, gap, synergy, template, optimization'),
|
|
180
|
+
limit: z.number().optional().describe('Max results'),
|
|
181
|
+
},
|
|
182
|
+
async ({ type, limit }) => {
|
|
183
|
+
let result;
|
|
184
|
+
if (type) {
|
|
185
|
+
result = await ipc.request('insight.byType', { type, limit: limit ?? 20 });
|
|
186
|
+
} else {
|
|
187
|
+
result = await ipc.request('insight.list', { limit: limit ?? 20 });
|
|
188
|
+
}
|
|
189
|
+
return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
|
|
190
|
+
},
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
// 12. analytics.summary — Overall analytics
|
|
194
|
+
server.tool(
|
|
195
|
+
'marketing_analytics_summary',
|
|
196
|
+
'Get a complete analytics summary: posts, campaigns, strategies, rules, insights, and network stats.',
|
|
197
|
+
{},
|
|
198
|
+
async () => {
|
|
199
|
+
const result = await ipc.request('analytics.summary', {});
|
|
200
|
+
return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
|
|
201
|
+
},
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
// 13. analytics.bestOf — Top performing content
|
|
205
|
+
server.tool(
|
|
206
|
+
'marketing_analytics_best',
|
|
207
|
+
'Get top performing posts, best strategies, and platform-level engagement stats.',
|
|
208
|
+
{
|
|
209
|
+
limit: z.number().optional().describe('Max results per category'),
|
|
210
|
+
},
|
|
211
|
+
async ({ limit }) => {
|
|
212
|
+
const result = await ipc.request('analytics.top', { limit: limit ?? 10 });
|
|
213
|
+
return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };
|
|
214
|
+
},
|
|
215
|
+
);
|
|
216
|
+
}
|