opencastle 0.17.0 → 0.18.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/dist/cli/convoy/engine.d.ts.map +1 -1
- package/dist/cli/convoy/engine.js +15 -2
- package/dist/cli/convoy/engine.js.map +1 -1
- package/dist/cli/convoy/export.d.ts +3 -0
- package/dist/cli/convoy/export.d.ts.map +1 -0
- package/dist/cli/convoy/export.js +46 -0
- package/dist/cli/convoy/export.js.map +1 -0
- package/dist/cli/convoy/export.test.d.ts +2 -0
- package/dist/cli/convoy/export.test.d.ts.map +1 -0
- package/dist/cli/convoy/export.test.js +157 -0
- package/dist/cli/convoy/export.test.js.map +1 -0
- package/dist/cli/dashboard.d.ts +14 -0
- package/dist/cli/dashboard.d.ts.map +1 -1
- package/dist/cli/dashboard.js +73 -36
- package/dist/cli/dashboard.js.map +1 -1
- package/dist/cli/run.d.ts +1 -1
- package/dist/cli/run.d.ts.map +1 -1
- package/dist/cli/run.js +16 -1
- package/dist/cli/run.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/convoy/engine.ts +9 -2
- package/src/cli/convoy/export.test.ts +190 -0
- package/src/cli/convoy/export.ts +52 -0
- package/src/cli/dashboard.ts +94 -42
- package/src/cli/run.ts +16 -1
- package/src/dashboard/dist/_astro/{index.Bnq19_1M.css → index.DyyaCW8L.css} +1 -1
- package/src/dashboard/dist/index.html +145 -6
- package/src/dashboard/node_modules/.vite/deps/_metadata.json +6 -6
- package/src/dashboard/src/pages/index.astro +160 -4
- package/src/dashboard/src/styles/dashboard.css +60 -0
|
@@ -26,6 +26,7 @@ const base = import.meta.env.BASE_URL;
|
|
|
26
26
|
<!-- Sidebar Navigation -->
|
|
27
27
|
<nav class="dash-sidebar" id="dash-sidebar">
|
|
28
28
|
<ul class="dash-sidebar__list">
|
|
29
|
+
<li><a class="dash-sidebar__link" href="#convoy-section" data-section="convoy-section">Convoy</a></li>
|
|
29
30
|
<li><a class="dash-sidebar__link dash-sidebar__link--active" href="#kpi-row" data-section="kpi-row">Overview</a></li>
|
|
30
31
|
<li><a class="dash-sidebar__link" href="#pipeline-section" data-section="pipeline-section">Pipeline</a></li>
|
|
31
32
|
<li><a class="dash-sidebar__link" href="#agent-section" data-section="agent-section">Agents</a></li>
|
|
@@ -66,9 +67,25 @@ const base = import.meta.env.BASE_URL;
|
|
|
66
67
|
<option value="failed">Failed</option>
|
|
67
68
|
</select>
|
|
68
69
|
</div>
|
|
70
|
+
<div class="filter-group">
|
|
71
|
+
<label class="filter-label" for="filter-convoy">Convoy</label>
|
|
72
|
+
<select class="filter-select" id="filter-convoy">
|
|
73
|
+
<option value="">All convoys</option>
|
|
74
|
+
</select>
|
|
75
|
+
</div>
|
|
69
76
|
<button class="dash-btn dash-btn--ghost filter-reset" id="filter-reset" type="button">Reset</button>
|
|
70
77
|
</div>
|
|
71
78
|
|
|
79
|
+
<!-- Convoy Status Section -->
|
|
80
|
+
<section class="chart-card convoy-status" id="convoy-section" data-nav-section style="display:none">
|
|
81
|
+
<div class="chart-card__header">
|
|
82
|
+
<h2 class="chart-card__title">Convoy Status</h2>
|
|
83
|
+
<p class="chart-card__desc" id="convoy-desc">Select a convoy to view details</p>
|
|
84
|
+
</div>
|
|
85
|
+
<div class="chart-card__body" id="convoy-body">
|
|
86
|
+
</div>
|
|
87
|
+
</section>
|
|
88
|
+
|
|
72
89
|
<!-- KPI Row -->
|
|
73
90
|
<section class="kpi-row" id="kpi-row" data-nav-section>
|
|
74
91
|
<div class="kpi-card" id="kpi-sessions">
|
|
@@ -1154,12 +1171,14 @@ const base = import.meta.env.BASE_URL;
|
|
|
1154
1171
|
let rawDelegations = [];
|
|
1155
1172
|
let rawPanels = [];
|
|
1156
1173
|
let rawReviews = [];
|
|
1174
|
+
let rawConvoys = [];
|
|
1157
1175
|
|
|
1158
1176
|
function applyFilters() {
|
|
1159
1177
|
const dateFrom = document.getElementById('filter-date-from').value;
|
|
1160
1178
|
const dateTo = document.getElementById('filter-date-to').value;
|
|
1161
1179
|
const agentFilter = document.getElementById('filter-agent').value;
|
|
1162
1180
|
const outcomeFilter = document.getElementById('filter-outcome').value;
|
|
1181
|
+
const convoyFilter = document.getElementById('filter-convoy').value;
|
|
1163
1182
|
|
|
1164
1183
|
function matchDate(ts) {
|
|
1165
1184
|
const date = ts.slice(0, 10);
|
|
@@ -1168,27 +1187,43 @@ const base = import.meta.env.BASE_URL;
|
|
|
1168
1187
|
return true;
|
|
1169
1188
|
}
|
|
1170
1189
|
|
|
1171
|
-
|
|
1190
|
+
let sessions = rawSessions.filter((s) => {
|
|
1172
1191
|
if (!matchDate(s.timestamp)) return false;
|
|
1173
1192
|
if (agentFilter && s.agent !== agentFilter) return false;
|
|
1174
1193
|
if (outcomeFilter && s.outcome !== outcomeFilter) return false;
|
|
1175
1194
|
return true;
|
|
1176
1195
|
});
|
|
1177
1196
|
|
|
1178
|
-
|
|
1197
|
+
let delegations = rawDelegations.filter((d) => {
|
|
1179
1198
|
if (!matchDate(d.timestamp)) return false;
|
|
1180
1199
|
if (agentFilter && d.agent !== agentFilter) return false;
|
|
1181
1200
|
if (outcomeFilter && d.outcome !== outcomeFilter) return false;
|
|
1182
1201
|
return true;
|
|
1183
1202
|
});
|
|
1184
1203
|
|
|
1185
|
-
|
|
1186
|
-
|
|
1204
|
+
let panels = rawPanels.filter((p) => matchDate(p.timestamp));
|
|
1205
|
+
let reviews = rawReviews.filter((r) => {
|
|
1187
1206
|
if (!matchDate(r.timestamp)) return false;
|
|
1188
1207
|
if (agentFilter && r.agent !== agentFilter) return false;
|
|
1189
1208
|
return true;
|
|
1190
1209
|
});
|
|
1191
1210
|
|
|
1211
|
+
if (convoyFilter) {
|
|
1212
|
+
sessions = sessions.filter((s) => s.convoy_id === convoyFilter);
|
|
1213
|
+
delegations = delegations.filter((d) => d.convoy_id === convoyFilter);
|
|
1214
|
+
panels = panels.filter((p) => p.convoy_id === convoyFilter);
|
|
1215
|
+
reviews = reviews.filter((r) => r.convoy_id === convoyFilter);
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
const convoySection = document.getElementById('convoy-section');
|
|
1219
|
+
if (convoySection) {
|
|
1220
|
+
convoySection.style.display = convoyFilter ? '' : 'none';
|
|
1221
|
+
if (convoyFilter) {
|
|
1222
|
+
const convoy = rawConvoys.find((c) => c.id === convoyFilter);
|
|
1223
|
+
renderConvoyStatus(convoy);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1192
1227
|
renderAll(sessions, delegations, panels, reviews);
|
|
1193
1228
|
}
|
|
1194
1229
|
|
|
@@ -1300,8 +1335,80 @@ const base = import.meta.env.BASE_URL;
|
|
|
1300
1335
|
URL.revokeObjectURL(url);
|
|
1301
1336
|
}
|
|
1302
1337
|
|
|
1338
|
+
function populateConvoyFilter(convoys) {
|
|
1339
|
+
const select = document.getElementById('filter-convoy');
|
|
1340
|
+
if (!select) return;
|
|
1341
|
+
while (select.options.length > 1) select.remove(1);
|
|
1342
|
+
const sorted = convoys.slice().sort((a, b) => b.created_at.localeCompare(a.created_at));
|
|
1343
|
+
sorted.forEach((c) => {
|
|
1344
|
+
const opt = document.createElement('option');
|
|
1345
|
+
opt.value = c.id;
|
|
1346
|
+
opt.textContent = c.name + ' (' + c.status + ')';
|
|
1347
|
+
select.appendChild(opt);
|
|
1348
|
+
});
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
function renderConvoyStatus(convoy) {
|
|
1352
|
+
const descEl = document.getElementById('convoy-desc');
|
|
1353
|
+
const bodyEl = document.getElementById('convoy-body');
|
|
1354
|
+
if (!descEl || !bodyEl) return;
|
|
1355
|
+
|
|
1356
|
+
if (!convoy) {
|
|
1357
|
+
bodyEl.innerHTML = emptyStateHtml('pipeline', 'Convoy not found', 'No matching convoy data available.');
|
|
1358
|
+
return;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
descEl.textContent = convoy.name + ' — ' + (convoy.branch || 'no branch');
|
|
1362
|
+
|
|
1363
|
+
const s = convoy.summary || {};
|
|
1364
|
+
const total = s.total || (convoy.tasks ? convoy.tasks.length : 0);
|
|
1365
|
+
const done = s.done || 0;
|
|
1366
|
+
const pct = total > 0 ? Math.round((done / total) * 100) : 0;
|
|
1367
|
+
|
|
1368
|
+
const statusClass = convoy.status === 'done' ? 'success'
|
|
1369
|
+
: (convoy.status === 'failed' || convoy.status === 'gate-failed') ? 'failed'
|
|
1370
|
+
: convoy.status === 'running' ? 'partial' : '';
|
|
1371
|
+
|
|
1372
|
+
let html = '';
|
|
1373
|
+
html += '<div class="convoy-overview">';
|
|
1374
|
+
html += '<div class="convoy-stat"><span class="convoy-stat__label">Status</span><span class="outcome-badge outcome-badge--' + statusClass + '">' + escapeHtml(convoy.status) + '</span></div>';
|
|
1375
|
+
html += '<div class="convoy-stat"><span class="convoy-stat__label">Branch</span><span class="convoy-stat__value">' + escapeHtml(convoy.branch || '\u2014') + '</span></div>';
|
|
1376
|
+
html += '<div class="convoy-stat"><span class="convoy-stat__label">Tasks</span><span class="convoy-stat__value">' + done + '/' + total + '</span></div>';
|
|
1377
|
+
html += '<div class="convoy-stat"><span class="convoy-stat__label">Events</span><span class="convoy-stat__value">' + (convoy.events_count || 0) + '</span></div>';
|
|
1378
|
+
html += '<div class="convoy-stat"><span class="convoy-stat__label">Started</span><span class="convoy-stat__value">' + (convoy.started_at ? formatTime(convoy.started_at) : '\u2014') + '</span></div>';
|
|
1379
|
+
html += '</div>';
|
|
1380
|
+
|
|
1381
|
+
html += '<div class="convoy-progress">';
|
|
1382
|
+
html += '<div class="convoy-progress__bar"><div class="convoy-progress__fill" style="width:' + pct + '%"></div></div>';
|
|
1383
|
+
html += '<span class="convoy-progress__label">' + pct + '% complete</span>';
|
|
1384
|
+
html += '</div>';
|
|
1385
|
+
|
|
1386
|
+
if (convoy.tasks && convoy.tasks.length > 0) {
|
|
1387
|
+
html += '<table class="sessions-table convoy-tasks">';
|
|
1388
|
+
html += '<thead><tr><th>Task</th><th>Phase</th><th>Agent</th><th>Adapter</th><th>Status</th><th>Retries</th></tr></thead>';
|
|
1389
|
+
html += '<tbody>';
|
|
1390
|
+
convoy.tasks.forEach(function(t) {
|
|
1391
|
+
const tStatus = t.status === 'done' ? 'success'
|
|
1392
|
+
: (t.status === 'failed' || t.status === 'timed-out') ? 'failed'
|
|
1393
|
+
: t.status === 'running' ? 'partial' : '';
|
|
1394
|
+
html += '<tr>';
|
|
1395
|
+
html += '<td>' + escapeHtml(t.id) + '</td>';
|
|
1396
|
+
html += '<td class="td-num">' + t.phase + '</td>';
|
|
1397
|
+
html += '<td class="td-agent">' + escapeHtml(t.agent) + '</td>';
|
|
1398
|
+
html += '<td>' + escapeHtml(t.adapter || '\u2014') + '</td>';
|
|
1399
|
+
html += '<td><span class="outcome-badge outcome-badge--' + tStatus + '">' + escapeHtml(t.status) + '</span></td>';
|
|
1400
|
+
html += '<td class="td-num">' + (t.retries || 0) + '</td>';
|
|
1401
|
+
html += '</tr>';
|
|
1402
|
+
});
|
|
1403
|
+
html += '</tbody></table>';
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
bodyEl.innerHTML = html;
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1303
1409
|
async function main() {
|
|
1304
1410
|
const events = await loadNdjson(base + 'data/events.ndjson');
|
|
1411
|
+
const convoys = await loadNdjson(base + 'data/convoys.ndjson');
|
|
1305
1412
|
|
|
1306
1413
|
const sessions = events.filter((e) => e.type === 'session');
|
|
1307
1414
|
const delegations = events.filter((e) => e.type === 'delegation');
|
|
@@ -1312,23 +1419,72 @@ const base = import.meta.env.BASE_URL;
|
|
|
1312
1419
|
rawDelegations = delegations;
|
|
1313
1420
|
rawPanels = panels;
|
|
1314
1421
|
rawReviews = reviews;
|
|
1422
|
+
rawConvoys = convoys;
|
|
1315
1423
|
|
|
1316
1424
|
populateAgentFilter(sessions, delegations, reviews);
|
|
1425
|
+
populateConvoyFilter(convoys);
|
|
1426
|
+
|
|
1427
|
+
// ── Read URL params ───────────────────────────────────
|
|
1428
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
1429
|
+
const convoyParam = urlParams.get('convoy');
|
|
1430
|
+
if (convoyParam === 'active') {
|
|
1431
|
+
const running = rawConvoys.find((c) => c.status === 'running');
|
|
1432
|
+
const latest = rawConvoys.slice().sort((a, b) => b.created_at.localeCompare(a.created_at))[0];
|
|
1433
|
+
const target = running || latest;
|
|
1434
|
+
if (target) {
|
|
1435
|
+
const sel = document.getElementById('filter-convoy');
|
|
1436
|
+
if (sel) sel.value = target.id;
|
|
1437
|
+
}
|
|
1438
|
+
} else if (convoyParam) {
|
|
1439
|
+
const sel = document.getElementById('filter-convoy');
|
|
1440
|
+
if (sel) sel.value = convoyParam;
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1317
1443
|
renderAll(sessions, delegations, panels, reviews);
|
|
1318
1444
|
|
|
1445
|
+
// Apply convoy param after initial render (shows convoy section if needed)
|
|
1446
|
+
if (convoyParam) applyFilters();
|
|
1447
|
+
|
|
1319
1448
|
// ── Filter event listeners ────────────────────────────
|
|
1320
1449
|
document.getElementById('filter-date-from')?.addEventListener('change', applyFilters);
|
|
1321
1450
|
document.getElementById('filter-date-to')?.addEventListener('change', applyFilters);
|
|
1322
1451
|
document.getElementById('filter-agent')?.addEventListener('change', applyFilters);
|
|
1323
1452
|
document.getElementById('filter-outcome')?.addEventListener('change', applyFilters);
|
|
1453
|
+
document.getElementById('filter-convoy')?.addEventListener('change', applyFilters);
|
|
1324
1454
|
document.getElementById('filter-reset')?.addEventListener('click', () => {
|
|
1325
1455
|
document.getElementById('filter-date-from').value = '';
|
|
1326
1456
|
document.getElementById('filter-date-to').value = '';
|
|
1327
1457
|
document.getElementById('filter-agent').value = '';
|
|
1328
1458
|
document.getElementById('filter-outcome').value = '';
|
|
1459
|
+
document.getElementById('filter-convoy').value = '';
|
|
1329
1460
|
applyFilters();
|
|
1330
1461
|
});
|
|
1331
1462
|
|
|
1463
|
+
// ── Auto-refresh for live convoy monitoring ───────────
|
|
1464
|
+
let refreshInterval = null;
|
|
1465
|
+
function startAutoRefresh() {
|
|
1466
|
+
if (refreshInterval) return;
|
|
1467
|
+
refreshInterval = setInterval(async () => {
|
|
1468
|
+
const freshEvents = await loadNdjson(base + 'data/events.ndjson');
|
|
1469
|
+
const freshConvoys = await loadNdjson(base + 'data/convoys.ndjson');
|
|
1470
|
+
rawSessions = freshEvents.filter((e) => e.type === 'session');
|
|
1471
|
+
rawDelegations = freshEvents.filter((e) => e.type === 'delegation');
|
|
1472
|
+
rawPanels = freshEvents.filter((e) => e.type === 'panel');
|
|
1473
|
+
rawReviews = freshEvents.filter((e) => e.type === 'review');
|
|
1474
|
+
rawConvoys = freshConvoys;
|
|
1475
|
+
const currentValue = document.getElementById('filter-convoy')?.value;
|
|
1476
|
+
populateConvoyFilter(freshConvoys);
|
|
1477
|
+
const sel = document.getElementById('filter-convoy');
|
|
1478
|
+
if (sel && currentValue) sel.value = currentValue;
|
|
1479
|
+
applyFilters();
|
|
1480
|
+
}, 5000);
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
const selectedConvoy = rawConvoys.find((c) => c.id === document.getElementById('filter-convoy')?.value);
|
|
1484
|
+
if (convoyParam === 'active' || (selectedConvoy && selectedConvoy.status === 'running')) {
|
|
1485
|
+
startAutoRefresh();
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1332
1488
|
// ── Export button ─────────────────────────────────────
|
|
1333
1489
|
document.getElementById('export-btn')?.addEventListener('click', exportData);
|
|
1334
1490
|
|
|
@@ -1533,3 +1533,63 @@ body {
|
|
|
1533
1533
|
padding: 8px 6px;
|
|
1534
1534
|
}
|
|
1535
1535
|
}
|
|
1536
|
+
|
|
1537
|
+
/* ---------- Convoy Section ---------- */
|
|
1538
|
+
.convoy-overview {
|
|
1539
|
+
display: flex;
|
|
1540
|
+
flex-wrap: wrap;
|
|
1541
|
+
gap: 24px;
|
|
1542
|
+
margin-bottom: 20px;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
.convoy-stat {
|
|
1546
|
+
display: flex;
|
|
1547
|
+
flex-direction: column;
|
|
1548
|
+
gap: 4px;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
.convoy-stat__label {
|
|
1552
|
+
font-size: 0.75rem;
|
|
1553
|
+
color: var(--text-tertiary);
|
|
1554
|
+
text-transform: uppercase;
|
|
1555
|
+
letter-spacing: 0.05em;
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
.convoy-stat__value {
|
|
1559
|
+
font-size: 0.95rem;
|
|
1560
|
+
color: var(--text-primary);
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
/* Progress bar */
|
|
1564
|
+
.convoy-progress {
|
|
1565
|
+
display: flex;
|
|
1566
|
+
align-items: center;
|
|
1567
|
+
gap: 12px;
|
|
1568
|
+
margin-bottom: 20px;
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
.convoy-progress__bar {
|
|
1572
|
+
flex: 1;
|
|
1573
|
+
height: 8px;
|
|
1574
|
+
background: var(--bg-tertiary);
|
|
1575
|
+
border-radius: 4px;
|
|
1576
|
+
overflow: hidden;
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
.convoy-progress__fill {
|
|
1580
|
+
height: 100%;
|
|
1581
|
+
background: var(--gradient-accent);
|
|
1582
|
+
border-radius: 4px;
|
|
1583
|
+
transition: width var(--transition-base);
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
.convoy-progress__label {
|
|
1587
|
+
font-size: 0.8rem;
|
|
1588
|
+
color: var(--text-secondary);
|
|
1589
|
+
white-space: nowrap;
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
/* Task table inherits .sessions-table styles */
|
|
1593
|
+
.convoy-tasks {
|
|
1594
|
+
margin-top: 8px;
|
|
1595
|
+
}
|