viberadar 0.3.140 → 0.3.141
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/scanner/index.d.ts +27 -0
- package/dist/scanner/index.d.ts.map +1 -1
- package/dist/scanner/index.js +53 -0
- package/dist/scanner/index.js.map +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +165 -3
- package/dist/server/index.js.map +1 -1
- package/dist/ui/dashboard.html +183 -0
- package/package.json +1 -1
package/dist/ui/dashboard.html
CHANGED
|
@@ -1585,6 +1585,7 @@ const modeStore = {
|
|
|
1585
1585
|
qa: { view: 'features', searchQuery: '', activeTypes: new Set(), drillFeatureKey: null, drillTestType: null, activePanelKey: null, showOnlyUntestedInFeature: false, testNavType: 'all', testNavFeature: null, testNavProblem: null },
|
|
1586
1586
|
observability: { view: 'files', searchQuery: '', activeTypes: new Set(), drillFeatureKey: null, drillTestType: null, activePanelKey: null, showOnlyUntestedInFeature: false, testNavType: 'all', testNavFeature: null, testNavProblem: null },
|
|
1587
1587
|
docs: { view: 'features', searchQuery: '', activeTypes: new Set(), drillFeatureKey: null, drillTestType: null, activePanelKey: null, showOnlyUntestedInFeature: false, testNavType: 'all', testNavFeature: null, testNavProblem: null },
|
|
1588
|
+
scenarios: { view: 'list', searchQuery: '', activeTypes: new Set(), drillFeatureKey: null, drillTestType: null, activePanelKey: null, showOnlyUntestedInFeature: false, testNavType: 'all', testNavFeature: null, testNavProblem: null },
|
|
1588
1589
|
services: { view: 'graph', searchQuery: '', activeTypes: new Set(), drillFeatureKey: null, drillTestType: null, activePanelKey: null, showOnlyUntestedInFeature: false, testNavType: 'all', testNavFeature: null, testNavProblem: null, svcTab: 'graph' },
|
|
1589
1590
|
};
|
|
1590
1591
|
|
|
@@ -2942,6 +2943,7 @@ function renderModeSwitch() {
|
|
|
2942
2943
|
{ key: 'qa', label: 'QA Coverage', hint: 'Покрытие, пробелы, тренды' },
|
|
2943
2944
|
{ key: 'observability', label: 'Наблюдаемость', hint: 'Логи, шум, сигналы ошибок' },
|
|
2944
2945
|
{ key: 'docs', label: 'Документация', hint: 'Актуальность, генерация, обновление' },
|
|
2946
|
+
{ key: 'scenarios', label: 'Сценарии', hint: 'Пользовательские сценарии, user journeys' },
|
|
2945
2947
|
{ key: 'services', label: 'Карта сервисов', hint: 'Зависимости, пайплайны, мониторинг' },
|
|
2946
2948
|
];
|
|
2947
2949
|
root.innerHTML = modes.map(m => `
|
|
@@ -2981,6 +2983,20 @@ function renderSidebar() {
|
|
|
2981
2983
|
return;
|
|
2982
2984
|
}
|
|
2983
2985
|
|
|
2986
|
+
if (contextMode === 'scenarios') {
|
|
2987
|
+
tabs.style.display = 'none';
|
|
2988
|
+
const sr = D.scenarios;
|
|
2989
|
+
extra.innerHTML = `
|
|
2990
|
+
<div class="sidebar-label">Сценарии</div>
|
|
2991
|
+
<div style="font-size:12px;color:var(--muted);padding:0 6px;line-height:1.45">
|
|
2992
|
+
Пользовательские сценарии и user journeys.
|
|
2993
|
+
</div>
|
|
2994
|
+
${sr ? `<div style="margin-top:12px;padding:0 6px;font-size:12px;color:var(--muted)">
|
|
2995
|
+
<span style="color:var(--green)">${sr.withDocCount}</span> готово · <span style="color:var(--red)">${sr.missingDocCount}</span> без доки
|
|
2996
|
+
</div>` : ''}`;
|
|
2997
|
+
return;
|
|
2998
|
+
}
|
|
2999
|
+
|
|
2984
3000
|
if (contextMode === 'services') {
|
|
2985
3001
|
tabs.style.display = 'none';
|
|
2986
3002
|
const svcTab = modeStore.services.svcTab || 'graph';
|
|
@@ -3053,6 +3069,10 @@ function renderContent() {
|
|
|
3053
3069
|
renderDocumentation(c);
|
|
3054
3070
|
return;
|
|
3055
3071
|
}
|
|
3072
|
+
if (contextMode === 'scenarios') {
|
|
3073
|
+
renderScenarios(c);
|
|
3074
|
+
return;
|
|
3075
|
+
}
|
|
3056
3076
|
if (contextMode === 'services') {
|
|
3057
3077
|
renderServiceMap(c);
|
|
3058
3078
|
return;
|
|
@@ -4398,6 +4418,169 @@ function renderObservabilityOverview(c) {
|
|
|
4398
4418
|
|
|
4399
4419
|
// ─── Documentation screen ─────────────────────────────────────────────────────
|
|
4400
4420
|
|
|
4421
|
+
// ─── Scenarios ───────────────────────────────────────────────────────────────
|
|
4422
|
+
|
|
4423
|
+
let scenarioDrillKey = null;
|
|
4424
|
+
|
|
4425
|
+
function renderScenarios(c) {
|
|
4426
|
+
const sr = D.scenarios;
|
|
4427
|
+
if (!sr) {
|
|
4428
|
+
c.innerHTML = `<div class="onboarding-block"><h3>Сценарии</h3>
|
|
4429
|
+
<p>Добавьте сценарии в <code>viberadar.config.json</code>:</p>
|
|
4430
|
+
<pre style="margin-top:12px;font-size:12px;background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:12px;overflow-x:auto">"scenarios": {
|
|
4431
|
+
"create-knowledge-skill": {
|
|
4432
|
+
"label": "Создать навык с базой знаний",
|
|
4433
|
+
"description": "Пользователь создаёт базу знаний и подключает её к навыку",
|
|
4434
|
+
"features": ["knowledge-base", "skills"]
|
|
4435
|
+
}
|
|
4436
|
+
}</pre>
|
|
4437
|
+
</div>`;
|
|
4438
|
+
return;
|
|
4439
|
+
}
|
|
4440
|
+
if (scenarioDrillKey) renderScenarioDetail(c);
|
|
4441
|
+
else renderScenarioCards(c);
|
|
4442
|
+
}
|
|
4443
|
+
|
|
4444
|
+
function renderScenarioCards(c) {
|
|
4445
|
+
const sr = D.scenarios;
|
|
4446
|
+
if (!sr || !sr.scenarios.length) {
|
|
4447
|
+
c.innerHTML = `<div class="onboarding-block"><p>Сценарии не найдены в viberadar.config.json.</p></div>`;
|
|
4448
|
+
return;
|
|
4449
|
+
}
|
|
4450
|
+
|
|
4451
|
+
let html = `
|
|
4452
|
+
<div class="doc-kpi-strip">
|
|
4453
|
+
<div class="doc-kpi"><span class="doc-kpi-val">${sr.totalScenarios}</span><span class="doc-kpi-lbl">Сценариев</span></div>
|
|
4454
|
+
<div class="doc-kpi"><span class="doc-kpi-val" style="color:var(--green)">${sr.withDocCount}</span><span class="doc-kpi-lbl">Готово</span></div>
|
|
4455
|
+
<div class="doc-kpi"><span class="doc-kpi-val" style="color:var(--red)">${sr.missingDocCount}</span><span class="doc-kpi-lbl">Без доки</span></div>
|
|
4456
|
+
</div>
|
|
4457
|
+
<div class="features-grid">`;
|
|
4458
|
+
|
|
4459
|
+
for (const s of sr.scenarios) {
|
|
4460
|
+
const hasDoc = s.docExists;
|
|
4461
|
+
const statusColor = hasDoc ? 'var(--green)' : 'var(--red)';
|
|
4462
|
+
const statusText = hasDoc ? `v${s.latestVersion}` : 'Нет доки';
|
|
4463
|
+
const lastUpd = s.lastUpdated ? new Date(s.lastUpdated).toLocaleDateString('ru-RU') : '—';
|
|
4464
|
+
html += `
|
|
4465
|
+
<div class="doc-feature-card" data-key="${s.key}" style="cursor:pointer">
|
|
4466
|
+
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px">
|
|
4467
|
+
<span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:${s.color};flex-shrink:0"></span>
|
|
4468
|
+
<span style="font-weight:600;font-size:14px;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${escapeHtml(s.label)}</span>
|
|
4469
|
+
<span style="font-size:11px;color:${statusColor};font-weight:600;flex-shrink:0">${statusText}</span>
|
|
4470
|
+
</div>
|
|
4471
|
+
${s.description ? `<div style="font-size:12px;color:var(--muted);margin-bottom:8px;line-height:1.4">${escapeHtml(s.description)}</div>` : ''}
|
|
4472
|
+
<div style="font-size:11px;color:var(--muted);display:flex;gap:8px;flex-wrap:wrap">
|
|
4473
|
+
${s.featureLabels.map(l => `<span style="background:var(--bg);border:1px solid var(--border);border-radius:4px;padding:1px 6px">${escapeHtml(l)}</span>`).join('')}
|
|
4474
|
+
</div>
|
|
4475
|
+
${hasDoc ? `<div style="font-size:11px;color:var(--muted);margin-top:6px">Обновлено: ${lastUpd}</div>` : ''}
|
|
4476
|
+
</div>`;
|
|
4477
|
+
}
|
|
4478
|
+
html += '</div>';
|
|
4479
|
+
c.innerHTML = html;
|
|
4480
|
+
|
|
4481
|
+
c.querySelectorAll('.doc-feature-card').forEach(card => {
|
|
4482
|
+
card.addEventListener('click', () => {
|
|
4483
|
+
scenarioDrillKey = card.dataset.key;
|
|
4484
|
+
renderContent();
|
|
4485
|
+
});
|
|
4486
|
+
});
|
|
4487
|
+
}
|
|
4488
|
+
|
|
4489
|
+
async function renderScenarioDetail(c) {
|
|
4490
|
+
const sr = D.scenarios;
|
|
4491
|
+
const s = sr?.scenarios.find(x => x.key === scenarioDrillKey);
|
|
4492
|
+
if (!s) { scenarioDrillKey = null; renderContent(); return; }
|
|
4493
|
+
|
|
4494
|
+
const statusColor = s.docExists ? 'var(--green)' : 'var(--red)';
|
|
4495
|
+
const statusText = s.docExists ? `Готово · v${s.latestVersion}` : 'Нет документации';
|
|
4496
|
+
const lastUpd = s.lastUpdated ? new Date(s.lastUpdated).toLocaleDateString('ru-RU') : '—';
|
|
4497
|
+
const nextVersion = (s.latestVersion || 0) + 1;
|
|
4498
|
+
const btnLabel = s.docExists ? `Актуализировать сценарий → v${nextVersion}` : 'Сгенерировать сценарий → v1';
|
|
4499
|
+
const btnHint = s.docExists
|
|
4500
|
+
? 'Агент перечитает документацию фич и обновит сценарий.'
|
|
4501
|
+
: 'Агент прочитает документацию связанных фич и напишет пошаговый сценарий.';
|
|
4502
|
+
|
|
4503
|
+
const versionsHtml = s.docVersions && s.docVersions.length > 0
|
|
4504
|
+
? `<div style="font-size:11px;color:var(--muted);margin-top:6px">Версии: ${s.docVersions.map((v, i) => `<span style="${i === s.docVersions.length - 1 ? 'color:var(--text)' : ''}">${v.split('/').pop()}</span>`).join(' · ')}</div>`
|
|
4505
|
+
: '';
|
|
4506
|
+
|
|
4507
|
+
const exportButtons = s.docExists ? `
|
|
4508
|
+
<a href="/api/docs/export/md?scenario=${encodeURIComponent(s.key)}" download class="doc-agent-btn" style="font-size:13px;padding:6px 14px;text-decoration:none">⬇ .md</a>
|
|
4509
|
+
<a href="/api/docs/export/docx?scenario=${encodeURIComponent(s.key)}" download class="doc-agent-btn" style="font-size:13px;padding:6px 14px;text-decoration:none">⬇ .docx</a>` : '';
|
|
4510
|
+
|
|
4511
|
+
// Check if referenced feature docs are missing
|
|
4512
|
+
const missingDocs = s.featureKeys.filter((fk, i) => {
|
|
4513
|
+
const featDoc = D.documentation?.features.find(f => f.key === fk);
|
|
4514
|
+
return !featDoc?.docExists;
|
|
4515
|
+
});
|
|
4516
|
+
const warningHtml = missingDocs.length > 0
|
|
4517
|
+
? `<div style="margin-top:8px;padding:8px 12px;border-radius:6px;background:rgba(210,153,34,0.1);border:1px solid rgba(210,153,34,0.3);font-size:12px;color:var(--yellow)">
|
|
4518
|
+
⚠️ Документация отсутствует для: ${missingDocs.map(k => `<strong>${escapeHtml(D.documentation?.features.find(f => f.key === k)?.label || k)}</strong>`).join(', ')}. Агент сможет сгенерировать сценарий, но качество будет ниже.
|
|
4519
|
+
</div>` : '';
|
|
4520
|
+
|
|
4521
|
+
c.innerHTML = `
|
|
4522
|
+
<div style="display:flex;align-items:center;gap:12px;margin-bottom:16px;flex-wrap:wrap">
|
|
4523
|
+
<button class="back-btn" id="scenarioBackBtn">← Все сценарии</button>
|
|
4524
|
+
<span style="width:12px;height:12px;border-radius:50%;background:${s.color};display:inline-block;flex-shrink:0"></span>
|
|
4525
|
+
<h2 style="font-size:18px;font-weight:700;margin:0;flex:1">${escapeHtml(s.label)}</h2>
|
|
4526
|
+
<div style="display:flex;align-items:center;font-size:13px"><span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:${statusColor};margin-right:6px"></span><span style="color:${statusColor};font-weight:600">${statusText}</span></div>
|
|
4527
|
+
${s.docExists ? `<span style="font-size:12px;color:var(--muted)">Обновлено: ${lastUpd}</span>` : ''}
|
|
4528
|
+
<div style="margin-left:auto;display:flex;gap:8px;align-items:center">
|
|
4529
|
+
${exportButtons}
|
|
4530
|
+
</div>
|
|
4531
|
+
</div>
|
|
4532
|
+
|
|
4533
|
+
<div style="margin-bottom:8px;font-size:12px;color:var(--muted);display:flex;gap:8px;flex-wrap:wrap;align-items:center">
|
|
4534
|
+
<span>Фичи:</span>
|
|
4535
|
+
${s.featureLabels.map(l => `<span style="background:var(--bg);border:1px solid var(--border);border-radius:4px;padding:2px 8px;font-size:12px">${escapeHtml(l)}</span>`).join('')}
|
|
4536
|
+
</div>
|
|
4537
|
+
|
|
4538
|
+
<div class="doc-detail-actions">
|
|
4539
|
+
<div class="doc-action-step">
|
|
4540
|
+
<div>
|
|
4541
|
+
<div class="doc-step-label">${btnLabel}</div>
|
|
4542
|
+
<div class="doc-step-hint">${btnHint}</div>
|
|
4543
|
+
<div style="margin-top:8px">
|
|
4544
|
+
<button class="agent-card-btn doc-detail-agent-btn" id="scenarioActualizeBtn" data-task="actualize-scenario" data-key="${s.key}">${btnLabel}</button>
|
|
4545
|
+
</div>
|
|
4546
|
+
${warningHtml}
|
|
4547
|
+
<div id="scenarioActualizeStatus" style="display:none;margin-top:10px;padding:8px 12px;border-radius:6px;background:rgba(88,166,255,0.08);border:1px solid rgba(88,166,255,0.2);font-size:12px;color:var(--blue)"></div>
|
|
4548
|
+
${versionsHtml}
|
|
4549
|
+
</div>
|
|
4550
|
+
</div>
|
|
4551
|
+
</div>
|
|
4552
|
+
|
|
4553
|
+
<div id="scenarioContent" style="margin-top:16px;padding:24px;background:var(--bg-card);border-radius:8px;border:1px solid var(--border)">
|
|
4554
|
+
<div style="color:var(--muted);font-size:13px">Загрузка...</div>
|
|
4555
|
+
</div>`;
|
|
4556
|
+
|
|
4557
|
+
document.getElementById('scenarioBackBtn').onclick = () => { scenarioDrillKey = null; renderContent(); };
|
|
4558
|
+
|
|
4559
|
+
document.getElementById('scenarioActualizeBtn').addEventListener('click', (e) => {
|
|
4560
|
+
e.stopPropagation();
|
|
4561
|
+
runAgentTask('actualize-scenario', s.key);
|
|
4562
|
+
});
|
|
4563
|
+
|
|
4564
|
+
// Load content
|
|
4565
|
+
if (s.docExists) {
|
|
4566
|
+
try {
|
|
4567
|
+
const res = await fetch(`/api/scenarios/content?scenario=${encodeURIComponent(s.key)}`);
|
|
4568
|
+
const data = await res.json();
|
|
4569
|
+
if (data.content) {
|
|
4570
|
+
document.getElementById('scenarioContent').innerHTML = renderMarkdownToHtml(data.content);
|
|
4571
|
+
} else {
|
|
4572
|
+
document.getElementById('scenarioContent').innerHTML = '<div style="color:var(--muted);font-size:13px">Документация отсутствует.</div>';
|
|
4573
|
+
}
|
|
4574
|
+
} catch {
|
|
4575
|
+
document.getElementById('scenarioContent').innerHTML = '<div style="color:var(--red);font-size:13px">Ошибка загрузки.</div>';
|
|
4576
|
+
}
|
|
4577
|
+
} else {
|
|
4578
|
+
document.getElementById('scenarioContent').innerHTML = `<div style="color:var(--muted);font-size:13px;text-align:center;padding:32px 0">Нажмите «${escapeHtml(btnLabel)}» чтобы сгенерировать документацию.</div>`;
|
|
4579
|
+
}
|
|
4580
|
+
}
|
|
4581
|
+
|
|
4582
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
4583
|
+
|
|
4401
4584
|
function renderDocumentation(c) {
|
|
4402
4585
|
const doc = D.documentation;
|
|
4403
4586
|
if (!doc) {
|