@devilsdev/rag-pipeline-utils 2.1.11 → 2.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/.ragrc.schema.json +218 -0
- package/README.md +651 -393
- package/bin/cli.js +8 -14
- package/dist/index.d.ts +237 -0
- package/dist/index.mjs +22 -7
- package/package.json +17 -13
- package/src/ai/adaptive-retrieval.js +191 -0
- package/src/ai/federated-learning.js +214 -0
- package/src/ai/federation/federated-learning-coordinator.js +345 -0
- package/src/ai/federation.js +76 -0
- package/src/ai/index-fixed.js +509 -0
- package/src/ai/index.js +33 -0
- package/src/ai/model-trainer.js +217 -0
- package/src/ai/model-training.js +205 -0
- package/src/ai/module-template.js +37 -0
- package/src/ai/multimodal/multi-modal-processor.js +345 -0
- package/src/ai/multimodal-processing.js +268 -0
- package/src/ai/multimodal.js +71 -0
- package/src/ai/orchestrator.js +70 -0
- package/src/ai/retrieval/adaptive-retrieval-engine.js +398 -0
- package/src/ai/retrieval-engine.js +69 -0
- package/src/ai/training/model-training-orchestrator.js +228 -0
- package/src/ci/workflow-analyzer.js +71 -0
- package/src/cli/commands/ai-ml.js +967 -0
- package/src/cli/commands/docs.js +228 -0
- package/src/cli/commands/dx.js +538 -0
- package/src/cli/commands/plugin-hub.js +875 -0
- package/src/cli/doctor-command.js +639 -0
- package/src/cli/enhanced-cli-commands.js +945 -0
- package/src/cli/interactive-wizard.js +997 -0
- package/src/cli/plugin-marketplace-commands.js +869 -0
- package/src/cli.js +17 -0
- package/src/config/enhanced-ragrc-schema.js +632 -0
- package/src/config/load-config.js +54 -0
- package/src/config/load-plugin-config.js +69 -0
- package/src/config/normalize-config.js +105 -0
- package/src/config/schema-validator.js +315 -0
- package/src/config/validate-plugin-schema.js +35 -0
- package/src/config/validate-schema.js +113 -0
- package/src/contracts/plugin-contracts.js +33 -0
- package/src/core/create-pipeline.js +99 -0
- package/src/core/dependency-injection.js +242 -0
- package/src/core/observability/event-logger.js +241 -0
- package/src/core/observability/instrumented-pipeline.js +609 -0
- package/src/core/observability/metrics.js +497 -0
- package/src/core/observability/tracing.js +781 -0
- package/src/core/performance/benchmark.js +516 -0
- package/src/core/performance/parallel-processor.js +240 -0
- package/src/core/performance/streaming-safeguards.js +611 -0
- package/src/core/pipeline-factory.js +60 -0
- package/src/core/plugin-contracts.js +89 -0
- package/src/core/plugin-marketplace/plugin-metadata.js +485 -0
- package/src/core/plugin-marketplace/plugin-publisher.js +574 -0
- package/src/core/plugin-marketplace/plugin-registry-format.js +419 -0
- package/src/core/plugin-marketplace/version-resolver.js +61 -0
- package/src/core/plugin-registry.js +265 -0
- package/src/dag/dag-engine.js +954 -0
- package/src/dag/dag-node.js +76 -0
- package/src/dag/enhanced-dag-engine.js +822 -0
- package/src/dx/index.js +40 -0
- package/src/dx/integration-templates.js +665 -0
- package/src/dx/performance-profiler.js +968 -0
- package/src/dx/realtime-debugger.js +755 -0
- package/src/dx/visual-pipeline-builder.js +578 -0
- package/src/ecosystem/plugin-analytics-dashboard.js +778 -0
- package/src/ecosystem/plugin-certification.js +580 -0
- package/src/ecosystem/plugin-hub.js +821 -0
- package/src/enterprise/audit-logging.js +967 -0
- package/src/enterprise/data-governance.js +408 -0
- package/src/enterprise/multi-tenancy.js +496 -0
- package/src/enterprise/sso-integration.js +903 -0
- package/src/evaluate/evaluator.js +52 -0
- package/src/evaluate/scoring.js +91 -0
- package/src/index.js +67 -0
- package/src/ingest.js +59 -0
- package/src/loader/csv-loader.js +38 -0
- package/src/loader/directory-loader.js +48 -0
- package/src/loader/html-loader.js +62 -0
- package/src/loader/markdown-loader.js +67 -0
- package/src/mocks/openai-embedder.js +119 -0
- package/src/mocks/openai-llm.js +77 -0
- package/src/mocks/pdf-loader.js +21 -0
- package/src/mocks/pinecone-retriever.js +106 -0
- package/src/observability/audit-logger.js +758 -0
- package/src/observability/dashboard-config.json +675 -0
- package/src/observability/event-logger.js +310 -0
- package/src/observability/metrics.js +691 -0
- package/src/observability/security-monitor.js +830 -0
- package/src/observability/slo-monitor.js +319 -0
- package/src/observability/telemetry.js +686 -0
- package/src/pipeline/pipeline.js +18 -0
- package/src/plugins/sample-embedder.js +53 -0
- package/src/plugins/sample-llm.js +30 -0
- package/src/plugins/sample-loader.js +24 -0
- package/src/plugins/sample-retriever.js +84 -0
- package/src/query.js +117 -0
- package/src/reranker/llm-reranker.js +85 -0
- package/src/scripts/script-utilities.js +36 -0
- package/src/security/plugin-sandbox.js +586 -0
- package/src/security/plugin-signature-verifier.js +414 -0
- package/src/security/validator.js +36 -0
- package/src/utils/ci/diagnostic-reporter.js +57 -0
- package/src/utils/logger.js +211 -0
- package/src/utils/performance-monitor.js +561 -0
- package/src/utils/plugin-scaffolder.js +447 -0
- package/src/utils/retry-policy.js +443 -0
- package/src/utils/retry.js +107 -0
- package/src/utils/safe-expression-evaluator.js +579 -0
- package/src/utils/structured-logger.js +361 -0
- package/src/utils/validate-plugin-contract.js +42 -0
|
@@ -0,0 +1,778 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Analytics Dashboard
|
|
3
|
+
* Real-time analytics and metrics visualization for plugin ecosystem
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs').promises;
|
|
7
|
+
// eslint-disable-line global-require
|
|
8
|
+
const path = require('path');
|
|
9
|
+
// eslint-disable-line global-require
|
|
10
|
+
const { EventEmitter } = require('events');
|
|
11
|
+
// eslint-disable-line global-require
|
|
12
|
+
|
|
13
|
+
class PluginAnalyticsDashboard extends EventEmitter {
|
|
14
|
+
constructor(_options = {}) {
|
|
15
|
+
super();
|
|
16
|
+
|
|
17
|
+
this._config = {
|
|
18
|
+
port: _options.port || 3333,
|
|
19
|
+
dataDir: _options.dataDir || path.join(process.cwd(), '.rag-analytics'),
|
|
20
|
+
refreshInterval: _options.refreshInterval || 30000, // 30 seconds
|
|
21
|
+
retentionDays: _options.retentionDays || 90,
|
|
22
|
+
..._options,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
this.metrics = new Map();
|
|
26
|
+
this.realTimeData = new Map();
|
|
27
|
+
this.subscribers = new Set();
|
|
28
|
+
|
|
29
|
+
this.initializeMetrics();
|
|
30
|
+
this.startDataCollection();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Initialize metric collectors
|
|
35
|
+
*/
|
|
36
|
+
initializeMetrics() {
|
|
37
|
+
// Plugin usage metrics
|
|
38
|
+
this.metrics.set('plugin_installs', {
|
|
39
|
+
type: 'counter',
|
|
40
|
+
description: 'Total plugin installations',
|
|
41
|
+
value: 0,
|
|
42
|
+
history: [],
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
this.metrics.set('plugin_searches', {
|
|
46
|
+
type: 'counter',
|
|
47
|
+
description: 'Total plugin searches',
|
|
48
|
+
value: 0,
|
|
49
|
+
history: [],
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
this.metrics.set('plugin_ratings', {
|
|
53
|
+
type: 'histogram',
|
|
54
|
+
description: 'Plugin rating distribution',
|
|
55
|
+
buckets: { 1: 0, 2: 0, 3: 0, 4: 0, 5: 0 },
|
|
56
|
+
history: [],
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
this.metrics.set('active_plugins', {
|
|
60
|
+
type: 'gauge',
|
|
61
|
+
description: 'Currently active plugins',
|
|
62
|
+
value: 0,
|
|
63
|
+
history: [],
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Performance metrics
|
|
67
|
+
this.metrics.set('installation_time', {
|
|
68
|
+
type: 'histogram',
|
|
69
|
+
description: 'Plugin installation time (ms)',
|
|
70
|
+
buckets: {},
|
|
71
|
+
history: [],
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
this.metrics.set('search_latency', {
|
|
75
|
+
type: 'histogram',
|
|
76
|
+
description: 'Search response time (ms)',
|
|
77
|
+
buckets: {},
|
|
78
|
+
history: [],
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Security metrics
|
|
82
|
+
this.metrics.set('security_scans', {
|
|
83
|
+
type: 'counter',
|
|
84
|
+
description: 'Security scans performed',
|
|
85
|
+
value: 0,
|
|
86
|
+
history: [],
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
this.metrics.set('security_issues', {
|
|
90
|
+
type: 'counter',
|
|
91
|
+
description: 'Security issues detected',
|
|
92
|
+
value: 0,
|
|
93
|
+
history: [],
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Certification metrics
|
|
97
|
+
this.metrics.set('certification_requests', {
|
|
98
|
+
type: 'counter',
|
|
99
|
+
description: 'Certification requests submitted',
|
|
100
|
+
value: 0,
|
|
101
|
+
history: [],
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
this.metrics.set('certified_plugins', {
|
|
105
|
+
type: 'gauge',
|
|
106
|
+
description: 'Total certified plugins',
|
|
107
|
+
value: 0,
|
|
108
|
+
history: [],
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Start collecting analytics data
|
|
114
|
+
*/
|
|
115
|
+
startDataCollection() {
|
|
116
|
+
// Periodic data collection
|
|
117
|
+
setInterval(() => {
|
|
118
|
+
this.collectMetrics();
|
|
119
|
+
this.updateRealTimeData();
|
|
120
|
+
this.notifySubscribers();
|
|
121
|
+
}, this._config.refreshInterval);
|
|
122
|
+
|
|
123
|
+
// Daily cleanup
|
|
124
|
+
setInterval(
|
|
125
|
+
() => {
|
|
126
|
+
this.cleanupOldData();
|
|
127
|
+
},
|
|
128
|
+
24 * 60 * 60 * 1000,
|
|
129
|
+
); // 24 hours
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Record plugin installation
|
|
134
|
+
*/
|
|
135
|
+
recordInstallation(pluginId, version, metadata = {}) {
|
|
136
|
+
const timestamp = Date.now();
|
|
137
|
+
|
|
138
|
+
this.incrementMetric('plugin_installs');
|
|
139
|
+
this.recordHistogram('installation_time', metadata.installTime || 0);
|
|
140
|
+
|
|
141
|
+
this.realTimeData.set(`install_${timestamp}`, {
|
|
142
|
+
type: 'installation',
|
|
143
|
+
pluginId,
|
|
144
|
+
version,
|
|
145
|
+
timestamp,
|
|
146
|
+
metadata,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
this.emit('installation_recorded', { pluginId, version, metadata });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Record plugin search
|
|
154
|
+
*/
|
|
155
|
+
recordSearch(query, results, latency) {
|
|
156
|
+
const timestamp = Date.now();
|
|
157
|
+
|
|
158
|
+
this.incrementMetric('plugin_searches');
|
|
159
|
+
this.recordHistogram('search_latency', latency);
|
|
160
|
+
|
|
161
|
+
this.realTimeData.set(`search_${timestamp}`, {
|
|
162
|
+
type: 'search',
|
|
163
|
+
query,
|
|
164
|
+
resultCount: results.length,
|
|
165
|
+
latency,
|
|
166
|
+
timestamp,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
this.emit('search_recorded', { query, results, latency });
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Record plugin rating
|
|
174
|
+
*/
|
|
175
|
+
recordRating(pluginId, rating, review = null) {
|
|
176
|
+
const timestamp = Date.now();
|
|
177
|
+
|
|
178
|
+
const ratingMetric = this.metrics.get('plugin_ratings');
|
|
179
|
+
if (ratingMetric && ratingMetric.buckets[rating] !== undefined) {
|
|
180
|
+
ratingMetric.buckets[rating]++;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
this.realTimeData.set(`rating_${timestamp}`, {
|
|
184
|
+
type: 'rating',
|
|
185
|
+
pluginId,
|
|
186
|
+
rating,
|
|
187
|
+
review,
|
|
188
|
+
timestamp,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
this.emit('rating_recorded', { pluginId, rating, review });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Record security scan
|
|
196
|
+
*/
|
|
197
|
+
recordSecurityScan(pluginId, result) {
|
|
198
|
+
const timestamp = Date.now();
|
|
199
|
+
|
|
200
|
+
this.incrementMetric('security_scans');
|
|
201
|
+
|
|
202
|
+
if (result.issues && result.issues.length > 0) {
|
|
203
|
+
this.incrementMetric('security_issues', result.issues.length);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
this.realTimeData.set(`security_${timestamp}`, {
|
|
207
|
+
type: 'security_scan',
|
|
208
|
+
pluginId,
|
|
209
|
+
risk: result.risk,
|
|
210
|
+
issueCount: result.issues?.length || 0,
|
|
211
|
+
timestamp,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
this.emit('security_scan_recorded', { pluginId, result });
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Record certification request
|
|
219
|
+
*/
|
|
220
|
+
recordCertification(pluginId, level, result) {
|
|
221
|
+
const timestamp = Date.now();
|
|
222
|
+
|
|
223
|
+
this.incrementMetric('certification_requests');
|
|
224
|
+
|
|
225
|
+
if (result.approved) {
|
|
226
|
+
this.incrementMetric('certified_plugins');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
this.realTimeData.set(`cert_${timestamp}`, {
|
|
230
|
+
type: 'certification',
|
|
231
|
+
pluginId,
|
|
232
|
+
level,
|
|
233
|
+
approved: result.approved,
|
|
234
|
+
score: result.score,
|
|
235
|
+
timestamp,
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
this.emit('certification_recorded', { pluginId, level, result });
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Get dashboard data
|
|
243
|
+
*/
|
|
244
|
+
getDashboardData() {
|
|
245
|
+
const now = Date.now();
|
|
246
|
+
const oneHourAgo = now - 60 * 60 * 1000;
|
|
247
|
+
|
|
248
|
+
// Recent activity
|
|
249
|
+
const recentActivity = Array.from(this.realTimeData.values())
|
|
250
|
+
.filter((item) => item.timestamp > oneHourAgo)
|
|
251
|
+
.sort((a, b) => b.timestamp - a.timestamp)
|
|
252
|
+
.slice(0, 50);
|
|
253
|
+
|
|
254
|
+
// Time series data
|
|
255
|
+
const timeSeriesData = this.generateTimeSeriesData();
|
|
256
|
+
|
|
257
|
+
// Top plugins
|
|
258
|
+
const topPlugins = this.getTopPlugins();
|
|
259
|
+
|
|
260
|
+
// Performance summary
|
|
261
|
+
const performanceSummary = this.getPerformanceSummary();
|
|
262
|
+
|
|
263
|
+
// Security overview
|
|
264
|
+
const securityOverview = this.getSecurityOverview();
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
timestamp: now,
|
|
268
|
+
metrics: Object.fromEntries(this.metrics),
|
|
269
|
+
recentActivity,
|
|
270
|
+
timeSeriesData,
|
|
271
|
+
topPlugins,
|
|
272
|
+
performanceSummary,
|
|
273
|
+
securityOverview,
|
|
274
|
+
summary: {
|
|
275
|
+
totalInstalls: this.metrics.get('plugin_installs')?.value || 0,
|
|
276
|
+
totalSearches: this.metrics.get('plugin_searches')?.value || 0,
|
|
277
|
+
activePlugins: this.metrics.get('active_plugins')?.value || 0,
|
|
278
|
+
certifiedPlugins: this.metrics.get('certified_plugins')?.value || 0,
|
|
279
|
+
avgRating: this.calculateAverageRating(),
|
|
280
|
+
securityScore: this.calculateSecurityScore(),
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Generate time series data for charts
|
|
287
|
+
*/
|
|
288
|
+
generateTimeSeriesData() {
|
|
289
|
+
const now = Date.now();
|
|
290
|
+
const oneWeekAgo = now - 7 * 24 * 60 * 60 * 1000;
|
|
291
|
+
const interval = 60 * 60 * 1000; // 1 hour intervals
|
|
292
|
+
|
|
293
|
+
const series = {
|
|
294
|
+
installs: [],
|
|
295
|
+
searches: [],
|
|
296
|
+
ratings: [],
|
|
297
|
+
securityScans: [],
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
for (let time = oneWeekAgo; time <= now; time += interval) {
|
|
301
|
+
const hourData = Array.from(this.realTimeData.values()).filter(
|
|
302
|
+
(item) => item.timestamp >= time && item.timestamp < time + interval,
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
series.installs.push({
|
|
306
|
+
timestamp: time,
|
|
307
|
+
value: hourData.filter((item) => item.type === 'installation').length,
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
series.searches.push({
|
|
311
|
+
timestamp: time,
|
|
312
|
+
value: hourData.filter((item) => item.type === 'search').length,
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
series.ratings.push({
|
|
316
|
+
timestamp: time,
|
|
317
|
+
value: hourData.filter((item) => item.type === 'rating').length,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
series.securityScans.push({
|
|
321
|
+
timestamp: time,
|
|
322
|
+
value: hourData.filter((item) => item.type === 'security_scan').length,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return series;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Get top performing plugins
|
|
331
|
+
*/
|
|
332
|
+
getTopPlugins() {
|
|
333
|
+
const pluginStats = new Map();
|
|
334
|
+
|
|
335
|
+
Array.from(this.realTimeData.values()).forEach((item) => {
|
|
336
|
+
if (item.pluginId) {
|
|
337
|
+
if (!pluginStats.has(item.pluginId)) {
|
|
338
|
+
pluginStats.set(item.pluginId, {
|
|
339
|
+
pluginId: item.pluginId,
|
|
340
|
+
installs: 0,
|
|
341
|
+
ratings: [],
|
|
342
|
+
securityScans: 0,
|
|
343
|
+
certifications: 0,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const stats = pluginStats.get(item.pluginId);
|
|
348
|
+
|
|
349
|
+
switch (item.type) {
|
|
350
|
+
case 'installation':
|
|
351
|
+
stats.installs++;
|
|
352
|
+
break;
|
|
353
|
+
case 'rating':
|
|
354
|
+
stats.ratings.push(item.rating);
|
|
355
|
+
break;
|
|
356
|
+
case 'security_scan':
|
|
357
|
+
stats.securityScans++;
|
|
358
|
+
break;
|
|
359
|
+
case 'certification':
|
|
360
|
+
if (item.approved) stats.certifications++;
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
return Array.from(pluginStats.values())
|
|
367
|
+
.map((stats) => ({
|
|
368
|
+
...stats,
|
|
369
|
+
avgRating:
|
|
370
|
+
stats.ratings.length > 0
|
|
371
|
+
? stats.ratings.reduce((a, b) => a + b, 0) / stats.ratings.length
|
|
372
|
+
: 0,
|
|
373
|
+
}))
|
|
374
|
+
.sort((a, b) => b.installs - a.installs)
|
|
375
|
+
.slice(0, 10);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Get performance summary
|
|
380
|
+
*/
|
|
381
|
+
getPerformanceSummary() {
|
|
382
|
+
const installTimes = Array.from(this.realTimeData.values())
|
|
383
|
+
.filter(
|
|
384
|
+
(item) => item.type === 'installation' && item.metadata?.installTime,
|
|
385
|
+
)
|
|
386
|
+
.map((item) => item.metadata.installTime);
|
|
387
|
+
|
|
388
|
+
const searchLatencies = Array.from(this.realTimeData.values())
|
|
389
|
+
.filter((item) => item.type === 'search' && item.latency)
|
|
390
|
+
.map((item) => item.latency);
|
|
391
|
+
|
|
392
|
+
return {
|
|
393
|
+
avgInstallTime:
|
|
394
|
+
installTimes.length > 0
|
|
395
|
+
? installTimes.reduce((a, b) => a + b, 0) / installTimes.length
|
|
396
|
+
: 0,
|
|
397
|
+
p95InstallTime: this.calculatePercentile(installTimes, 95),
|
|
398
|
+
avgSearchLatency:
|
|
399
|
+
searchLatencies.length > 0
|
|
400
|
+
? searchLatencies.reduce((a, b) => a + b, 0) / searchLatencies.length
|
|
401
|
+
: 0,
|
|
402
|
+
p95SearchLatency: this.calculatePercentile(searchLatencies, 95),
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Get security overview
|
|
408
|
+
*/
|
|
409
|
+
getSecurityOverview() {
|
|
410
|
+
const securityScans = Array.from(this.realTimeData.values()).filter(
|
|
411
|
+
(item) => item.type === 'security_scan',
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
const riskDistribution = { low: 0, medium: 0, high: 0 };
|
|
415
|
+
let totalIssues = 0;
|
|
416
|
+
|
|
417
|
+
securityScans.forEach((scan) => {
|
|
418
|
+
if (scan.risk) {
|
|
419
|
+
riskDistribution[scan.risk]++;
|
|
420
|
+
}
|
|
421
|
+
totalIssues += scan.issueCount || 0;
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
return {
|
|
425
|
+
totalScans: securityScans.length,
|
|
426
|
+
riskDistribution,
|
|
427
|
+
totalIssues,
|
|
428
|
+
avgIssuesPerScan:
|
|
429
|
+
securityScans.length > 0 ? totalIssues / securityScans.length : 0,
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Generate HTML dashboard
|
|
435
|
+
*/
|
|
436
|
+
async generateHTMLDashboard() {
|
|
437
|
+
const data = this.getDashboardData();
|
|
438
|
+
|
|
439
|
+
const html = `
|
|
440
|
+
<!DOCTYPE html>
|
|
441
|
+
<html lang="en">
|
|
442
|
+
<head>
|
|
443
|
+
${this._generateHTMLHead()}
|
|
444
|
+
</head>
|
|
445
|
+
<body>
|
|
446
|
+
${this._generateHTMLBody(data)}
|
|
447
|
+
${this._generateHTMLScripts(data)}
|
|
448
|
+
</body>
|
|
449
|
+
</html>`;
|
|
450
|
+
|
|
451
|
+
return html;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
_generateHTMLHead() {
|
|
455
|
+
return `
|
|
456
|
+
<meta charset="UTF-8">
|
|
457
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
458
|
+
<title>RAG Pipeline Plugin Analytics</title>
|
|
459
|
+
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
460
|
+
${this._generateHTMLStyles()}`;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
_generateHTMLStyles() {
|
|
464
|
+
return `
|
|
465
|
+
<style>
|
|
466
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
|
|
467
|
+
.dashboard { max-width: 1200px; margin: 0 auto; }
|
|
468
|
+
.header { background: white; padding: 20px; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
|
|
469
|
+
.metrics-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; margin-bottom: 20px; }
|
|
470
|
+
.metric-card { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
|
|
471
|
+
.metric-value { font-size: 2em; font-weight: bold; color: #2563eb; }
|
|
472
|
+
.metric-label { color: #6b7280; margin-top: 5px; }
|
|
473
|
+
.chart-container { background: white; padding: 20px; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
|
|
474
|
+
.activity-feed { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
|
|
475
|
+
.activity-item { padding: 10px 0; border-bottom: 1px solid #e5e7eb; }
|
|
476
|
+
.activity-time { color: #6b7280; font-size: 0.9em; }
|
|
477
|
+
.status-indicator { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 8px; }
|
|
478
|
+
.status-success { background: #10b981; }
|
|
479
|
+
.status-warning { background: #f59e0b; }
|
|
480
|
+
.status-error { background: #ef4444; }
|
|
481
|
+
</style>`;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
_generateHTMLBody(data) {
|
|
485
|
+
return `
|
|
486
|
+
<div class="dashboard">
|
|
487
|
+
${this._generateDashboardHeader(data)}
|
|
488
|
+
${this._generateMetricsGrid(data)}
|
|
489
|
+
${this._generateChartsSection()}
|
|
490
|
+
${this._generateActivityFeed(data)}
|
|
491
|
+
</div>`;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
_generateDashboardHeader(data) {
|
|
495
|
+
return `
|
|
496
|
+
<div class="header">
|
|
497
|
+
<h1>🔌 RAG Pipeline Plugin Analytics</h1>
|
|
498
|
+
<p>Real-time insights into plugin ecosystem performance and usage</p>
|
|
499
|
+
<p><strong>Last Updated:</strong> ${new Date(data.timestamp).toLocaleString()}</p>
|
|
500
|
+
</div>`;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
_generateMetricsGrid(data) {
|
|
504
|
+
const metrics = [
|
|
505
|
+
{
|
|
506
|
+
value: data.summary.totalInstalls.toLocaleString(),
|
|
507
|
+
label: 'Total Installations',
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
value: data.summary.totalSearches.toLocaleString(),
|
|
511
|
+
label: 'Total Searches',
|
|
512
|
+
},
|
|
513
|
+
{ value: data.summary.activePlugins, label: 'Active Plugins' },
|
|
514
|
+
{ value: data.summary.certifiedPlugins, label: 'Certified Plugins' },
|
|
515
|
+
{
|
|
516
|
+
value: `${data.summary.avgRating.toFixed(1)}★`,
|
|
517
|
+
label: 'Average Rating',
|
|
518
|
+
},
|
|
519
|
+
{ value: `${data.summary.securityScore}%`, label: 'Security Score' },
|
|
520
|
+
];
|
|
521
|
+
|
|
522
|
+
const metricCards = metrics
|
|
523
|
+
.map(
|
|
524
|
+
(metric) =>
|
|
525
|
+
`<div class="metric-card">
|
|
526
|
+
<div class="metric-value">${metric.value}</div>
|
|
527
|
+
<div class="metric-label">${metric.label}</div>
|
|
528
|
+
</div>`,
|
|
529
|
+
)
|
|
530
|
+
.join('');
|
|
531
|
+
|
|
532
|
+
return `<div class="metrics-grid">${metricCards}</div>`;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
_generateChartsSection() {
|
|
536
|
+
return `
|
|
537
|
+
<div class="chart-container">
|
|
538
|
+
<h3>Plugin Activity Over Time</h3>
|
|
539
|
+
<canvas id="activityChart" width="800" height="400"></canvas>
|
|
540
|
+
</div>
|
|
541
|
+
|
|
542
|
+
<div class="chart-container">
|
|
543
|
+
<h3>Top Plugins by Installations</h3>
|
|
544
|
+
<canvas id="topPluginsChart" width="800" height="400"></canvas>
|
|
545
|
+
</div>`;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
_generateActivityFeed(data) {
|
|
549
|
+
const activityItems = data.recentActivity
|
|
550
|
+
.map(
|
|
551
|
+
(activity) => `
|
|
552
|
+
<div class="activity-item">
|
|
553
|
+
<span class="status-indicator status-${this.getActivityStatus(activity)}"></span>
|
|
554
|
+
<strong>${activity.type.replace('_', ' ').toUpperCase()}</strong>
|
|
555
|
+
${this.formatActivityDescription(activity)}
|
|
556
|
+
<div class="activity-time">${new Date(activity.timestamp).toLocaleString()}</div>
|
|
557
|
+
</div>
|
|
558
|
+
`,
|
|
559
|
+
)
|
|
560
|
+
.join('');
|
|
561
|
+
|
|
562
|
+
return `
|
|
563
|
+
<div class="activity-feed">
|
|
564
|
+
<h3>Recent Activity</h3>
|
|
565
|
+
${activityItems}
|
|
566
|
+
</div>`;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
_generateHTMLScripts(data) {
|
|
570
|
+
return `
|
|
571
|
+
<script>
|
|
572
|
+
${this._generateActivityChartScript(data)}
|
|
573
|
+
${this._generateTopPluginsChartScript(data)}
|
|
574
|
+
|
|
575
|
+
// Auto-refresh every 30 seconds
|
|
576
|
+
setTimeout(() => location.reload(), 30000);
|
|
577
|
+
</script>`;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
_generateActivityChartScript(data) {
|
|
581
|
+
return `
|
|
582
|
+
// Activity Chart
|
|
583
|
+
const activityCtx = document.getElementById('activityChart').getContext('2d');
|
|
584
|
+
new Chart(activityCtx, {
|
|
585
|
+
type: 'line',
|
|
586
|
+
data: {
|
|
587
|
+
labels: ${JSON.stringify(data.timeSeriesData.installs.map((d) => new Date(d.timestamp).toLocaleDateString()))},
|
|
588
|
+
datasets: [
|
|
589
|
+
{
|
|
590
|
+
label: 'Installations',
|
|
591
|
+
data: ${JSON.stringify(data.timeSeriesData.installs.map((d) => d.value))},
|
|
592
|
+
borderColor: '#2563eb',
|
|
593
|
+
backgroundColor: 'rgba(37, 99, 235, 0.1)'
|
|
594
|
+
},
|
|
595
|
+
{
|
|
596
|
+
label: 'Searches',
|
|
597
|
+
data: ${JSON.stringify(data.timeSeriesData.searches.map((d) => d.value))},
|
|
598
|
+
borderColor: '#10b981',
|
|
599
|
+
backgroundColor: 'rgba(16, 185, 129, 0.1)'
|
|
600
|
+
}
|
|
601
|
+
]
|
|
602
|
+
},
|
|
603
|
+
options: {
|
|
604
|
+
responsive: true,
|
|
605
|
+
scales: {
|
|
606
|
+
y: { beginAtZero: true }
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
});`;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
_generateTopPluginsChartScript(data) {
|
|
613
|
+
return `
|
|
614
|
+
// Top Plugins Chart
|
|
615
|
+
const topPluginsCtx = document.getElementById('topPluginsChart').getContext('2d');
|
|
616
|
+
new Chart(topPluginsCtx, {
|
|
617
|
+
type: 'bar',
|
|
618
|
+
data: {
|
|
619
|
+
labels: ${JSON.stringify(data.topPlugins.map((p) => p.pluginId))},
|
|
620
|
+
datasets: [{
|
|
621
|
+
label: 'Installations',
|
|
622
|
+
data: ${JSON.stringify(data.topPlugins.map((p) => p.installs))},
|
|
623
|
+
backgroundColor: 'rgba(37, 99, 235, 0.8)'
|
|
624
|
+
}]
|
|
625
|
+
},
|
|
626
|
+
options: {
|
|
627
|
+
responsive: true,
|
|
628
|
+
scales: {
|
|
629
|
+
y: { beginAtZero: true }
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
});`;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// Helper methods
|
|
636
|
+
incrementMetric(name, amount = 1) {
|
|
637
|
+
const metric = this.metrics.get(name);
|
|
638
|
+
if (metric && metric._type === 'counter') {
|
|
639
|
+
metric.value += amount;
|
|
640
|
+
metric.history.push({ timestamp: Date.now(), value: metric.value });
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
recordHistogram(name, value) {
|
|
645
|
+
const metric = this.metrics.get(name);
|
|
646
|
+
if (metric && metric._type === 'histogram') {
|
|
647
|
+
const bucket = Math.floor(value / 100) * 100; // 100ms buckets
|
|
648
|
+
metric.buckets[bucket] = (metric.buckets[bucket] || 0) + 1;
|
|
649
|
+
metric.history.push({ timestamp: Date.now(), value });
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
calculateAverageRating() {
|
|
654
|
+
const ratingMetric = this.metrics.get('plugin_ratings');
|
|
655
|
+
if (!ratingMetric) return 0;
|
|
656
|
+
|
|
657
|
+
let totalRatings = 0;
|
|
658
|
+
let weightedSum = 0;
|
|
659
|
+
|
|
660
|
+
Object.entries(ratingMetric.buckets).forEach(([rating, count]) => {
|
|
661
|
+
totalRatings += count;
|
|
662
|
+
weightedSum += parseInt(rating) * count;
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
return totalRatings > 0 ? weightedSum / totalRatings : 0;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
calculateSecurityScore() {
|
|
669
|
+
const scans = this.metrics.get('security_scans')?.value || 0;
|
|
670
|
+
const issues = this.metrics.get('security_issues')?.value || 0;
|
|
671
|
+
|
|
672
|
+
if (scans === 0) return 100;
|
|
673
|
+
|
|
674
|
+
const issueRate = issues / scans;
|
|
675
|
+
return Math.max(0, Math.round(100 - issueRate * 20)); // Penalty for issues
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
calculatePercentile(values, percentile) {
|
|
679
|
+
if (values.length === 0) return 0;
|
|
680
|
+
|
|
681
|
+
const sorted = values.slice().sort((a, b) => a - b);
|
|
682
|
+
const index = Math.ceil((percentile / 100) * sorted.length) - 1;
|
|
683
|
+
return sorted[index] || 0;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
getActivityStatus(activity) {
|
|
687
|
+
switch (activity.type) {
|
|
688
|
+
case 'installation':
|
|
689
|
+
return 'success';
|
|
690
|
+
case 'security_scan':
|
|
691
|
+
return activity.issueCount > 0 ? 'warning' : 'success';
|
|
692
|
+
case 'certification':
|
|
693
|
+
return activity.approved ? 'success' : 'error';
|
|
694
|
+
default:
|
|
695
|
+
return 'success';
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
formatActivityDescription(activity) {
|
|
700
|
+
switch (activity.type) {
|
|
701
|
+
case 'installation':
|
|
702
|
+
return `Plugin ${activity.pluginId}@${activity.version} installed`;
|
|
703
|
+
case 'search':
|
|
704
|
+
return `Search "${activity.query}" returned ${activity.resultCount} results`;
|
|
705
|
+
case 'rating':
|
|
706
|
+
return `Plugin ${activity.pluginId} rated ${activity.rating} stars`;
|
|
707
|
+
case 'security_scan':
|
|
708
|
+
return `Security scan for ${activity.pluginId} - ${activity.risk} risk, ${activity.issueCount} issues`;
|
|
709
|
+
case 'certification':
|
|
710
|
+
return `Certification ${activity.approved ? 'approved' : 'rejected'} for ${activity.pluginId} (${activity.level})`;
|
|
711
|
+
default:
|
|
712
|
+
return JSON.stringify(activity);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
async collectMetrics() {
|
|
717
|
+
// Update active plugins count
|
|
718
|
+
try {
|
|
719
|
+
const pluginsDir = path.join(this._config.dataDir, 'plugins');
|
|
720
|
+
const entries = await fs.readdir(pluginsDir).catch(() => []);
|
|
721
|
+
|
|
722
|
+
const activeMetric = this.metrics.get('active_plugins');
|
|
723
|
+
if (activeMetric) {
|
|
724
|
+
activeMetric.value = entries.length;
|
|
725
|
+
activeMetric.history.push({
|
|
726
|
+
timestamp: Date.now(),
|
|
727
|
+
value: entries.length,
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
} catch (error) {
|
|
731
|
+
// Ignore errors
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
updateRealTimeData() {
|
|
736
|
+
// Clean up old real-time data (keep last 24 hours)
|
|
737
|
+
const cutoff = Date.now() - 24 * 60 * 60 * 1000;
|
|
738
|
+
|
|
739
|
+
for (const [key, data] of this.realTimeData) {
|
|
740
|
+
if (data.timestamp < cutoff) {
|
|
741
|
+
this.realTimeData.delete(key);
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
notifySubscribers() {
|
|
747
|
+
const data = this.getDashboardData();
|
|
748
|
+
this.subscribers.forEach((callback) => {
|
|
749
|
+
try {
|
|
750
|
+
callback(data);
|
|
751
|
+
} catch (error) {
|
|
752
|
+
console.error('Error notifying subscriber:', error);
|
|
753
|
+
// eslint-disable-line no-console
|
|
754
|
+
}
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
async cleanupOldData() {
|
|
759
|
+
const cutoff =
|
|
760
|
+
Date.now() - this._config.retentionDays * 24 * 60 * 60 * 1000;
|
|
761
|
+
|
|
762
|
+
// Clean up metric history
|
|
763
|
+
for (const metric of this.metrics.values()) {
|
|
764
|
+
if (metric.history) {
|
|
765
|
+
metric.history = metric.history.filter(
|
|
766
|
+
(entry) => entry.timestamp > cutoff,
|
|
767
|
+
);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
subscribe(callback) {
|
|
773
|
+
this.subscribers.add(callback);
|
|
774
|
+
return () => this.subscribers.delete(callback);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
module.exports = { PluginAnalyticsDashboard };
|