@timmeck/trading-brain 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/README.md +20 -0
  2. package/dashboard.html +480 -0
  3. package/dist/api/server.d.ts +3 -16
  4. package/dist/api/server.js +4 -123
  5. package/dist/api/server.js.map +1 -1
  6. package/dist/cli/colors.d.ts +10 -26
  7. package/dist/cli/colors.js +3 -62
  8. package/dist/cli/colors.js.map +1 -1
  9. package/dist/cli/commands/dashboard.d.ts +2 -0
  10. package/dist/cli/commands/dashboard.js +159 -0
  11. package/dist/cli/commands/dashboard.js.map +1 -0
  12. package/dist/cli/commands/peers.d.ts +2 -0
  13. package/dist/cli/commands/peers.js +38 -0
  14. package/dist/cli/commands/peers.js.map +1 -0
  15. package/dist/dashboard/renderer.d.ts +17 -0
  16. package/dist/dashboard/renderer.js +130 -0
  17. package/dist/dashboard/renderer.js.map +1 -0
  18. package/dist/dashboard/server.d.ts +15 -0
  19. package/dist/dashboard/server.js +116 -0
  20. package/dist/dashboard/server.js.map +1 -0
  21. package/dist/db/connection.d.ts +1 -2
  22. package/dist/db/connection.js +1 -18
  23. package/dist/db/connection.js.map +1 -1
  24. package/dist/hooks/post-tool-use.d.ts +2 -0
  25. package/dist/hooks/post-tool-use.js +108 -0
  26. package/dist/hooks/post-tool-use.js.map +1 -0
  27. package/dist/index.js +5 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/ipc/__tests__/protocol.test.d.ts +1 -0
  30. package/dist/ipc/__tests__/protocol.test.js +89 -0
  31. package/dist/ipc/__tests__/protocol.test.js.map +1 -0
  32. package/dist/ipc/client.d.ts +1 -16
  33. package/dist/ipc/client.js +1 -94
  34. package/dist/ipc/client.js.map +1 -1
  35. package/dist/ipc/protocol.d.ts +1 -8
  36. package/dist/ipc/protocol.js +1 -28
  37. package/dist/ipc/protocol.js.map +1 -1
  38. package/dist/ipc/router.js +8 -0
  39. package/dist/ipc/router.js.map +1 -1
  40. package/dist/ipc/server.d.ts +1 -18
  41. package/dist/ipc/server.js +1 -141
  42. package/dist/ipc/server.js.map +1 -1
  43. package/dist/mcp/http-server.d.ts +1 -7
  44. package/dist/mcp/http-server.js +6 -111
  45. package/dist/mcp/http-server.js.map +1 -1
  46. package/dist/mcp/server.js +5 -60
  47. package/dist/mcp/server.js.map +1 -1
  48. package/dist/signals/__tests__/fingerprint.test.d.ts +1 -0
  49. package/dist/signals/__tests__/fingerprint.test.js +164 -0
  50. package/dist/signals/__tests__/fingerprint.test.js.map +1 -0
  51. package/dist/signals/__tests__/wilson-score.test.d.ts +1 -0
  52. package/dist/signals/__tests__/wilson-score.test.js +65 -0
  53. package/dist/signals/__tests__/wilson-score.test.js.map +1 -0
  54. package/dist/trading-core.d.ts +1 -0
  55. package/dist/trading-core.js +6 -1
  56. package/dist/trading-core.js.map +1 -1
  57. package/dist/types/ipc.types.d.ts +1 -11
  58. package/dist/utils/__tests__/hash.test.d.ts +1 -0
  59. package/dist/utils/__tests__/hash.test.js +23 -0
  60. package/dist/utils/__tests__/hash.test.js.map +1 -0
  61. package/dist/utils/__tests__/paths.test.d.ts +1 -0
  62. package/dist/utils/__tests__/paths.test.js +60 -0
  63. package/dist/utils/__tests__/paths.test.js.map +1 -0
  64. package/dist/utils/events.d.ts +4 -8
  65. package/dist/utils/events.js +2 -14
  66. package/dist/utils/events.js.map +1 -1
  67. package/dist/utils/hash.d.ts +1 -1
  68. package/dist/utils/hash.js +1 -4
  69. package/dist/utils/hash.js.map +1 -1
  70. package/dist/utils/logger.d.ts +3 -2
  71. package/dist/utils/logger.js +8 -35
  72. package/dist/utils/logger.js.map +1 -1
  73. package/dist/utils/paths.d.ts +2 -1
  74. package/dist/utils/paths.js +4 -13
  75. package/dist/utils/paths.js.map +1 -1
  76. package/package.json +2 -1
  77. package/src/api/server.ts +0 -160
  78. package/src/cli/colors.ts +0 -80
  79. package/src/cli/commands/config.ts +0 -76
  80. package/src/cli/commands/doctor.ts +0 -62
  81. package/src/cli/commands/export.ts +0 -24
  82. package/src/cli/commands/import.ts +0 -44
  83. package/src/cli/commands/insights.ts +0 -30
  84. package/src/cli/commands/network.ts +0 -43
  85. package/src/cli/commands/query.ts +0 -28
  86. package/src/cli/commands/rules.ts +0 -27
  87. package/src/cli/commands/start.ts +0 -93
  88. package/src/cli/commands/status.ts +0 -64
  89. package/src/cli/commands/stop.ts +0 -33
  90. package/src/cli/ipc-helper.ts +0 -22
  91. package/src/config.ts +0 -103
  92. package/src/db/connection.ts +0 -22
  93. package/src/db/migrations/001_core.ts +0 -43
  94. package/src/db/migrations/002_synapses.ts +0 -44
  95. package/src/db/migrations/003_learning.ts +0 -49
  96. package/src/db/migrations/004_research.ts +0 -30
  97. package/src/db/migrations/index.ts +0 -60
  98. package/src/db/repositories/calibration.repository.ts +0 -86
  99. package/src/db/repositories/chain.repository.ts +0 -70
  100. package/src/db/repositories/graph.repository.ts +0 -103
  101. package/src/db/repositories/insight.repository.ts +0 -80
  102. package/src/db/repositories/rule.repository.ts +0 -67
  103. package/src/db/repositories/signal.repository.ts +0 -48
  104. package/src/db/repositories/synapse.repository.ts +0 -71
  105. package/src/db/repositories/trade.repository.ts +0 -97
  106. package/src/graph/weighted-graph.ts +0 -194
  107. package/src/index.ts +0 -55
  108. package/src/ipc/client.ts +0 -112
  109. package/src/ipc/protocol.ts +0 -35
  110. package/src/ipc/router.ts +0 -113
  111. package/src/ipc/server.ts +0 -150
  112. package/src/learning/calibrator.ts +0 -57
  113. package/src/learning/chain-detector.ts +0 -43
  114. package/src/learning/learning-engine.ts +0 -94
  115. package/src/learning/pattern-extractor.ts +0 -53
  116. package/src/mcp/http-server.ts +0 -118
  117. package/src/mcp/server.ts +0 -72
  118. package/src/mcp/tools.ts +0 -256
  119. package/src/research/research-engine.ts +0 -223
  120. package/src/services/analytics.service.ts +0 -68
  121. package/src/services/insight.service.ts +0 -29
  122. package/src/services/signal.service.ts +0 -109
  123. package/src/services/strategy.service.ts +0 -130
  124. package/src/services/synapse.service.ts +0 -58
  125. package/src/services/trade.service.ts +0 -139
  126. package/src/signals/fingerprint.ts +0 -93
  127. package/src/signals/wilson-score.ts +0 -17
  128. package/src/synapses/decay.ts +0 -19
  129. package/src/synapses/hebbian.ts +0 -23
  130. package/src/synapses/synapse-manager.ts +0 -112
  131. package/src/trading-core.ts +0 -285
  132. package/src/types/config.types.ts +0 -60
  133. package/src/types/ipc.types.ts +0 -8
  134. package/src/utils/events.ts +0 -42
  135. package/src/utils/hash.ts +0 -5
  136. package/src/utils/logger.ts +0 -48
  137. package/src/utils/paths.ts +0 -19
  138. package/tsconfig.json +0 -18
@@ -0,0 +1,130 @@
1
+ function escapeHtml(str) {
2
+ return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
3
+ }
4
+ export function renderDashboard(template, services) {
5
+ let html = template;
6
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
+ const summary = services.analytics.getSummary();
8
+ const s = summary;
9
+ // Stats
10
+ html = html.replace('{{TRADES}}', String(s.trades?.total ?? 0));
11
+ html = html.replace('{{RULES}}', String(s.rules?.total ?? 0));
12
+ html = html.replace('{{CHAINS}}', String(s.chains?.total ?? 0));
13
+ html = html.replace('{{INSIGHTS}}', String(s.insights?.total ?? 0));
14
+ html = html.replace('{{SYNAPSES}}', String(s.network?.synapses ?? 0));
15
+ html = html.replace('{{GRAPH_NODES}}', String(s.network?.graphNodes ?? 0));
16
+ // Win rate
17
+ const winRate = Math.round(s.trades?.recentWinRate ?? 0);
18
+ html = html.replace('{{WIN_RATE}}', String(winRate));
19
+ // Gauge: arc length = 251.2 (half circle), offset = (1 - winRate/100) * 251.2
20
+ const gaugeOffset = ((1 - winRate / 100) * 251.2).toFixed(1);
21
+ html = html.replace('{{GAUGE_OFFSET}}', gaugeOffset);
22
+ // Activity score
23
+ const activity = Math.min(100, Math.round(((s.trades?.total ?? 0) * 3 +
24
+ (s.rules?.total ?? 0) * 15 +
25
+ (s.chains?.total ?? 0) * 5 +
26
+ (s.insights?.total ?? 0) * 5 +
27
+ (s.network?.synapses ?? 0) * 2) / 2));
28
+ html = html.replace(/\{\{ACTIVITY\}\}/g, String(activity));
29
+ // Version
30
+ html = html.replace('{{VERSION}}', '1.0.0');
31
+ // Recent trades
32
+ const recent = services.tradeRepo.getRecent(10);
33
+ let tradesHtml = '';
34
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
35
+ for (const trade of recent) {
36
+ const win = trade.win ? 'win' : 'loss';
37
+ const resultLabel = trade.win ? 'WIN' : 'LOSS';
38
+ tradesHtml += `<div class="trade-card ${win}"><div class="trade-meta"><span class="trade-result ${win}">${resultLabel}</span><strong>${escapeHtml(trade.pair ?? '')}</strong></div><p>${escapeHtml(trade.fingerprint ?? '')}</p><div class="trade-details"><span>${escapeHtml(trade.bot_type ?? '')}</span><span>${escapeHtml(trade.created_at?.slice(0, 10) ?? '')}</span></div></div>\n`;
39
+ }
40
+ if (!tradesHtml)
41
+ tradesHtml = '<p class="empty">No trades recorded yet. Start trading to see results here.</p>';
42
+ html = html.replace('{{RECENT_TRADES}}', tradesHtml);
43
+ // Chains
44
+ const chains = s.chains?.recent ?? [];
45
+ let chainsHtml = '';
46
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
47
+ for (const chain of chains) {
48
+ const type = chain.type === 'win' ? 'win' : 'loss';
49
+ const icon = chain.type === 'win' ? '&#128293;' : '&#10060;';
50
+ chainsHtml += `<div class="chain-card"><div class="chain-icon">${icon}</div><div class="chain-info"><div class="chain-pair">${escapeHtml(chain.pair ?? '')}</div><div class="chain-type">${type} streak</div></div><div class="chain-length ${type}">${chain.length}x</div></div>\n`;
51
+ }
52
+ if (!chainsHtml)
53
+ chainsHtml = '<p class="empty">No chains detected yet. Chains appear after 3+ consecutive wins or losses on the same pair.</p>';
54
+ html = html.replace('{{CHAINS_LIST}}', chainsHtml);
55
+ // Rules
56
+ const rules = services.ruleRepo.getAll();
57
+ let rulesHtml = '';
58
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
59
+ for (const rule of rules) {
60
+ const conf = Math.round((rule.confidence ?? 0) * 100);
61
+ const wr = Math.round((rule.win_rate ?? 0) * 100);
62
+ rulesHtml += `<div class="rule-card"><div class="rule-pattern">${escapeHtml(rule.pattern ?? '')}</div><div class="rule-recommendation">Win rate: ${wr}% (${rule.sample_count ?? 0} trades)</div><div class="rule-confidence"><span>Confidence:</span><div class="confidence-bar"><div class="confidence-fill" data-width="${conf}"></div></div><span>${conf}%</span></div></div>\n`;
63
+ }
64
+ if (!rulesHtml)
65
+ rulesHtml = '<p class="empty">No rules learned yet. Record more trades to discover patterns.</p>';
66
+ html = html.replace('{{RULES_LIST}}', rulesHtml);
67
+ // Calibration
68
+ const cal = services.calRepo.get();
69
+ if (cal) {
70
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
71
+ const c = cal;
72
+ const stage = (c.outcomeCount ?? 0) < 20 ? '1' : (c.outcomeCount ?? 0) < 100 ? '2' : (c.outcomeCount ?? 0) < 500 ? '3' : '4';
73
+ html = html.replace('{{CAL_STAGE}}', stage);
74
+ html = html.replace('{{CAL_LEARNING_RATE}}', String(c.learningRate ?? '0.3'));
75
+ html = html.replace('{{CAL_WILSON_Z}}', String(c.wilsonZ ?? '1.0'));
76
+ html = html.replace('{{CAL_DECAY}}', String((c.decayHalfLifeDays ?? 60) + 'd'));
77
+ }
78
+ else {
79
+ html = html.replace('{{CAL_STAGE}}', '1');
80
+ html = html.replace('{{CAL_LEARNING_RATE}}', '0.3');
81
+ html = html.replace('{{CAL_WILSON_Z}}', '1.0');
82
+ html = html.replace('{{CAL_DECAY}}', '60d');
83
+ }
84
+ // Insights by type
85
+ const allInsights = services.insight.getRecent(200);
86
+ const insightsByType = {
87
+ trend: [], gap: [], synergy: [], performance: [], regime_shift: [],
88
+ };
89
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
90
+ for (const ins of allInsights) {
91
+ const type = ins.type ?? 'performance';
92
+ if (insightsByType[type])
93
+ insightsByType[type].push(ins);
94
+ else
95
+ insightsByType.performance.push(ins);
96
+ }
97
+ const typeColors = {
98
+ trend: 'cyan', gap: 'orange', synergy: 'green', performance: 'blue', regime_shift: 'red',
99
+ };
100
+ const pluralMap = {
101
+ trend: 'TRENDS', gap: 'GAPS', synergy: 'SYNERGIES',
102
+ performance: 'PERFORMANCE', regime_shift: 'REGIMES',
103
+ };
104
+ for (const [type, items] of Object.entries(insightsByType)) {
105
+ const plural = pluralMap[type] ?? `${type.toUpperCase()}S`;
106
+ html = html.replace(`{{${plural}_COUNT}}`, String(items.length));
107
+ let insHtml = '';
108
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
109
+ for (const ins of items) {
110
+ const sev = ins.severity === 'high' ? 'high' : ins.severity === 'medium' ? 'medium' : 'low';
111
+ insHtml += `<div class="insight-card ${typeColors[type] ?? 'blue'}"><div class="insight-header"><span class="prio prio-${sev}">${escapeHtml(ins.severity ?? 'low')}</span><strong>${escapeHtml(ins.title ?? '')}</strong></div><p>${escapeHtml(ins.description ?? '')}</p></div>\n`;
112
+ }
113
+ if (!insHtml)
114
+ insHtml = '<p class="empty">No insights in this category yet.</p>';
115
+ html = html.replace(`{{${plural}}}`, insHtml);
116
+ }
117
+ // Graph edges
118
+ const strongest = services.synapseManager.getStrongest(50);
119
+ const edges = Array.isArray(strongest) ? strongest : [];
120
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
121
+ const graphEdges = edges.map((e) => ({
122
+ s: `${e.source_type ?? 'node'}:${e.source_id ?? ''}`,
123
+ t: `${e.target_type ?? 'node'}:${e.target_id ?? ''}`,
124
+ type: e.synapse_type ?? 'related',
125
+ w: e.weight ?? 0.5,
126
+ }));
127
+ html = html.replace('{{GRAPH_EDGES}}', JSON.stringify(graphEdges));
128
+ return html;
129
+ }
130
+ //# sourceMappingURL=renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.js","sourceRoot":"","sources":["../../src/dashboard/renderer.ts"],"names":[],"mappings":"AAQA,SAAS,UAAU,CAAC,GAAW;IAC7B,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACxG,CAAC;AAYD,MAAM,UAAU,eAAe,CAAC,QAAgB,EAAE,QAA2B;IAC3E,IAAI,IAAI,GAAG,QAAQ,CAAC;IAEpB,8DAA8D;IAC9D,MAAM,OAAO,GAAQ,QAAQ,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;IACrD,MAAM,CAAC,GAAG,OAAO,CAAC;IAElB,QAAQ;IACR,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC;IAChE,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9D,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC;IAChE,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC;IACpE,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,IAAI,CAAC,CAAC,CAAC,CAAC;IACtE,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,EAAE,UAAU,IAAI,CAAC,CAAC,CAAC,CAAC;IAE3E,WAAW;IACX,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,aAAa,IAAI,CAAC,CAAC,CAAC;IACzD,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACrD,8EAA8E;IAC9E,MAAM,WAAW,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC7D,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,WAAW,CAAC,CAAC;IAErD,iBAAiB;IACjB,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,CACvC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC;QAC1B,CAAC,CAAC,CAAC,KAAK,EAAE,KAAK,IAAI,CAAC,CAAC,GAAG,EAAE;QAC1B,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC;QAC1B,CAAC,CAAC,CAAC,QAAQ,EAAE,KAAK,IAAI,CAAC,CAAC,GAAG,CAAC;QAC5B,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CACrC,CAAC,CAAC;IACH,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;IAE3D,UAAU;IACV,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IAE5C,gBAAgB;IAChB,MAAM,MAAM,GAAG,QAAQ,CAAC,SAAS,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IAChD,IAAI,UAAU,GAAG,EAAE,CAAC;IACpB,8DAA8D;IAC9D,KAAK,MAAM,KAAK,IAAI,MAAe,EAAE,CAAC;QACpC,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;QACvC,MAAM,WAAW,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;QAC/C,UAAU,IAAI,0BAA0B,GAAG,uDAAuD,GAAG,KAAK,WAAW,kBAAkB,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,qBAAqB,UAAU,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,CAAC,wCAAwC,UAAU,CAAC,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC,gBAAgB,UAAU,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,uBAAuB,CAAC;IAC7X,CAAC;IACD,IAAI,CAAC,UAAU;QAAE,UAAU,GAAG,iFAAiF,CAAC;IAChH,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,mBAAmB,EAAE,UAAU,CAAC,CAAC;IAErD,SAAS;IACT,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,MAAM,IAAI,EAAE,CAAC;IACtC,IAAI,UAAU,GAAG,EAAE,CAAC;IACpB,8DAA8D;IAC9D,KAAK,MAAM,KAAK,IAAI,MAAe,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;QACnD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC;QAC7D,UAAU,IAAI,mDAAmD,IAAI,yDAAyD,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,iCAAiC,IAAI,+CAA+C,IAAI,KAAK,KAAK,CAAC,MAAM,iBAAiB,CAAC;IACvR,CAAC;IACD,IAAI,CAAC,UAAU;QAAE,UAAU,GAAG,kHAAkH,CAAC;IACjJ,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,UAAU,CAAC,CAAC;IAEnD,QAAQ;IACR,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;IACzC,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,8DAA8D;IAC9D,KAAK,MAAM,IAAI,IAAI,KAAc,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;QACtD,MAAM,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC;QAClD,SAAS,IAAI,oDAAoD,UAAU,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,oDAAoD,EAAE,MAAM,IAAI,CAAC,YAAY,IAAI,CAAC,2IAA2I,IAAI,uBAAuB,IAAI,wBAAwB,CAAC;IACtX,CAAC;IACD,IAAI,CAAC,SAAS;QAAE,SAAS,GAAG,qFAAqF,CAAC;IAClH,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC;IAEjD,cAAc;IACd,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;IACnC,IAAI,GAAG,EAAE,CAAC;QACR,8DAA8D;QAC9D,MAAM,CAAC,GAAG,GAAU,CAAC;QACrB,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,IAAI,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;QAC7H,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;QAC5C,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC,CAAC,YAAY,IAAI,KAAK,CAAC,CAAC,CAAC;QAC9E,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAC,CAAC,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC;QACpE,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,iBAAiB,IAAI,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;IAClF,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC;QAC1C,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,uBAAuB,EAAE,KAAK,CAAC,CAAC;QACpD,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAAC;QAC/C,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;IAC9C,CAAC;IAED,mBAAmB;IACnB,MAAM,WAAW,GAAG,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IACpD,MAAM,cAAc,GAA8B;QAChD,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,YAAY,EAAE,EAAE;KACnE,CAAC;IACF,8DAA8D;IAC9D,KAAK,MAAM,GAAG,IAAI,WAAoB,EAAE,CAAC;QACvC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,IAAI,aAAa,CAAC;QACvC,IAAI,cAAc,CAAC,IAAI,CAAC;YAAE,cAAc,CAAC,IAAI,CAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;;YACrD,cAAc,CAAC,WAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,UAAU,GAA2B;QACzC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,KAAK;KACzF,CAAC;IAEF,MAAM,SAAS,GAA2B;QACxC,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW;QAClD,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,SAAS;KACpD,CAAC;IAEF,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;QAC3D,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC;QAC3D,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,MAAM,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QAEjE,IAAI,OAAO,GAAG,EAAE,CAAC;QACjB,8DAA8D;QAC9D,KAAK,MAAM,GAAG,IAAI,KAAc,EAAE,CAAC;YACjC,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;YAC5F,OAAO,IAAI,4BAA4B,UAAU,CAAC,IAAI,CAAC,IAAI,MAAM,wDAAwD,GAAG,KAAK,UAAU,CAAC,GAAG,CAAC,QAAQ,IAAI,KAAK,CAAC,kBAAkB,UAAU,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,qBAAqB,UAAU,CAAC,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,cAAc,CAAC;QACtR,CAAC;QACD,IAAI,CAAC,OAAO;YAAE,OAAO,GAAG,wDAAwD,CAAC;QACjF,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,MAAM,IAAI,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;IAED,cAAc;IACd,MAAM,SAAS,GAAG,QAAQ,CAAC,cAAc,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IAC3D,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;IACxD,8DAA8D;IAC9D,MAAM,UAAU,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC;QACxC,CAAC,EAAE,GAAG,CAAC,CAAC,WAAW,IAAI,MAAM,IAAI,CAAC,CAAC,SAAS,IAAI,EAAE,EAAE;QACpD,CAAC,EAAE,GAAG,CAAC,CAAC,WAAW,IAAI,MAAM,IAAI,CAAC,CAAC,SAAS,IAAI,EAAE,EAAE;QACpD,IAAI,EAAE,CAAC,CAAC,YAAY,IAAI,SAAS;QACjC,CAAC,EAAE,CAAC,CAAC,MAAM,IAAI,GAAG;KACnB,CAAC,CAAC,CAAC;IACJ,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,iBAAiB,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC;IAEnE,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,15 @@
1
+ export interface DashboardServerOptions {
2
+ port: number;
3
+ getDashboardHtml: () => string;
4
+ getStats: () => unknown;
5
+ }
6
+ export declare class DashboardServer {
7
+ private options;
8
+ private server;
9
+ private clients;
10
+ private logger;
11
+ constructor(options: DashboardServerOptions);
12
+ start(): void;
13
+ stop(): void;
14
+ private broadcast;
15
+ }
@@ -0,0 +1,116 @@
1
+ import http from 'node:http';
2
+ import { getEventBus } from '../utils/events.js';
3
+ import { getLogger } from '../utils/logger.js';
4
+ export class DashboardServer {
5
+ options;
6
+ server = null;
7
+ clients = new Set();
8
+ logger = getLogger();
9
+ constructor(options) {
10
+ this.options = options;
11
+ }
12
+ start() {
13
+ const { port, getDashboardHtml, getStats } = this.options;
14
+ const bus = getEventBus();
15
+ this.server = http.createServer((req, res) => {
16
+ const url = new URL(req.url ?? '/', `http://localhost:${port}`);
17
+ res.setHeader('Access-Control-Allow-Origin', '*');
18
+ res.setHeader('Access-Control-Allow-Methods', 'GET, OPTIONS');
19
+ if (req.method === 'OPTIONS') {
20
+ res.writeHead(204);
21
+ res.end();
22
+ return;
23
+ }
24
+ if (url.pathname === '/events') {
25
+ res.writeHead(200, {
26
+ 'Content-Type': 'text/event-stream',
27
+ 'Cache-Control': 'no-cache',
28
+ 'Connection': 'keep-alive',
29
+ });
30
+ res.write('data: {"type":"connected"}\n\n');
31
+ this.clients.add(res);
32
+ req.on('close', () => this.clients.delete(res));
33
+ return;
34
+ }
35
+ if (url.pathname === '/api/stats') {
36
+ const stats = getStats();
37
+ res.writeHead(200, { 'Content-Type': 'application/json' });
38
+ res.end(JSON.stringify(stats));
39
+ return;
40
+ }
41
+ if (url.pathname === '/' || url.pathname === '/dashboard') {
42
+ const html = getDashboardHtml();
43
+ const sseScript = `
44
+ <script>
45
+ (function(){
46
+ const evtSource = new EventSource('/events');
47
+ evtSource.onmessage = function(e) {
48
+ try {
49
+ const data = JSON.parse(e.data);
50
+ if (data.type === 'stats_update') {
51
+ document.querySelectorAll('.stat-number').forEach(el => {
52
+ const key = el.parentElement?.querySelector('.stat-label')?.textContent?.toLowerCase();
53
+ if (key && data.stats[key] !== undefined) {
54
+ el.textContent = Number(data.stats[key]).toLocaleString();
55
+ }
56
+ });
57
+ }
58
+ if (data.type === 'event') {
59
+ const dot = document.querySelector('.activity-dot');
60
+ if (dot) { dot.style.background = '#ff5577'; setTimeout(() => dot.style.background = '', 500); }
61
+ }
62
+ } catch {}
63
+ };
64
+ evtSource.onerror = function() { setTimeout(() => location.reload(), 5000); };
65
+ })();
66
+ </script>`;
67
+ const liveHtml = html.replace('</body>', sseScript + '</body>');
68
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
69
+ res.end(liveHtml);
70
+ return;
71
+ }
72
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
73
+ res.end('Not Found');
74
+ });
75
+ const eventNames = [
76
+ 'trade:recorded', 'synapse:updated',
77
+ 'rule:learned', 'chain:detected',
78
+ 'insight:created', 'calibration:updated',
79
+ ];
80
+ for (const eventName of eventNames) {
81
+ bus.on(eventName, (data) => {
82
+ this.broadcast({ type: 'event', event: eventName, data });
83
+ });
84
+ }
85
+ setInterval(() => {
86
+ if (this.clients.size > 0) {
87
+ const stats = getStats();
88
+ this.broadcast({ type: 'stats_update', stats });
89
+ }
90
+ }, 30_000);
91
+ this.server.listen(port, () => {
92
+ this.logger.info(`Dashboard server started on http://localhost:${port}`);
93
+ });
94
+ }
95
+ stop() {
96
+ for (const client of this.clients) {
97
+ client.end();
98
+ }
99
+ this.clients.clear();
100
+ this.server?.close();
101
+ this.server = null;
102
+ this.logger.info('Dashboard server stopped');
103
+ }
104
+ broadcast(data) {
105
+ const msg = `data: ${JSON.stringify(data)}\n\n`;
106
+ for (const client of this.clients) {
107
+ try {
108
+ client.write(msg);
109
+ }
110
+ catch {
111
+ this.clients.delete(client);
112
+ }
113
+ }
114
+ }
115
+ }
116
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/dashboard/server.ts"],"names":[],"mappings":"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"}
@@ -1,2 +1 @@
1
- import Database from 'better-sqlite3';
2
- export declare function createConnection(dbPath: string): Database.Database;
1
+ export { createConnection } from '@timmeck/brain-core';
@@ -1,19 +1,2 @@
1
- import Database from 'better-sqlite3';
2
- import fs from 'node:fs';
3
- import path from 'node:path';
4
- import { getLogger } from '../utils/logger.js';
5
- export function createConnection(dbPath) {
6
- const logger = getLogger();
7
- const dir = path.dirname(dbPath);
8
- if (!fs.existsSync(dir)) {
9
- fs.mkdirSync(dir, { recursive: true });
10
- }
11
- logger.info(`Opening database at ${dbPath}`);
12
- const db = new Database(dbPath);
13
- db.pragma('journal_mode = WAL');
14
- db.pragma('synchronous = NORMAL');
15
- db.pragma('cache_size = 10000');
16
- db.pragma('foreign_keys = ON');
17
- return db;
18
- }
1
+ export { createConnection } from '@timmeck/brain-core';
19
2
  //# sourceMappingURL=connection.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/db/connection.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,MAAM,CAAC,IAAI,CAAC,uBAAuB,MAAM,EAAE,CAAC,CAAC;IAC7C,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;IAEhC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC;IAClC,EAAE,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC;IAChC,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAE/B,OAAO,EAAE,CAAC;AACZ,CAAC"}
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../../src/db/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env node
2
+ // PostToolUse hook for Bash tool — auto-detects trade outcomes from bot output
3
+ // Looks for trade completion patterns in terminal output and records them to Trading Brain
4
+ //
5
+ // Configured in .claude/settings.json:
6
+ // { "hooks": { "PostToolUse": [{ "matcher": { "tool_name": "Bash" }, "hooks": [{ "type": "command", "command": "npx tsx C:/Users/mecklenburg/Desktop/trading-brain/src/hooks/post-tool-use.ts" }] }] } }
7
+ import { IpcClient } from '../ipc/client.js';
8
+ import { getPipeName } from '../utils/paths.js';
9
+ // Patterns that indicate a trade was completed
10
+ const TRADE_PATTERNS = [
11
+ // DCA Bot patterns
12
+ /(?:DCA|dca)\s+(?:bot|trade)\s+(?:completed|finished|closed)\s+(?:on|for)\s+([\w/]+)\s+.*?(win|loss|profit|loss)/i,
13
+ // Grid bot patterns
14
+ /(?:grid|GRID)\s+(?:bot|trade)\s+(?:completed|finished|closed)\s+(?:on|for)\s+([\w/]+)\s+.*?(win|loss|profit|loss)/i,
15
+ // Generic trade outcome
16
+ /trade\s+(?:outcome|result|completed?):\s*([\w/]+)\s+.*?(win|loss|profit|loss)/i,
17
+ // Profit/Loss with pair
18
+ /([\w]+\/[\w]+)\s+.*?(?:P&?L|PnL|profit|loss):\s*([+-]?\$?[\d,.]+)/i,
19
+ // Bot closed position
20
+ /(?:closed|sold|exited)\s+(?:position|trade)\s+(?:on|for|in)\s+([\w/]+)\s+.*?(?:profit|loss|P&?L):\s*([+-]?\$?[\d,.]+)/i,
21
+ ];
22
+ // Extract RSI value from output
23
+ const RSI_PATTERN = /RSI[:\s]+(\d+(?:\.\d+)?)/i;
24
+ // Extract MACD value from output
25
+ const MACD_PATTERN = /MACD[:\s]+([+-]?\d+(?:\.\d+)?)/i;
26
+ // Extract trend value
27
+ const TREND_PATTERN = /trend[:\s]+([+-]?\d+(?:\.\d+)?)/i;
28
+ // Extract volatility value
29
+ const VOL_PATTERN = /volatil(?:ity|\.?)[:\s]+(\d+(?:\.\d+)?)/i;
30
+ // Extract bot type
31
+ const BOT_PATTERN = /(?:bot[_\s]?type|strategy)[:\s]+(dca|grid|spot|futures|scalp)/i;
32
+ function detectTrade(output) {
33
+ for (const pattern of TRADE_PATTERNS) {
34
+ const match = output.match(pattern);
35
+ if (!match)
36
+ continue;
37
+ const pair = match[1];
38
+ const resultText = match[2].toLowerCase();
39
+ const win = resultText.includes('win') || resultText.includes('profit') ||
40
+ (resultText.startsWith('+') && !resultText.startsWith('+-'));
41
+ // Try to extract signal values
42
+ const rsiMatch = output.match(RSI_PATTERN);
43
+ const macdMatch = output.match(MACD_PATTERN);
44
+ const trendMatch = output.match(TREND_PATTERN);
45
+ const volMatch = output.match(VOL_PATTERN);
46
+ const botMatch = output.match(BOT_PATTERN);
47
+ return {
48
+ pair: pair.toUpperCase(),
49
+ win,
50
+ botType: botMatch?.[1] ?? 'dca',
51
+ rsi: rsiMatch ? parseFloat(rsiMatch[1]) : undefined,
52
+ macdHistogram: macdMatch ? parseFloat(macdMatch[1]) : undefined,
53
+ trendStrength: trendMatch ? parseFloat(trendMatch[1]) : undefined,
54
+ volatility: volMatch ? parseFloat(volMatch[1]) : undefined,
55
+ };
56
+ }
57
+ return null;
58
+ }
59
+ function readStdin() {
60
+ return new Promise((resolve) => {
61
+ let data = '';
62
+ process.stdin.setEncoding('utf8');
63
+ process.stdin.on('data', (chunk) => { data += chunk; });
64
+ process.stdin.on('end', () => resolve(data));
65
+ });
66
+ }
67
+ async function main() {
68
+ const raw = await readStdin();
69
+ if (!raw.trim())
70
+ return;
71
+ let input;
72
+ try {
73
+ input = JSON.parse(raw);
74
+ }
75
+ catch {
76
+ return;
77
+ }
78
+ const output = input.tool_output ?? input.tool_response?.stdout ?? '';
79
+ if (!output)
80
+ return;
81
+ const trade = detectTrade(output);
82
+ if (!trade)
83
+ return;
84
+ const client = new IpcClient(getPipeName(), 3000);
85
+ try {
86
+ await client.connect();
87
+ await client.request('trade.recordOutcome', {
88
+ pair: trade.pair,
89
+ win: trade.win,
90
+ botType: trade.botType,
91
+ signals: {
92
+ rsi: trade.rsi ?? 50,
93
+ macdHistogram: trade.macdHistogram ?? 0,
94
+ trendStrength: trade.trendStrength ?? 0,
95
+ volatility: trade.volatility ?? 0.5,
96
+ },
97
+ });
98
+ process.stderr.write(`Trading Brain: Recorded ${trade.win ? 'WIN' : 'LOSS'} for ${trade.pair} (${trade.botType})\n`);
99
+ }
100
+ catch {
101
+ // Hook must never block workflow
102
+ }
103
+ finally {
104
+ client.disconnect();
105
+ }
106
+ }
107
+ main();
108
+ //# sourceMappingURL=post-tool-use.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"post-tool-use.js","sourceRoot":"","sources":["../../src/hooks/post-tool-use.ts"],"names":[],"mappings":";AAEA,+EAA+E;AAC/E,2FAA2F;AAC3F,EAAE;AACF,uCAAuC;AACvC,yMAAyM;AAEzM,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAShD,+CAA+C;AAC/C,MAAM,cAAc,GAAG;IACrB,mBAAmB;IACnB,kHAAkH;IAClH,oBAAoB;IACpB,oHAAoH;IACpH,wBAAwB;IACxB,gFAAgF;IAChF,wBAAwB;IACxB,oEAAoE;IACpE,sBAAsB;IACtB,wHAAwH;CACzH,CAAC;AAEF,gCAAgC;AAChC,MAAM,WAAW,GAAG,2BAA2B,CAAC;AAChD,iCAAiC;AACjC,MAAM,YAAY,GAAG,iCAAiC,CAAC;AACvD,sBAAsB;AACtB,MAAM,aAAa,GAAG,kCAAkC,CAAC;AACzD,2BAA2B;AAC3B,MAAM,WAAW,GAAG,0CAA0C,CAAC;AAC/D,mBAAmB;AACnB,MAAM,WAAW,GAAG,gEAAgE,CAAC;AAYrF,SAAS,WAAW,CAAC,MAAc;IACjC,KAAK,MAAM,OAAO,IAAI,cAAc,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACpC,IAAI,CAAC,KAAK;YAAE,SAAS;QAErB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;QACvB,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC,WAAW,EAAE,CAAC;QAC3C,MAAM,GAAG,GAAG,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC;YACrE,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;QAE/D,+BAA+B;QAC/B,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC3C,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QAC7C,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAC/C,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAC3C,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAE3C,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE;YACxB,GAAG;YACH,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK;YAC/B,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,SAAS;YACpD,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,SAAS;YAChE,aAAa,EAAE,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,SAAS;YAClE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,SAAS;SAC5D,CAAC;IACJ,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,SAAS;IAChB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,IAAI,IAAI,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAClC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,GAAG,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACxD,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,GAAG,GAAG,MAAM,SAAS,EAAE,CAAC;IAC9B,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE;QAAE,OAAO;IAExB,IAAI,KAAgB,CAAC;IACrB,IAAI,CAAC;QACH,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,aAAa,EAAE,MAAM,IAAI,EAAE,CAAC;IACtE,IAAI,CAAC,MAAM;QAAE,OAAO;IAEpB,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IAClC,IAAI,CAAC,KAAK;QAAE,OAAO;IAEnB,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,CAAC;IAClD,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;QAEvB,MAAM,MAAM,CAAC,OAAO,CAAC,qBAAqB,EAAE;YAC1C,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,GAAG,EAAE,KAAK,CAAC,GAAG;YACd,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,OAAO,EAAE;gBACP,GAAG,EAAE,KAAK,CAAC,GAAG,IAAI,EAAE;gBACpB,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,CAAC;gBACvC,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,CAAC;gBACvC,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,GAAG;aACpC;SACF,CAAC,CAAC;QAEH,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,2BAA2B,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,QAAQ,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,KAAK,CAC/F,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,iCAAiC;IACnC,CAAC;YAAS,CAAC;QACT,MAAM,CAAC,UAAU,EAAE,CAAC;IACtB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC"}
package/dist/index.js CHANGED
@@ -11,11 +11,13 @@ import { exportCommand } from './cli/commands/export.js';
11
11
  import { importCommand } from './cli/commands/import.js';
12
12
  import { configCommand } from './cli/commands/config.js';
13
13
  import { doctorCommand } from './cli/commands/doctor.js';
14
+ import { dashboardCommand } from './cli/commands/dashboard.js';
15
+ import { peersCommand } from './cli/commands/peers.js';
14
16
  const program = new Command();
15
17
  program
16
18
  .name('trading')
17
19
  .description('Trading Brain — Adaptive Trading Intelligence & Signal Learning System')
18
- .version('1.0.0');
20
+ .version('1.1.0');
19
21
  program.addCommand(startCommand());
20
22
  program.addCommand(stopCommand());
21
23
  program.addCommand(statusCommand());
@@ -27,6 +29,8 @@ program.addCommand(exportCommand());
27
29
  program.addCommand(importCommand());
28
30
  program.addCommand(configCommand());
29
31
  program.addCommand(doctorCommand());
32
+ program.addCommand(dashboardCommand());
33
+ program.addCommand(peersCommand());
30
34
  // Hidden command: run MCP server (called by Claude Code)
31
35
  program
32
36
  .command('mcp-server')
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEzD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,SAAS,CAAC;KACf,WAAW,CAAC,wEAAwE,CAAC;KACrF,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;AACpC,OAAO,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,eAAe,EAAE,CAAC,CAAC;AACtC,OAAO,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,cAAc,EAAE,CAAC,CAAC;AACrC,OAAO,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;AACpC,OAAO,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;AACpC,OAAO,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;AACpC,OAAO,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;AAEpC,yDAAyD;AACzD,OAAO;KACJ,OAAO,CAAC,YAAY,CAAC;KACrB,WAAW,CAAC,yDAAyD,CAAC;KACtE,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAC3D,MAAM,cAAc,EAAE,CAAC;AACzB,CAAC,CAAC,CAAC;AAEL,qEAAqE;AACrE,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,0BAA0B,CAAC;KACvC,MAAM,CAAC,qBAAqB,EAAE,kBAAkB,CAAC;KACjD,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC1D,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC;IAC/B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC1B,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAEvD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,SAAS,CAAC;KACf,WAAW,CAAC,wEAAwE,CAAC;KACrF,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;AACpC,OAAO,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,eAAe,EAAE,CAAC,CAAC;AACtC,OAAO,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,cAAc,EAAE,CAAC,CAAC;AACrC,OAAO,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;AACpC,OAAO,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;AACpC,OAAO,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;AACpC,OAAO,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC,CAAC;AACpC,OAAO,CAAC,UAAU,CAAC,gBAAgB,EAAE,CAAC,CAAC;AACvC,OAAO,CAAC,UAAU,CAAC,YAAY,EAAE,CAAC,CAAC;AAEnC,yDAAyD;AACzD,OAAO;KACJ,OAAO,CAAC,YAAY,CAAC;KACrB,WAAW,CAAC,yDAAyD,CAAC;KACtE,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,CAAC;IAC3D,MAAM,cAAc,EAAE,CAAC;AACzB,CAAC,CAAC,CAAC;AAEL,qEAAqE;AACrE,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,0BAA0B,CAAC;KACvC,MAAM,CAAC,qBAAqB,EAAE,kBAAkB,CAAC;KACjD,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAC1D,MAAM,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC;IAC/B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC1B,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,89 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { Buffer } from 'node:buffer';
3
+ import { encodeMessage, MessageDecoder } from '../protocol.js';
4
+ const sampleMessage = {
5
+ id: '1',
6
+ type: 'request',
7
+ method: 'ping',
8
+ params: { ts: 123 },
9
+ };
10
+ describe('encodeMessage', () => {
11
+ it('produces a buffer starting with a 4-byte big-endian length prefix', () => {
12
+ const buf = encodeMessage(sampleMessage);
13
+ const payloadLength = buf.readUInt32BE(0);
14
+ expect(buf.length).toBe(4 + payloadLength);
15
+ });
16
+ it('payload is valid JSON matching the original message', () => {
17
+ const buf = encodeMessage(sampleMessage);
18
+ const payloadLength = buf.readUInt32BE(0);
19
+ const json = buf.subarray(4, 4 + payloadLength).toString('utf8');
20
+ expect(JSON.parse(json)).toEqual(sampleMessage);
21
+ });
22
+ it('encodes a minimal notification message', () => {
23
+ const msg = { id: '2', type: 'notification' };
24
+ const buf = encodeMessage(msg);
25
+ const payloadLength = buf.readUInt32BE(0);
26
+ const decoded = JSON.parse(buf.subarray(4, 4 + payloadLength).toString('utf8'));
27
+ expect(decoded).toEqual(msg);
28
+ });
29
+ });
30
+ describe('MessageDecoder', () => {
31
+ it('decodes a single complete frame', () => {
32
+ const decoder = new MessageDecoder();
33
+ const frame = encodeMessage(sampleMessage);
34
+ const messages = decoder.feed(frame);
35
+ expect(messages).toHaveLength(1);
36
+ expect(messages[0]).toEqual(sampleMessage);
37
+ });
38
+ it('decodes multiple frames fed at once', () => {
39
+ const decoder = new MessageDecoder();
40
+ const msg1 = { id: '1', type: 'request', method: 'a' };
41
+ const msg2 = { id: '2', type: 'response', result: 42 };
42
+ const combined = Buffer.concat([encodeMessage(msg1), encodeMessage(msg2)]);
43
+ const messages = decoder.feed(combined);
44
+ expect(messages).toHaveLength(2);
45
+ expect(messages[0]).toEqual(msg1);
46
+ expect(messages[1]).toEqual(msg2);
47
+ });
48
+ it('handles partial frames across multiple feed() calls', () => {
49
+ const decoder = new MessageDecoder();
50
+ const frame = encodeMessage(sampleMessage);
51
+ // Split the frame in the middle
52
+ const splitPoint = Math.floor(frame.length / 2);
53
+ const part1 = frame.subarray(0, splitPoint);
54
+ const part2 = frame.subarray(splitPoint);
55
+ const firstResult = decoder.feed(part1);
56
+ expect(firstResult).toHaveLength(0);
57
+ const secondResult = decoder.feed(part2);
58
+ expect(secondResult).toHaveLength(1);
59
+ expect(secondResult[0]).toEqual(sampleMessage);
60
+ });
61
+ it('handles byte-by-byte feeding', () => {
62
+ const decoder = new MessageDecoder();
63
+ const frame = encodeMessage(sampleMessage);
64
+ let messages = [];
65
+ for (let i = 0; i < frame.length; i++) {
66
+ const result = decoder.feed(Buffer.from([frame[i]]));
67
+ messages.push(...result);
68
+ }
69
+ expect(messages).toHaveLength(1);
70
+ expect(messages[0]).toEqual(sampleMessage);
71
+ });
72
+ it('reset() clears internal buffer', () => {
73
+ const decoder = new MessageDecoder();
74
+ const frame = encodeMessage(sampleMessage);
75
+ // Feed a partial frame then reset
76
+ decoder.feed(frame.subarray(0, 3));
77
+ decoder.reset();
78
+ // Feed a complete frame after reset
79
+ const messages = decoder.feed(frame);
80
+ expect(messages).toHaveLength(1);
81
+ expect(messages[0]).toEqual(sampleMessage);
82
+ });
83
+ it('returns empty array when fed an empty buffer', () => {
84
+ const decoder = new MessageDecoder();
85
+ const messages = decoder.feed(Buffer.alloc(0));
86
+ expect(messages).toHaveLength(0);
87
+ });
88
+ });
89
+ //# sourceMappingURL=protocol.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.test.js","sourceRoot":"","sources":["../../../src/ipc/__tests__/protocol.test.ts"],"names":[],"mappings":"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"}
@@ -1,16 +1 @@
1
- import type { IpcMessage } from '../types/ipc.types.js';
2
- export declare class IpcClient {
3
- private pipeName;
4
- private timeout;
5
- private socket;
6
- private decoder;
7
- private pending;
8
- private onNotification?;
9
- constructor(pipeName?: string, timeout?: number);
10
- connect(): Promise<void>;
11
- request(method: string, params?: unknown): Promise<unknown>;
12
- setNotificationHandler(handler: (msg: IpcMessage) => void): void;
13
- disconnect(): void;
14
- get connected(): boolean;
15
- private handleMessage;
16
- }
1
+ export { IpcClient } from '@timmeck/brain-core';