@timmeck/brain-core 2.36.11 → 2.36.14
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/dist/cross-brain/__tests__/borg-sync-engine.test.d.ts +1 -0
- package/dist/cross-brain/__tests__/borg-sync-engine.test.js +240 -0
- package/dist/cross-brain/__tests__/borg-sync-engine.test.js.map +1 -0
- package/dist/cross-brain/borg-sync-engine.d.ts +62 -0
- package/dist/cross-brain/borg-sync-engine.js +215 -0
- package/dist/cross-brain/borg-sync-engine.js.map +1 -0
- package/dist/cross-brain/borg-types.d.ts +37 -0
- package/dist/cross-brain/borg-types.js +9 -0
- package/dist/cross-brain/borg-types.js.map +1 -0
- package/dist/embeddings/engine.js +2 -1
- package/dist/embeddings/engine.js.map +1 -1
- package/dist/index.d.ts +18 -1
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/llm/__tests__/anthropic-provider.test.d.ts +1 -0
- package/dist/llm/__tests__/anthropic-provider.test.js +121 -0
- package/dist/llm/__tests__/anthropic-provider.test.js.map +1 -0
- package/dist/llm/__tests__/llm-service.test.js +181 -40
- package/dist/llm/__tests__/llm-service.test.js.map +1 -1
- package/dist/llm/__tests__/ollama-embedding.test.d.ts +1 -0
- package/dist/llm/__tests__/ollama-embedding.test.js +128 -0
- package/dist/llm/__tests__/ollama-embedding.test.js.map +1 -0
- package/dist/llm/__tests__/ollama-provider.test.d.ts +1 -0
- package/dist/llm/__tests__/ollama-provider.test.js +213 -0
- package/dist/llm/__tests__/ollama-provider.test.js.map +1 -0
- package/dist/llm/__tests__/provider.test.d.ts +1 -0
- package/dist/llm/__tests__/provider.test.js +126 -0
- package/dist/llm/__tests__/provider.test.js.map +1 -0
- package/dist/llm/anthropic-provider.d.ts +41 -0
- package/dist/llm/anthropic-provider.js +86 -0
- package/dist/llm/anthropic-provider.js.map +1 -0
- package/dist/llm/index.d.ts +9 -1
- package/dist/llm/index.js +4 -0
- package/dist/llm/index.js.map +1 -1
- package/dist/llm/llm-service.d.ts +55 -7
- package/dist/llm/llm-service.js +184 -82
- package/dist/llm/llm-service.js.map +1 -1
- package/dist/llm/ollama-embedding.d.ts +46 -0
- package/dist/llm/ollama-embedding.js +93 -0
- package/dist/llm/ollama-embedding.js.map +1 -0
- package/dist/llm/ollama-provider.d.ts +80 -0
- package/dist/llm/ollama-provider.js +178 -0
- package/dist/llm/ollama-provider.js.map +1 -0
- package/dist/llm/provider.d.ts +120 -0
- package/dist/llm/provider.js +104 -0
- package/dist/llm/provider.js.map +1 -0
- package/dist/missions/mission-engine.d.ts +4 -0
- package/dist/missions/mission-engine.js +30 -8
- package/dist/missions/mission-engine.js.map +1 -1
- package/dist/notifications/__tests__/notification-service.test.d.ts +1 -0
- package/dist/notifications/__tests__/notification-service.test.js +176 -0
- package/dist/notifications/__tests__/notification-service.test.js.map +1 -0
- package/dist/notifications/discord-provider.d.ts +30 -0
- package/dist/notifications/discord-provider.js +89 -0
- package/dist/notifications/discord-provider.js.map +1 -0
- package/dist/notifications/email-provider.d.ts +41 -0
- package/dist/notifications/email-provider.js +101 -0
- package/dist/notifications/email-provider.js.map +1 -0
- package/dist/notifications/index.d.ts +8 -0
- package/dist/notifications/index.js +5 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/notifications/notification-provider.d.ts +75 -0
- package/dist/notifications/notification-provider.js +47 -0
- package/dist/notifications/notification-provider.js.map +1 -0
- package/dist/notifications/notification-service.d.ts +85 -0
- package/dist/notifications/notification-service.js +184 -0
- package/dist/notifications/notification-service.js.map +1 -0
- package/dist/notifications/telegram-provider.d.ts +30 -0
- package/dist/notifications/telegram-provider.js +78 -0
- package/dist/notifications/telegram-provider.js.map +1 -0
- package/dist/plugin/__tests__/plugin-registry.test.d.ts +1 -0
- package/dist/plugin/__tests__/plugin-registry.test.js +166 -0
- package/dist/plugin/__tests__/plugin-registry.test.js.map +1 -0
- package/dist/plugin/plugin-registry.d.ts +38 -0
- package/dist/plugin/plugin-registry.js +185 -0
- package/dist/plugin/plugin-registry.js.map +1 -0
- package/dist/plugin/types.d.ts +59 -0
- package/dist/plugin/types.js +2 -0
- package/dist/plugin/types.js.map +1 -0
- package/dist/research/adapters/__tests__/web-adapters.test.d.ts +1 -0
- package/dist/research/adapters/__tests__/web-adapters.test.js +106 -0
- package/dist/research/adapters/__tests__/web-adapters.test.js.map +1 -0
- package/dist/research/adapters/firecrawl-adapter.d.ts +57 -0
- package/dist/research/adapters/firecrawl-adapter.js +137 -0
- package/dist/research/adapters/firecrawl-adapter.js.map +1 -0
- package/dist/research/adapters/index.d.ts +3 -0
- package/dist/research/adapters/index.js +2 -0
- package/dist/research/adapters/index.js.map +1 -1
- package/dist/research/adapters/playwright-adapter.d.ts +54 -0
- package/dist/research/adapters/playwright-adapter.js +130 -0
- package/dist/research/adapters/playwright-adapter.js.map +1 -0
- package/dist/research/research-orchestrator.d.ts +3 -0
- package/dist/research/research-orchestrator.js +19 -1
- package/dist/research/research-orchestrator.js.map +1 -1
- package/dist/self-modification/self-modification-engine.js +28 -4
- package/dist/self-modification/self-modification-engine.js.map +1 -1
- package/dist/techradar/__tests__/techradar-engine.test.d.ts +1 -0
- package/dist/techradar/__tests__/techradar-engine.test.js +246 -0
- package/dist/techradar/__tests__/techradar-engine.test.js.map +1 -0
- package/dist/techradar/daily-digest.d.ts +18 -0
- package/dist/techradar/daily-digest.js +100 -0
- package/dist/techradar/daily-digest.js.map +1 -0
- package/dist/techradar/index.d.ts +5 -0
- package/dist/techradar/index.js +5 -0
- package/dist/techradar/index.js.map +1 -0
- package/dist/techradar/relevance-scorer.d.ts +29 -0
- package/dist/techradar/relevance-scorer.js +139 -0
- package/dist/techradar/relevance-scorer.js.map +1 -0
- package/dist/techradar/repo-watcher.d.ts +24 -0
- package/dist/techradar/repo-watcher.js +87 -0
- package/dist/techradar/repo-watcher.js.map +1 -0
- package/dist/techradar/techradar-engine.d.ts +69 -0
- package/dist/techradar/techradar-engine.js +382 -0
- package/dist/techradar/techradar-engine.js.map +1 -0
- package/dist/techradar/types.d.ts +87 -0
- package/dist/techradar/types.js +5 -0
- package/dist/techradar/types.js.map +1 -0
- package/dist/watchdog/__tests__/watchdog-service.test.d.ts +1 -0
- package/dist/watchdog/__tests__/watchdog-service.test.js +113 -0
- package/dist/watchdog/__tests__/watchdog-service.test.js.map +1 -0
- package/dist/watchdog/watchdog-service.d.ts +60 -0
- package/dist/watchdog/watchdog-service.js +275 -0
- package/dist/watchdog/watchdog-service.js.map +1 -0
- package/dist/watchdog/windows-service.d.ts +39 -0
- package/dist/watchdog/windows-service.js +179 -0
- package/dist/watchdog/windows-service.js.map +1 -0
- package/package.json +20 -2
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TechRadar Engine — Täglicher Internet-Scan + Relevanz-Analyse
|
|
3
|
+
*
|
|
4
|
+
* ═══════════════════════════════════════════════════════════════
|
|
5
|
+
* EINRICHTEN
|
|
6
|
+
* ═══════════════════════════════════════════════════════════════
|
|
7
|
+
*
|
|
8
|
+
* Funktioniert out of the box (nutzt SignalScanner-Daten).
|
|
9
|
+
*
|
|
10
|
+
* Für bessere Ergebnisse:
|
|
11
|
+
* 1. GITHUB_TOKEN in .env → höheres Rate Limit für Repo-Watching
|
|
12
|
+
* 2. LLMService angebunden → intelligentes Relevanz-Scoring
|
|
13
|
+
* 3. Repos watchlisten:
|
|
14
|
+
* brain techradar repos add anthropics/claude-code
|
|
15
|
+
* brain techradar repos add modelcontextprotocol/servers
|
|
16
|
+
*
|
|
17
|
+
* CLI:
|
|
18
|
+
* brain techradar → Heutigen Digest anzeigen
|
|
19
|
+
* brain techradar scan → Jetzt scannen
|
|
20
|
+
* brain techradar repos list → Überwachte Repos
|
|
21
|
+
* brain techradar repos add <repo>
|
|
22
|
+
* ═══════════════════════════════════════════════════════════════
|
|
23
|
+
*/
|
|
24
|
+
import { getLogger } from '../utils/logger.js';
|
|
25
|
+
import { RepoWatcher } from './repo-watcher.js';
|
|
26
|
+
import { RelevanceScorer } from './relevance-scorer.js';
|
|
27
|
+
import { DigestGenerator } from './daily-digest.js';
|
|
28
|
+
const log = getLogger();
|
|
29
|
+
const DEFAULT_CONFIG = {
|
|
30
|
+
enabled: true,
|
|
31
|
+
scanIntervalMs: 6 * 60 * 60 * 1000, // 6h
|
|
32
|
+
digestTime: '06:00',
|
|
33
|
+
maxEntriesPerScan: 50,
|
|
34
|
+
relevanceThreshold: 30,
|
|
35
|
+
watchedRepos: [
|
|
36
|
+
'anthropics/claude-code',
|
|
37
|
+
'modelcontextprotocol/servers',
|
|
38
|
+
'anthropics/anthropic-sdk-python',
|
|
39
|
+
],
|
|
40
|
+
};
|
|
41
|
+
// ── Migration ────────────────────────────────────────────
|
|
42
|
+
export function runTechRadarMigration(db) {
|
|
43
|
+
db.exec(`
|
|
44
|
+
CREATE TABLE IF NOT EXISTS techradar_entries (
|
|
45
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
46
|
+
name TEXT NOT NULL,
|
|
47
|
+
source TEXT NOT NULL,
|
|
48
|
+
source_url TEXT NOT NULL DEFAULT '',
|
|
49
|
+
category TEXT NOT NULL DEFAULT 'other',
|
|
50
|
+
ring TEXT NOT NULL DEFAULT 'assess',
|
|
51
|
+
description TEXT NOT NULL DEFAULT '',
|
|
52
|
+
relevance_score REAL DEFAULT 0,
|
|
53
|
+
relevance_reason TEXT DEFAULT '',
|
|
54
|
+
action_type TEXT DEFAULT 'none',
|
|
55
|
+
action_detail TEXT DEFAULT '',
|
|
56
|
+
first_seen_at TEXT DEFAULT (datetime('now')),
|
|
57
|
+
last_seen_at TEXT DEFAULT (datetime('now')),
|
|
58
|
+
is_active INTEGER DEFAULT 1
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
CREATE INDEX IF NOT EXISTS idx_techradar_score ON techradar_entries(relevance_score DESC);
|
|
62
|
+
CREATE INDEX IF NOT EXISTS idx_techradar_source ON techradar_entries(source);
|
|
63
|
+
CREATE INDEX IF NOT EXISTS idx_techradar_ring ON techradar_entries(ring);
|
|
64
|
+
|
|
65
|
+
CREATE TABLE IF NOT EXISTS techradar_watched_repos (
|
|
66
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
67
|
+
full_name TEXT UNIQUE NOT NULL,
|
|
68
|
+
url TEXT NOT NULL DEFAULT '',
|
|
69
|
+
reason TEXT DEFAULT '',
|
|
70
|
+
last_release_tag TEXT,
|
|
71
|
+
last_release_at TEXT,
|
|
72
|
+
last_checked_at TEXT,
|
|
73
|
+
is_active INTEGER DEFAULT 1
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
CREATE TABLE IF NOT EXISTS techradar_digests (
|
|
77
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
78
|
+
date TEXT UNIQUE NOT NULL,
|
|
79
|
+
summary TEXT NOT NULL DEFAULT '',
|
|
80
|
+
entries_json TEXT DEFAULT '[]',
|
|
81
|
+
opportunities_json TEXT DEFAULT '[]',
|
|
82
|
+
action_items_json TEXT DEFAULT '[]',
|
|
83
|
+
created_at TEXT DEFAULT (datetime('now'))
|
|
84
|
+
);
|
|
85
|
+
`);
|
|
86
|
+
}
|
|
87
|
+
// ── Engine ────────────────────────────────────────────────
|
|
88
|
+
export class TechRadarEngine {
|
|
89
|
+
db;
|
|
90
|
+
config;
|
|
91
|
+
repoWatcher;
|
|
92
|
+
relevanceScorer;
|
|
93
|
+
digestGenerator;
|
|
94
|
+
scanTimer = null;
|
|
95
|
+
constructor(db, config = {}) {
|
|
96
|
+
this.db = db;
|
|
97
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
98
|
+
this.repoWatcher = new RepoWatcher(this.config.githubToken);
|
|
99
|
+
this.relevanceScorer = new RelevanceScorer();
|
|
100
|
+
this.digestGenerator = new DigestGenerator();
|
|
101
|
+
runTechRadarMigration(db);
|
|
102
|
+
this.ensureDefaultWatchedRepos();
|
|
103
|
+
}
|
|
104
|
+
setLLMService(llmService) {
|
|
105
|
+
this.relevanceScorer.setLLMService(llmService);
|
|
106
|
+
this.digestGenerator.setLLMService(llmService);
|
|
107
|
+
}
|
|
108
|
+
/** Start periodic scanning */
|
|
109
|
+
start() {
|
|
110
|
+
if (this.scanTimer)
|
|
111
|
+
return;
|
|
112
|
+
this.scanTimer = setInterval(() => {
|
|
113
|
+
this.scan().catch(err => {
|
|
114
|
+
log.error(`[TechRadar] Scan error: ${err.message}`);
|
|
115
|
+
});
|
|
116
|
+
}, this.config.scanIntervalMs);
|
|
117
|
+
log.info(`[TechRadar] Engine started (interval: ${this.config.scanIntervalMs / 1000}s)`);
|
|
118
|
+
}
|
|
119
|
+
/** Stop periodic scanning */
|
|
120
|
+
stop() {
|
|
121
|
+
if (this.scanTimer) {
|
|
122
|
+
clearInterval(this.scanTimer);
|
|
123
|
+
this.scanTimer = null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/** Run a full scan */
|
|
127
|
+
async scan() {
|
|
128
|
+
const startedAt = new Date().toISOString();
|
|
129
|
+
const errors = [];
|
|
130
|
+
let newEntries = 0;
|
|
131
|
+
let updatedEntries = 0;
|
|
132
|
+
let releasesFound = 0;
|
|
133
|
+
log.info('[TechRadar] Starting scan...');
|
|
134
|
+
// 1. Check watched repos for new releases
|
|
135
|
+
try {
|
|
136
|
+
const releases = await this.checkWatchedRepos();
|
|
137
|
+
releasesFound = releases;
|
|
138
|
+
newEntries += releases;
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
errors.push(`Repo watch: ${err.message}`);
|
|
142
|
+
}
|
|
143
|
+
// 2. Import high-scoring entries from SignalScanner (if available)
|
|
144
|
+
try {
|
|
145
|
+
const imported = this.importFromSignalScanner();
|
|
146
|
+
newEntries += imported.new;
|
|
147
|
+
updatedEntries += imported.updated;
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
errors.push(`Signal import: ${err.message}`);
|
|
151
|
+
}
|
|
152
|
+
// 3. Generate digest if we have new entries
|
|
153
|
+
let digestGenerated = false;
|
|
154
|
+
const today = new Date().toISOString().split('T')[0];
|
|
155
|
+
if (newEntries > 0 || updatedEntries > 0) {
|
|
156
|
+
try {
|
|
157
|
+
await this.generateDigest(today);
|
|
158
|
+
digestGenerated = true;
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
errors.push(`Digest: ${err.message}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
const result = {
|
|
165
|
+
started_at: startedAt,
|
|
166
|
+
finished_at: new Date().toISOString(),
|
|
167
|
+
duration_ms: Date.now() - new Date(startedAt).getTime(),
|
|
168
|
+
new_entries: newEntries,
|
|
169
|
+
updated_entries: updatedEntries,
|
|
170
|
+
releases_found: releasesFound,
|
|
171
|
+
digest_generated: digestGenerated,
|
|
172
|
+
errors,
|
|
173
|
+
};
|
|
174
|
+
log.info(`[TechRadar] Scan complete: ${newEntries} new, ${updatedEntries} updated, ${releasesFound} releases`);
|
|
175
|
+
return result;
|
|
176
|
+
}
|
|
177
|
+
// ── Watched Repos ──────────────────────────────────────
|
|
178
|
+
addWatchedRepo(fullName, reason = '') {
|
|
179
|
+
const stmt = this.db.prepare(`
|
|
180
|
+
INSERT OR IGNORE INTO techradar_watched_repos (full_name, url, reason)
|
|
181
|
+
VALUES (?, ?, ?)
|
|
182
|
+
`);
|
|
183
|
+
stmt.run(fullName, `https://github.com/${fullName}`, reason);
|
|
184
|
+
}
|
|
185
|
+
removeWatchedRepo(fullName) {
|
|
186
|
+
this.db.prepare('UPDATE techradar_watched_repos SET is_active = 0 WHERE full_name = ?').run(fullName);
|
|
187
|
+
}
|
|
188
|
+
getWatchedRepos() {
|
|
189
|
+
return this.db.prepare('SELECT * FROM techradar_watched_repos WHERE is_active = 1').all();
|
|
190
|
+
}
|
|
191
|
+
ensureDefaultWatchedRepos() {
|
|
192
|
+
for (const repo of this.config.watchedRepos) {
|
|
193
|
+
this.addWatchedRepo(repo, 'default watchlist');
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async checkWatchedRepos() {
|
|
197
|
+
const repos = this.getWatchedRepos();
|
|
198
|
+
let count = 0;
|
|
199
|
+
for (const repo of repos) {
|
|
200
|
+
const releases = await this.repoWatcher.checkReleases(repo);
|
|
201
|
+
if (releases.length > 0) {
|
|
202
|
+
const latest = releases[0];
|
|
203
|
+
// Score relevance
|
|
204
|
+
const relevance = await this.relevanceScorer.score(`${repo.full_name} ${latest.tag}`, `New release: ${latest.name}. ${latest.body.substring(0, 500)}`, 'github_release');
|
|
205
|
+
// Create/update radar entry
|
|
206
|
+
this.upsertEntry({
|
|
207
|
+
name: `${repo.full_name}@${latest.tag}`,
|
|
208
|
+
source: 'github_release',
|
|
209
|
+
source_url: latest.url,
|
|
210
|
+
category: relevance.category,
|
|
211
|
+
ring: relevance.ring,
|
|
212
|
+
description: `Release ${latest.tag}: ${latest.name}. ${latest.body.substring(0, 300)}`,
|
|
213
|
+
relevance_score: relevance.score,
|
|
214
|
+
relevance_reason: relevance.reason,
|
|
215
|
+
action_type: relevance.action,
|
|
216
|
+
action_detail: relevance.actionDetail,
|
|
217
|
+
first_seen_at: new Date().toISOString(),
|
|
218
|
+
last_seen_at: new Date().toISOString(),
|
|
219
|
+
is_active: true,
|
|
220
|
+
});
|
|
221
|
+
// Update watched repo
|
|
222
|
+
this.db.prepare(`
|
|
223
|
+
UPDATE techradar_watched_repos
|
|
224
|
+
SET last_release_tag = ?, last_release_at = ?, last_checked_at = datetime('now')
|
|
225
|
+
WHERE full_name = ?
|
|
226
|
+
`).run(latest.tag, latest.published_at, repo.full_name);
|
|
227
|
+
count += releases.length;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
// Update last checked
|
|
231
|
+
this.db.prepare(`
|
|
232
|
+
UPDATE techradar_watched_repos SET last_checked_at = datetime('now') WHERE full_name = ?
|
|
233
|
+
`).run(repo.full_name);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return count;
|
|
237
|
+
}
|
|
238
|
+
// ── SignalScanner Integration ──────────────────────────
|
|
239
|
+
/**
|
|
240
|
+
* Import high-scoring repos from existing SignalScanner data.
|
|
241
|
+
* Only imports breakout and signal-level repos.
|
|
242
|
+
*/
|
|
243
|
+
importFromSignalScanner() {
|
|
244
|
+
let newCount = 0;
|
|
245
|
+
let updatedCount = 0;
|
|
246
|
+
try {
|
|
247
|
+
// Check if scanned_repos table exists
|
|
248
|
+
const tableExists = this.db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='scanned_repos'").get();
|
|
249
|
+
if (!tableExists)
|
|
250
|
+
return { new: 0, updated: 0 };
|
|
251
|
+
const repos = this.db.prepare(`
|
|
252
|
+
SELECT full_name, url, description, language, topics, current_stars,
|
|
253
|
+
signal_score, signal_level, star_velocity_24h
|
|
254
|
+
FROM scanned_repos
|
|
255
|
+
WHERE signal_level IN ('breakout', 'signal')
|
|
256
|
+
AND is_active = 1
|
|
257
|
+
ORDER BY signal_score DESC
|
|
258
|
+
LIMIT ?
|
|
259
|
+
`).all(this.config.maxEntriesPerScan);
|
|
260
|
+
for (const repo of repos) {
|
|
261
|
+
const existing = this.db.prepare('SELECT id FROM techradar_entries WHERE name = ? AND source = ?').get(repo.full_name, 'github_trending');
|
|
262
|
+
// Score relevance
|
|
263
|
+
const relevance = this.relevanceScorer.scoreKeywords(repo.full_name, `${repo.description ?? ''} ${repo.language ?? ''} ${repo.topics ?? ''}`);
|
|
264
|
+
if (relevance.score < this.config.relevanceThreshold)
|
|
265
|
+
continue;
|
|
266
|
+
if (existing) {
|
|
267
|
+
this.db.prepare(`
|
|
268
|
+
UPDATE techradar_entries
|
|
269
|
+
SET relevance_score = ?, last_seen_at = datetime('now'), description = ?
|
|
270
|
+
WHERE name = ? AND source = ?
|
|
271
|
+
`).run(relevance.score, `${repo.description ?? ''}. Stars: ${repo.current_stars}, velocity: ${repo.star_velocity_24h}/day`, repo.full_name, 'github_trending');
|
|
272
|
+
updatedCount++;
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
this.upsertEntry({
|
|
276
|
+
name: repo.full_name,
|
|
277
|
+
source: 'github_trending',
|
|
278
|
+
source_url: repo.url,
|
|
279
|
+
category: relevance.category,
|
|
280
|
+
ring: relevance.ring,
|
|
281
|
+
description: `${repo.description ?? ''}. Stars: ${repo.current_stars}, velocity: ${repo.star_velocity_24h}/day`,
|
|
282
|
+
relevance_score: relevance.score,
|
|
283
|
+
relevance_reason: relevance.reason,
|
|
284
|
+
action_type: relevance.action,
|
|
285
|
+
action_detail: relevance.actionDetail,
|
|
286
|
+
first_seen_at: new Date().toISOString(),
|
|
287
|
+
last_seen_at: new Date().toISOString(),
|
|
288
|
+
is_active: true,
|
|
289
|
+
});
|
|
290
|
+
newCount++;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
catch (err) {
|
|
295
|
+
log.warn(`[TechRadar] SignalScanner import error: ${err.message}`);
|
|
296
|
+
}
|
|
297
|
+
return { new: newCount, updated: updatedCount };
|
|
298
|
+
}
|
|
299
|
+
// ── Entries ────────────────────────────────────────────
|
|
300
|
+
upsertEntry(entry) {
|
|
301
|
+
const existing = this.db.prepare('SELECT id FROM techradar_entries WHERE name = ? AND source = ?').get(entry.name, entry.source);
|
|
302
|
+
if (existing) {
|
|
303
|
+
this.db.prepare(`
|
|
304
|
+
UPDATE techradar_entries
|
|
305
|
+
SET relevance_score = ?, relevance_reason = ?, ring = ?,
|
|
306
|
+
action_type = ?, action_detail = ?, last_seen_at = datetime('now'),
|
|
307
|
+
description = ?, category = ?
|
|
308
|
+
WHERE id = ?
|
|
309
|
+
`).run(entry.relevance_score, entry.relevance_reason, entry.ring, entry.action_type, entry.action_detail, entry.description, entry.category, existing.id);
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
this.db.prepare(`
|
|
313
|
+
INSERT INTO techradar_entries
|
|
314
|
+
(name, source, source_url, category, ring, description,
|
|
315
|
+
relevance_score, relevance_reason, action_type, action_detail)
|
|
316
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
317
|
+
`).run(entry.name, entry.source, entry.source_url, entry.category, entry.ring, entry.description, entry.relevance_score, entry.relevance_reason, entry.action_type, entry.action_detail);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
getEntries(options = {}) {
|
|
321
|
+
let sql = 'SELECT * FROM techradar_entries WHERE is_active = 1';
|
|
322
|
+
const params = [];
|
|
323
|
+
if (options.minScore !== undefined) {
|
|
324
|
+
sql += ' AND relevance_score >= ?';
|
|
325
|
+
params.push(options.minScore);
|
|
326
|
+
}
|
|
327
|
+
if (options.source) {
|
|
328
|
+
sql += ' AND source = ?';
|
|
329
|
+
params.push(options.source);
|
|
330
|
+
}
|
|
331
|
+
if (options.ring) {
|
|
332
|
+
sql += ' AND ring = ?';
|
|
333
|
+
params.push(options.ring);
|
|
334
|
+
}
|
|
335
|
+
sql += ' ORDER BY relevance_score DESC LIMIT ?';
|
|
336
|
+
params.push(options.limit ?? 50);
|
|
337
|
+
return this.db.prepare(sql).all(...params);
|
|
338
|
+
}
|
|
339
|
+
// ── Digest ─────────────────────────────────────────────
|
|
340
|
+
async generateDigest(date) {
|
|
341
|
+
const today = date ?? new Date().toISOString().split('T')[0];
|
|
342
|
+
const entries = this.getEntries({ minScore: this.config.relevanceThreshold });
|
|
343
|
+
const digest = await this.digestGenerator.generate(entries, today);
|
|
344
|
+
// Store digest
|
|
345
|
+
this.db.prepare(`
|
|
346
|
+
INSERT OR REPLACE INTO techradar_digests
|
|
347
|
+
(date, summary, entries_json, opportunities_json, action_items_json)
|
|
348
|
+
VALUES (?, ?, ?, ?, ?)
|
|
349
|
+
`).run(today, digest.summary, JSON.stringify(digest.entries), JSON.stringify(digest.opportunities), JSON.stringify(digest.action_items));
|
|
350
|
+
return digest;
|
|
351
|
+
}
|
|
352
|
+
getDigest(date) {
|
|
353
|
+
const today = date ?? new Date().toISOString().split('T')[0];
|
|
354
|
+
const row = this.db.prepare('SELECT * FROM techradar_digests WHERE date = ?').get(today);
|
|
355
|
+
if (!row)
|
|
356
|
+
return null;
|
|
357
|
+
return {
|
|
358
|
+
date: row.date,
|
|
359
|
+
summary: row.summary,
|
|
360
|
+
entries: JSON.parse(row.entries_json),
|
|
361
|
+
opportunities: JSON.parse(row.opportunities_json),
|
|
362
|
+
action_items: JSON.parse(row.action_items_json),
|
|
363
|
+
created_at: row.created_at,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
// ── Stats ──────────────────────────────────────────────
|
|
367
|
+
getStats() {
|
|
368
|
+
const total = this.db.prepare('SELECT COUNT(*) as c FROM techradar_entries WHERE is_active = 1').get().c;
|
|
369
|
+
const bySrc = this.db.prepare('SELECT source, COUNT(*) as c FROM techradar_entries WHERE is_active = 1 GROUP BY source').all();
|
|
370
|
+
const byRing = this.db.prepare('SELECT ring, COUNT(*) as c FROM techradar_entries WHERE is_active = 1 GROUP BY ring').all();
|
|
371
|
+
const watched = this.db.prepare('SELECT COUNT(*) as c FROM techradar_watched_repos WHERE is_active = 1').get().c;
|
|
372
|
+
const lastDigest = this.db.prepare('SELECT date FROM techradar_digests ORDER BY date DESC LIMIT 1').get();
|
|
373
|
+
return {
|
|
374
|
+
totalEntries: total,
|
|
375
|
+
bySource: Object.fromEntries(bySrc.map(r => [r.source, r.c])),
|
|
376
|
+
byRing: Object.fromEntries(byRing.map(r => [r.ring, r.c])),
|
|
377
|
+
watchedRepos: watched,
|
|
378
|
+
lastDigest: lastDigest?.date ?? null,
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
//# sourceMappingURL=techradar-engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"techradar-engine.js","sourceRoot":"","sources":["../../src/techradar/techradar-engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TechRadar Types — Täglicher Internet-Scan + Relevanz-Analyse
|
|
3
|
+
*/
|
|
4
|
+
export interface TechRadarEntry {
|
|
5
|
+
id?: number;
|
|
6
|
+
name: string;
|
|
7
|
+
source: TechRadarSource;
|
|
8
|
+
source_url: string;
|
|
9
|
+
category: TechRadarCategory;
|
|
10
|
+
ring: TechRadarRing;
|
|
11
|
+
description: string;
|
|
12
|
+
relevance_score: number;
|
|
13
|
+
relevance_reason: string;
|
|
14
|
+
action_type: TechRadarAction;
|
|
15
|
+
action_detail: string;
|
|
16
|
+
first_seen_at: string;
|
|
17
|
+
last_seen_at: string;
|
|
18
|
+
is_active: boolean;
|
|
19
|
+
}
|
|
20
|
+
export type TechRadarSource = 'github_release' | 'github_trending' | 'hackernews' | 'web' | 'changelog' | 'npm' | 'manual';
|
|
21
|
+
export type TechRadarCategory = 'framework' | 'library' | 'tool' | 'language' | 'platform' | 'technique' | 'ai_model' | 'crypto' | 'other';
|
|
22
|
+
export type TechRadarRing = 'adopt' | 'trial' | 'assess' | 'hold';
|
|
23
|
+
export type TechRadarAction = 'integrate' | 'update' | 'investigate' | 'monitor' | 'none';
|
|
24
|
+
export interface WatchedRepo {
|
|
25
|
+
id?: number;
|
|
26
|
+
full_name: string;
|
|
27
|
+
url: string;
|
|
28
|
+
reason: string;
|
|
29
|
+
last_release_tag: string | null;
|
|
30
|
+
last_release_at: string | null;
|
|
31
|
+
last_checked_at: string | null;
|
|
32
|
+
is_active: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface RepoRelease {
|
|
35
|
+
tag: string;
|
|
36
|
+
name: string;
|
|
37
|
+
body: string;
|
|
38
|
+
published_at: string;
|
|
39
|
+
url: string;
|
|
40
|
+
is_prerelease: boolean;
|
|
41
|
+
}
|
|
42
|
+
export interface DailyDigest {
|
|
43
|
+
id?: number;
|
|
44
|
+
date: string;
|
|
45
|
+
summary: string;
|
|
46
|
+
entries: DigestEntry[];
|
|
47
|
+
opportunities: DigestOpportunity[];
|
|
48
|
+
action_items: DigestActionItem[];
|
|
49
|
+
created_at: string;
|
|
50
|
+
}
|
|
51
|
+
export interface DigestEntry {
|
|
52
|
+
name: string;
|
|
53
|
+
source: TechRadarSource;
|
|
54
|
+
category: TechRadarCategory;
|
|
55
|
+
relevance_score: number;
|
|
56
|
+
summary: string;
|
|
57
|
+
}
|
|
58
|
+
export interface DigestOpportunity {
|
|
59
|
+
title: string;
|
|
60
|
+
description: string;
|
|
61
|
+
effort: 'low' | 'medium' | 'high';
|
|
62
|
+
impact: 'low' | 'medium' | 'high';
|
|
63
|
+
}
|
|
64
|
+
export interface DigestActionItem {
|
|
65
|
+
action: string;
|
|
66
|
+
priority: 'critical' | 'high' | 'medium' | 'low';
|
|
67
|
+
related_entry: string;
|
|
68
|
+
}
|
|
69
|
+
export interface TechRadarConfig {
|
|
70
|
+
enabled: boolean;
|
|
71
|
+
scanIntervalMs: number;
|
|
72
|
+
digestTime: string;
|
|
73
|
+
maxEntriesPerScan: number;
|
|
74
|
+
relevanceThreshold: number;
|
|
75
|
+
githubToken?: string;
|
|
76
|
+
watchedRepos: string[];
|
|
77
|
+
}
|
|
78
|
+
export interface TechRadarScanResult {
|
|
79
|
+
started_at: string;
|
|
80
|
+
finished_at: string;
|
|
81
|
+
duration_ms: number;
|
|
82
|
+
new_entries: number;
|
|
83
|
+
updated_entries: number;
|
|
84
|
+
releases_found: number;
|
|
85
|
+
digest_generated: boolean;
|
|
86
|
+
errors: string[];
|
|
87
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/techradar/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { WatchdogService, createDefaultWatchdogConfig } from '../watchdog-service.js';
|
|
3
|
+
// Mock child_process
|
|
4
|
+
vi.mock('node:child_process', () => ({
|
|
5
|
+
spawn: vi.fn().mockReturnValue({
|
|
6
|
+
pid: 12345,
|
|
7
|
+
unref: vi.fn(),
|
|
8
|
+
on: vi.fn(),
|
|
9
|
+
}),
|
|
10
|
+
}));
|
|
11
|
+
// Mock IpcClient
|
|
12
|
+
vi.mock('../../ipc/client.js', () => ({
|
|
13
|
+
IpcClient: vi.fn().mockImplementation(() => ({
|
|
14
|
+
connect: vi.fn().mockResolvedValue(undefined),
|
|
15
|
+
request: vi.fn().mockResolvedValue({ status: 'ok' }),
|
|
16
|
+
disconnect: vi.fn(),
|
|
17
|
+
})),
|
|
18
|
+
}));
|
|
19
|
+
function createTestConfig() {
|
|
20
|
+
return {
|
|
21
|
+
daemons: [
|
|
22
|
+
{
|
|
23
|
+
name: 'test-brain',
|
|
24
|
+
entryPoint: '/fake/path/index.js',
|
|
25
|
+
args: ['daemon'],
|
|
26
|
+
pidPath: '/tmp/test-brain.pid',
|
|
27
|
+
pipeName: '\\\\.\\pipe\\test-brain',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: 'test-trading',
|
|
31
|
+
entryPoint: '/fake/path/trading/index.js',
|
|
32
|
+
args: ['daemon'],
|
|
33
|
+
pidPath: '/tmp/test-trading.pid',
|
|
34
|
+
pipeName: '\\\\.\\pipe\\test-trading',
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
maxRestarts: 3,
|
|
38
|
+
restartWindowMs: 60_000,
|
|
39
|
+
baseBackoffMs: 100,
|
|
40
|
+
healthCheckIntervalMs: 1000,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
describe('WatchdogService', () => {
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
vi.clearAllMocks();
|
|
46
|
+
});
|
|
47
|
+
it('creates with configured daemons', () => {
|
|
48
|
+
const config = createTestConfig();
|
|
49
|
+
const watchdog = new WatchdogService(config);
|
|
50
|
+
const status = watchdog.getStatus();
|
|
51
|
+
expect(status).toHaveLength(2);
|
|
52
|
+
expect(status[0].name).toBe('test-brain');
|
|
53
|
+
expect(status[1].name).toBe('test-trading');
|
|
54
|
+
expect(status[0].running).toBe(false);
|
|
55
|
+
expect(status[0].restarts).toBe(0);
|
|
56
|
+
});
|
|
57
|
+
it('returns null for unknown daemon', () => {
|
|
58
|
+
const watchdog = new WatchdogService(createTestConfig());
|
|
59
|
+
expect(watchdog.getDaemonStatus('nonexistent')).toBeNull();
|
|
60
|
+
});
|
|
61
|
+
it('gets individual daemon status', () => {
|
|
62
|
+
const watchdog = new WatchdogService(createTestConfig());
|
|
63
|
+
const status = watchdog.getDaemonStatus('test-brain');
|
|
64
|
+
expect(status).not.toBeNull();
|
|
65
|
+
expect(status.name).toBe('test-brain');
|
|
66
|
+
expect(status.pid).toBeNull();
|
|
67
|
+
expect(status.healthy).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
it('restartDaemon returns false for unknown daemon', () => {
|
|
70
|
+
const watchdog = new WatchdogService(createTestConfig());
|
|
71
|
+
expect(watchdog.restartDaemon('nonexistent')).toBe(false);
|
|
72
|
+
});
|
|
73
|
+
it('stop is safe when not started', () => {
|
|
74
|
+
const watchdog = new WatchdogService(createTestConfig());
|
|
75
|
+
expect(() => watchdog.stop()).not.toThrow();
|
|
76
|
+
});
|
|
77
|
+
it('getStatus returns uptime as null when not started', () => {
|
|
78
|
+
const watchdog = new WatchdogService(createTestConfig());
|
|
79
|
+
const status = watchdog.getStatus();
|
|
80
|
+
for (const s of status) {
|
|
81
|
+
expect(s.uptime).toBeNull();
|
|
82
|
+
expect(s.lastCrash).toBeNull();
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
describe('createDefaultWatchdogConfig', () => {
|
|
87
|
+
it('creates config with 3 brain daemons', () => {
|
|
88
|
+
const config = createDefaultWatchdogConfig();
|
|
89
|
+
expect(config.daemons).toHaveLength(3);
|
|
90
|
+
expect(config.daemons.map(d => d.name)).toEqual([
|
|
91
|
+
'brain',
|
|
92
|
+
'trading-brain',
|
|
93
|
+
'marketing-brain',
|
|
94
|
+
]);
|
|
95
|
+
});
|
|
96
|
+
it('each daemon has required fields', () => {
|
|
97
|
+
const config = createDefaultWatchdogConfig();
|
|
98
|
+
for (const d of config.daemons) {
|
|
99
|
+
expect(d.name).toBeTruthy();
|
|
100
|
+
expect(d.entryPoint).toBeTruthy();
|
|
101
|
+
expect(d.args).toContain('daemon');
|
|
102
|
+
expect(d.pidPath).toBeTruthy();
|
|
103
|
+
expect(d.pipeName).toBeTruthy();
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
it('pipe names contain daemon name', () => {
|
|
107
|
+
const config = createDefaultWatchdogConfig();
|
|
108
|
+
expect(config.daemons[0].pipeName).toContain('brain');
|
|
109
|
+
expect(config.daemons[1].pipeName).toContain('trading');
|
|
110
|
+
expect(config.daemons[2].pipeName).toContain('marketing');
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
//# sourceMappingURL=watchdog-service.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watchdog-service.test.js","sourceRoot":"","sources":["../../../src/watchdog/__tests__/watchdog-service.test.ts"],"names":[],"mappings":"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"}
|