clementine-agent 1.18.82 β 1.18.83
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/dashboard.js +271 -1
- package/package.json +1 -1
package/dist/cli/dashboard.js
CHANGED
|
@@ -16389,6 +16389,9 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
16389
16389
|
<button class="build-tab-btn active" data-build-tab="crons" onclick="switchBuildTab('crons')" style="padding:8px 14px;border-radius:6px 6px 0 0;border:none;background:transparent;color:var(--text-primary);font-size:13px;font-weight:500;cursor:pointer;border-bottom:2px solid transparent">
|
|
16390
16390
|
<span style="margin-right:6px">π
</span>Tasks <span id="build-tab-cron-count" style="display:none;margin-left:4px;font-size:10px;background:var(--bg-tertiary);padding:1px 6px;border-radius:999px;color:var(--text-muted)">0</span>
|
|
16391
16391
|
</button>
|
|
16392
|
+
<button class="build-tab-btn" data-build-tab="runs" onclick="switchBuildTab('runs')" style="padding:8px 14px;border-radius:6px 6px 0 0;border:none;background:transparent;color:var(--text-secondary);font-size:13px;font-weight:500;cursor:pointer;border-bottom:2px solid transparent">
|
|
16393
|
+
<span style="margin-right:6px">π</span>Runs <span id="build-tab-runs-count" style="display:none;margin-left:4px;font-size:10px;background:var(--bg-tertiary);padding:1px 6px;border-radius:999px;color:var(--text-muted)">0</span>
|
|
16394
|
+
</button>
|
|
16392
16395
|
<button class="build-tab-btn" data-build-tab="toolsmcp" onclick="switchBuildTab('toolsmcp')" style="padding:8px 14px;border-radius:6px 6px 0 0;border:none;background:transparent;color:var(--text-secondary);font-size:13px;font-weight:500;cursor:pointer;border-bottom:2px solid transparent">
|
|
16393
16396
|
<span style="margin-right:6px">π§°</span>Tools & MCP <span id="build-tab-toolsmcp-count" style="display:none;margin-left:4px;font-size:10px;background:var(--bg-tertiary);padding:1px 6px;border-radius:999px;color:var(--text-muted)">0</span>
|
|
16394
16397
|
</button>
|
|
@@ -16404,6 +16407,12 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
16404
16407
|
<div id="build-tab-crons" style="display:none;flex:1;min-height:0;overflow-y:auto;padding:18px;background:var(--bg-primary)">
|
|
16405
16408
|
<div id="panel-cron"><div class="empty-state" style="padding:24px;color:var(--text-muted)">Loading scheduled tasksβ¦</div></div>
|
|
16406
16409
|
</div>
|
|
16410
|
+
<!-- ββ PRD Phase 3: Run list βββββββββββββββββββββββββββββββββββββββββββ
|
|
16411
|
+
Single table of every run across all tasks, with filters + saved
|
|
16412
|
+
views. Default view is "Failures (last 24h)". -->
|
|
16413
|
+
<div id="build-tab-runs" style="display:none;flex:1;min-height:0;overflow-y:auto;padding:18px;background:var(--bg-primary)">
|
|
16414
|
+
<div id="panel-runs"><div class="empty-state" style="padding:24px;color:var(--text-muted)">Loading run historyβ¦</div></div>
|
|
16415
|
+
</div>
|
|
16407
16416
|
<!-- ββ PRD Phase 2: Tools & MCP catalog ββββββββββββββββββββββββββββββββ
|
|
16408
16417
|
Read-only foundation in 1.18.81. Future slices: per-tool bindings,
|
|
16409
16418
|
Reconnect/Toggle/Edit actions, Approval Mode + Max-auto-runs config. -->
|
|
@@ -21261,8 +21270,10 @@ function switchBuildTab(tab) {
|
|
|
21261
21270
|
// Always close any open workflow when changing tabs β switching context
|
|
21262
21271
|
// is a clean slate, not a stale node hanging on the canvas.
|
|
21263
21272
|
if (typeof closeBuilderCanvas === 'function') closeBuilderCanvas();
|
|
21264
|
-
|
|
21273
|
+
var runsPane = document.getElementById('build-tab-runs');
|
|
21274
|
+
// Default: hide the Tools & MCP + Runs panes unless we're explicitly on them.
|
|
21265
21275
|
if (toolsmcpPane && tab !== 'toolsmcp') toolsmcpPane.style.display = 'none';
|
|
21276
|
+
if (runsPane && tab !== 'runs') runsPane.style.display = 'none';
|
|
21266
21277
|
if (tab === 'toolsmcp') {
|
|
21267
21278
|
// PRD Phase 2: Tools & MCP catalog. Read-only foundation in 1.18.81.
|
|
21268
21279
|
if (workPane) workPane.style.display = 'none';
|
|
@@ -21275,6 +21286,18 @@ function switchBuildTab(tab) {
|
|
|
21275
21286
|
if (typeof refreshToolsMcpCatalog === 'function') refreshToolsMcpCatalog();
|
|
21276
21287
|
return;
|
|
21277
21288
|
}
|
|
21289
|
+
if (tab === 'runs') {
|
|
21290
|
+
// PRD Phase 3: Run list β every run across every task.
|
|
21291
|
+
if (workPane) workPane.style.display = 'none';
|
|
21292
|
+
if (cronPane) cronPane.style.display = 'none';
|
|
21293
|
+
if (tplPane) tplPane.style.display = 'none';
|
|
21294
|
+
if (runsPane) runsPane.style.display = 'block';
|
|
21295
|
+
if (headerStrip) headerStrip.style.display = 'none';
|
|
21296
|
+
if (usagePanel) usagePanel.style.display = 'none';
|
|
21297
|
+
if (newBtn) newBtn.style.display = 'none';
|
|
21298
|
+
if (typeof refreshRunList === 'function') refreshRunList();
|
|
21299
|
+
return;
|
|
21300
|
+
}
|
|
21278
21301
|
if (tab === 'templates') {
|
|
21279
21302
|
if (workPane) workPane.style.display = 'none';
|
|
21280
21303
|
if (cronPane) cronPane.style.display = 'none';
|
|
@@ -23653,6 +23676,245 @@ function renderRunningCard(item) {
|
|
|
23653
23676
|
+ '</div></div>';
|
|
23654
23677
|
}
|
|
23655
23678
|
|
|
23679
|
+
// ββ PRD Phase 3: Run list ββββββββββββββββββββββββββββββββββββββββββββββ
|
|
23680
|
+
// Single sortable/filterable table of every CronRunEntry across all tasks.
|
|
23681
|
+
// Filters: status, task name, time window. Browser-local saved views.
|
|
23682
|
+
// Default view: "Failures (last 24h)". No new endpoints β reuses
|
|
23683
|
+
// /api/cron/runs (CronRunLog.readAllRecent).
|
|
23684
|
+
|
|
23685
|
+
var _runListState = {
|
|
23686
|
+
filterStatus: 'all', // 'all' | 'failed' | 'ok'
|
|
23687
|
+
filterWindow: '24h', // '24h' | '7d' | 'all'
|
|
23688
|
+
filterText: '', // free-text task name match
|
|
23689
|
+
data: [], // raw runs from /api/cron/runs
|
|
23690
|
+
};
|
|
23691
|
+
|
|
23692
|
+
function _runListLoadDefaultView() {
|
|
23693
|
+
// First-time visit: PRD Β§5.3 β default Saved View is "Failures (last 24h)".
|
|
23694
|
+
try {
|
|
23695
|
+
var raw = localStorage.getItem('runListView');
|
|
23696
|
+
if (raw) {
|
|
23697
|
+
var saved = JSON.parse(raw);
|
|
23698
|
+
_runListState.filterStatus = saved.filterStatus || 'all';
|
|
23699
|
+
_runListState.filterWindow = saved.filterWindow || '24h';
|
|
23700
|
+
_runListState.filterText = saved.filterText || '';
|
|
23701
|
+
return;
|
|
23702
|
+
}
|
|
23703
|
+
} catch (e) { /* ignore */ }
|
|
23704
|
+
// Default: failures, last 24h.
|
|
23705
|
+
_runListState.filterStatus = 'failed';
|
|
23706
|
+
_runListState.filterWindow = '24h';
|
|
23707
|
+
_runListState.filterText = '';
|
|
23708
|
+
}
|
|
23709
|
+
|
|
23710
|
+
function _runListSaveView() {
|
|
23711
|
+
try {
|
|
23712
|
+
localStorage.setItem('runListView', JSON.stringify({
|
|
23713
|
+
filterStatus: _runListState.filterStatus,
|
|
23714
|
+
filterWindow: _runListState.filterWindow,
|
|
23715
|
+
filterText: _runListState.filterText,
|
|
23716
|
+
}));
|
|
23717
|
+
} catch (e) { /* ignore */ }
|
|
23718
|
+
}
|
|
23719
|
+
|
|
23720
|
+
function _runListApplyFilters(runs) {
|
|
23721
|
+
var now = Date.now();
|
|
23722
|
+
var windowMs = _runListState.filterWindow === '24h' ? 24 * 60 * 60 * 1000
|
|
23723
|
+
: _runListState.filterWindow === '7d' ? 7 * 24 * 60 * 60 * 1000
|
|
23724
|
+
: Infinity;
|
|
23725
|
+
var query = (_runListState.filterText || '').trim().toLowerCase();
|
|
23726
|
+
return runs.filter(function(r) {
|
|
23727
|
+
if (_runListState.filterStatus === 'failed') {
|
|
23728
|
+
if (r.status !== 'error' && r.status !== 'timeout' && r.status !== 'lost') return false;
|
|
23729
|
+
} else if (_runListState.filterStatus === 'ok') {
|
|
23730
|
+
if (r.status !== 'ok') return false;
|
|
23731
|
+
}
|
|
23732
|
+
if (query && String(r.jobName || '').toLowerCase().indexOf(query) === -1) return false;
|
|
23733
|
+
if (windowMs !== Infinity && r.startedAt) {
|
|
23734
|
+
var age = now - new Date(r.startedAt).getTime();
|
|
23735
|
+
if (age > windowMs) return false;
|
|
23736
|
+
}
|
|
23737
|
+
return true;
|
|
23738
|
+
});
|
|
23739
|
+
}
|
|
23740
|
+
|
|
23741
|
+
async function refreshRunList() {
|
|
23742
|
+
var panel = document.getElementById('panel-runs');
|
|
23743
|
+
if (!panel) return;
|
|
23744
|
+
if (!_runListState.data.length) {
|
|
23745
|
+
_runListLoadDefaultView();
|
|
23746
|
+
}
|
|
23747
|
+
panel.innerHTML = '<div class="empty-state" style="padding:24px;color:var(--text-muted)">Loading run historyβ¦</div>';
|
|
23748
|
+
try {
|
|
23749
|
+
var r = await apiFetch('/api/cron/runs?limit=200');
|
|
23750
|
+
var d = await r.json();
|
|
23751
|
+
_runListState.data = (d && d.runs) || [];
|
|
23752
|
+
} catch (e) {
|
|
23753
|
+
panel.innerHTML = '<div class="empty-state" style="padding:24px;color:var(--red)">Failed to load runs: ' + esc(String(e)) + '</div>';
|
|
23754
|
+
return;
|
|
23755
|
+
}
|
|
23756
|
+
panel.innerHTML = renderRunListBody(_runListState.data);
|
|
23757
|
+
// Update tab count badge with total runs (not filtered count β that's
|
|
23758
|
+
// shown alongside the filter chips).
|
|
23759
|
+
var tabCount = document.getElementById('build-tab-runs-count');
|
|
23760
|
+
if (tabCount) {
|
|
23761
|
+
tabCount.textContent = _runListState.data.length;
|
|
23762
|
+
tabCount.style.display = _runListState.data.length > 0 ? '' : 'none';
|
|
23763
|
+
}
|
|
23764
|
+
}
|
|
23765
|
+
|
|
23766
|
+
function renderRunListBody(allRuns) {
|
|
23767
|
+
var filtered = _runListApplyFilters(allRuns);
|
|
23768
|
+
var html = '';
|
|
23769
|
+
// Header
|
|
23770
|
+
html += '<div style="margin-bottom:18px"><h2 style="margin:0 0 4px;font-size:18px;font-weight:600;color:var(--text-primary)">Runs</h2>'
|
|
23771
|
+
+ '<div style="font-size:12px;color:var(--text-muted)">'+ filtered.length +' of '+ allRuns.length +' total runs Β· default view: <strong>Failures (last 24h)</strong></div></div>';
|
|
23772
|
+
// Filter row β saved automatically to localStorage on change.
|
|
23773
|
+
html += '<div style="display:flex;gap:10px;align-items:center;margin-bottom:14px;flex-wrap:wrap">';
|
|
23774
|
+
html += _runListChip('Status', [
|
|
23775
|
+
{ value: 'all', label: 'All' },
|
|
23776
|
+
{ value: 'ok', label: 'OK' },
|
|
23777
|
+
{ value: 'failed', label: 'Failed' },
|
|
23778
|
+
], 'filterStatus');
|
|
23779
|
+
html += _runListChip('Window', [
|
|
23780
|
+
{ value: '24h', label: 'Last 24h' },
|
|
23781
|
+
{ value: '7d', label: 'Last 7 days' },
|
|
23782
|
+
{ value: 'all', label: 'All time' },
|
|
23783
|
+
], 'filterWindow');
|
|
23784
|
+
html += '<input type="search" placeholder="Filter by task nameβ¦" value="' + esc(_runListState.filterText) + '" oninput="onRunListSearch(this.value)" style="flex:1;min-width:200px;max-width:320px;padding:6px 10px;font-size:12px;border:1px solid var(--border);border-radius:6px;background:var(--bg-secondary);color:var(--text-primary)">';
|
|
23785
|
+
html += '<button class="btn-sm" onclick="resetRunListFilters()" style="font-size:11px">Reset to default</button>';
|
|
23786
|
+
html += '</div>';
|
|
23787
|
+
if (filtered.length === 0) {
|
|
23788
|
+
html += '<div class="empty-state" style="padding:36px 24px;text-align:center;color:var(--text-muted)"><div style="font-size:14px;margin-bottom:6px">No runs match the current filter.</div><div style="font-size:12px">Try widening the time window or clearing the task-name filter.</div></div>';
|
|
23789
|
+
return html;
|
|
23790
|
+
}
|
|
23791
|
+
// Table β same shape as the Recent History list on the Tasks page,
|
|
23792
|
+
// but sortable and with a Trigger column.
|
|
23793
|
+
html += '<div style="background:var(--bg-secondary);border:1px solid var(--border);border-radius:var(--radius)">';
|
|
23794
|
+
html += '<div style="display:grid;grid-template-columns:24px 24px minmax(180px,1.2fr) 90px minmax(180px,1fr) 90px auto;gap:10px;padding:8px 14px;border-bottom:1px solid var(--border);font-size:11px;color:var(--text-muted);text-transform:uppercase;letter-spacing:0.04em;font-weight:500">'
|
|
23795
|
+
+ '<div></div><div title="Goal verdict">Goal</div><div>Task</div><div>Trigger</div><div>Started</div><div>Duration</div><div></div>'
|
|
23796
|
+
+ '</div>';
|
|
23797
|
+
for (var i = 0; i < filtered.length; i++) {
|
|
23798
|
+
var entry = filtered[i] || {};
|
|
23799
|
+
var status = entry.status || 'unknown';
|
|
23800
|
+
var statusColor, statusIcon;
|
|
23801
|
+
if (status === 'ok') { statusColor = 'var(--green)'; statusIcon = '✓'; }
|
|
23802
|
+
else if (status === 'error') { statusColor = 'var(--red)'; statusIcon = '✗'; }
|
|
23803
|
+
else if (status === 'retried') { statusColor = 'var(--yellow)'; statusIcon = '↻'; }
|
|
23804
|
+
else if (status === 'timeout') { statusColor = 'var(--yellow)'; statusIcon = '⏳'; }
|
|
23805
|
+
else if (status === 'lost') { statusColor = 'var(--red)'; statusIcon = '?'; }
|
|
23806
|
+
else if (status === 'running') { statusColor = 'var(--accent)'; statusIcon = 'β'; }
|
|
23807
|
+
else if (status === 'skipped') { statusColor = 'var(--text-muted)'; statusIcon = '−'; }
|
|
23808
|
+
else { statusColor = 'var(--text-muted)'; statusIcon = '·'; }
|
|
23809
|
+
var jobName = entry.jobName || '(unknown)';
|
|
23810
|
+
var safeName = jsStr(jobName);
|
|
23811
|
+
var startedAt = entry.startedAt ? new Date(entry.startedAt) : null;
|
|
23812
|
+
var startedLabel = startedAt ? startedAt.toLocaleString() : 'β';
|
|
23813
|
+
var durationLabel = entry.durationMs != null ? formatDurationMs(entry.durationMs) : 'β';
|
|
23814
|
+
// Trigger heuristic until we persist it for real (Phase 3.1):
|
|
23815
|
+
// 'unleashed' mode + manual cron run = manual; otherwise scheduled.
|
|
23816
|
+
// The cron-running.json sidecar already carries pid which can hint at
|
|
23817
|
+
// manual but isn't on terminal entries. Best-effort label only.
|
|
23818
|
+
var triggerLabel = entry.attempt > 1 ? 'retry' : 'scheduled';
|
|
23819
|
+
var triggerColor = 'var(--text-muted)';
|
|
23820
|
+
// Goal cell
|
|
23821
|
+
var goalCell = '<div></div>';
|
|
23822
|
+
if (entry.goalCheck) {
|
|
23823
|
+
var gc = entry.goalCheck;
|
|
23824
|
+
var gIcon = gc.status === 'pass' ? 'π―' : gc.status === 'fail' ? 'β' : gc.status === 'error' ? 'β ' : '';
|
|
23825
|
+
var gColor = gc.status === 'pass' ? 'var(--green)' : gc.status === 'fail' ? 'var(--red)' : 'var(--yellow)';
|
|
23826
|
+
var gTip = gc.evaluatorReason || (Array.isArray(gc.schemaErrors) ? gc.schemaErrors.join('; ') : gc.status);
|
|
23827
|
+
goalCell = '<div style="color:' + gColor + ';font-size:13px;line-height:18px;text-align:center" title="' + esc(gTip) + '">' + gIcon + '</div>';
|
|
23828
|
+
}
|
|
23829
|
+
var preview = '';
|
|
23830
|
+
if (status === 'error' && entry.error) {
|
|
23831
|
+
preview = '<div style="font-size:11px;color:var(--red);margin-top:2px;word-break:break-word">' + esc(String(entry.error).slice(0, 140)) + '</div>';
|
|
23832
|
+
} else if (entry.outputPreview) {
|
|
23833
|
+
preview = '<div style="font-size:11px;color:var(--text-muted);margin-top:2px;word-break:break-word">' + esc(String(entry.outputPreview).slice(0, 120)) + '</div>';
|
|
23834
|
+
}
|
|
23835
|
+
html += '<div class="history-row" data-trace-job="' + esc(jobName) + '" style="display:grid;grid-template-columns:24px 24px minmax(180px,1.2fr) 90px minmax(180px,1fr) 90px auto;gap:10px;align-items:start;padding:8px 14px;border-bottom:1px solid var(--border);cursor:pointer">'
|
|
23836
|
+
+ '<div style="color:' + statusColor + ';font-size:14px;line-height:18px;text-align:center" title="' + esc(status) + '">' + statusIcon + '</div>'
|
|
23837
|
+
+ goalCell
|
|
23838
|
+
+ '<div style="min-width:0">'
|
|
23839
|
+
+ '<div style="font-weight:500;color:var(--text-primary);font-size:13px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="' + esc(jobName) + '">' + esc(jobName) + (entry.attempt > 1 ? ' Β· attempt ' + esc(entry.attempt) : '') + '</div>'
|
|
23840
|
+
+ preview
|
|
23841
|
+
+ '</div>'
|
|
23842
|
+
+ '<div style="font-size:11px;color:' + triggerColor + ';line-height:18px">' + esc(triggerLabel) + '</div>'
|
|
23843
|
+
+ '<div style="font-size:12px;color:var(--text-secondary);line-height:18px">' + esc(startedLabel) + '</div>'
|
|
23844
|
+
+ '<div style="font-size:12px;color:var(--text-muted);line-height:18px">' + esc(durationLabel) + '</div>'
|
|
23845
|
+
+ '<div style="display:flex;gap:6px;align-items:center"><button class="btn-sm" onclick="event.stopPropagation();openTraceViewer(\\x27' + safeName + '\\x27)" style="font-size:11px;padding:3px 8px">Trace</button></div>'
|
|
23846
|
+
+ '</div>';
|
|
23847
|
+
}
|
|
23848
|
+
html += '</div>';
|
|
23849
|
+
return html;
|
|
23850
|
+
}
|
|
23851
|
+
|
|
23852
|
+
function _runListChip(label, options, stateKey) {
|
|
23853
|
+
var current = _runListState[stateKey];
|
|
23854
|
+
var html = '<span style="display:inline-flex;align-items:center;gap:4px">';
|
|
23855
|
+
html += '<span style="font-size:11px;color:var(--text-muted);font-weight:500;text-transform:uppercase;letter-spacing:0.04em;margin-right:2px">' + esc(label) + '</span>';
|
|
23856
|
+
for (var i = 0; i < options.length; i++) {
|
|
23857
|
+
var o = options[i];
|
|
23858
|
+
var active = o.value === current;
|
|
23859
|
+
var bg = active ? 'var(--accent)' : 'var(--bg-secondary)';
|
|
23860
|
+
var fg = active ? '#fff' : 'var(--text-primary)';
|
|
23861
|
+
html += '<button class="btn-sm" onclick="onRunListChipClick(\\x27' + jsStr(stateKey) + '\\x27,\\x27' + jsStr(o.value) + '\\x27)" style="font-size:11px;padding:4px 10px;background:' + bg + ';color:' + fg + ';border:1px solid var(--border);border-radius:999px">' + esc(o.label) + '</button>';
|
|
23862
|
+
}
|
|
23863
|
+
html += '</span>';
|
|
23864
|
+
return html;
|
|
23865
|
+
}
|
|
23866
|
+
|
|
23867
|
+
function onRunListChipClick(key, value) {
|
|
23868
|
+
_runListState[key] = value;
|
|
23869
|
+
_runListSaveView();
|
|
23870
|
+
var panel = document.getElementById('panel-runs');
|
|
23871
|
+
if (panel) panel.innerHTML = renderRunListBody(_runListState.data);
|
|
23872
|
+
}
|
|
23873
|
+
|
|
23874
|
+
function onRunListSearch(value) {
|
|
23875
|
+
_runListState.filterText = value;
|
|
23876
|
+
_runListSaveView();
|
|
23877
|
+
// Debounce-by-render: just re-render. Filtering is in-memory + cheap.
|
|
23878
|
+
var panel = document.getElementById('panel-runs');
|
|
23879
|
+
if (panel) panel.innerHTML = renderRunListBody(_runListState.data);
|
|
23880
|
+
}
|
|
23881
|
+
|
|
23882
|
+
function resetRunListFilters() {
|
|
23883
|
+
_runListState.filterStatus = 'failed';
|
|
23884
|
+
_runListState.filterWindow = '24h';
|
|
23885
|
+
_runListState.filterText = '';
|
|
23886
|
+
_runListSaveView();
|
|
23887
|
+
var panel = document.getElementById('panel-runs');
|
|
23888
|
+
if (panel) panel.innerHTML = renderRunListBody(_runListState.data);
|
|
23889
|
+
}
|
|
23890
|
+
|
|
23891
|
+
// Wire the panel's click handler so clicking anywhere on a row opens the
|
|
23892
|
+
// trace viewer (the row's data-trace-job attribute is what the existing
|
|
23893
|
+
// global panel-cron click handler reads).
|
|
23894
|
+
function _runListAttachClickHandler() {
|
|
23895
|
+
var pane = document.getElementById('build-tab-runs');
|
|
23896
|
+
if (!pane || pane._handlerAttached) return;
|
|
23897
|
+
pane.addEventListener('click', function(ev) {
|
|
23898
|
+
var t = ev.target;
|
|
23899
|
+
while (t && t !== pane) {
|
|
23900
|
+
if (t.dataset && t.dataset.traceJob) {
|
|
23901
|
+
openTraceViewer(t.dataset.traceJob);
|
|
23902
|
+
return;
|
|
23903
|
+
}
|
|
23904
|
+
t = t.parentElement;
|
|
23905
|
+
}
|
|
23906
|
+
});
|
|
23907
|
+
pane._handlerAttached = true;
|
|
23908
|
+
}
|
|
23909
|
+
// Attach once on first DOM ready β runs idempotent thanks to the flag.
|
|
23910
|
+
if (typeof document !== 'undefined') {
|
|
23911
|
+
if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
|
23912
|
+
setTimeout(_runListAttachClickHandler, 0);
|
|
23913
|
+
} else {
|
|
23914
|
+
document.addEventListener('DOMContentLoaded', _runListAttachClickHandler);
|
|
23915
|
+
}
|
|
23916
|
+
}
|
|
23917
|
+
|
|
23656
23918
|
// ββ PRD Phase 2: Tools & MCP catalog ββββββββββββββββββββββββββββββββββ
|
|
23657
23919
|
// Read-only foundation in 1.18.81. Renders the four-card taxonomy:
|
|
23658
23920
|
// β’ Built-in β Claude SDK native tools (Read/Write/Bash/etc.)
|
|
@@ -35595,6 +35857,14 @@ try {
|
|
|
35595
35857
|
if (evt.type === 'cron_complete' && evt.data && evt.data.job && typeof handleCronRunOnceComplete === 'function') {
|
|
35596
35858
|
try { handleCronRunOnceComplete(evt.data.job); } catch (err) { /* non-fatal */ }
|
|
35597
35859
|
}
|
|
35860
|
+
// PRD Phase 3: if the Runs tab is visible, refresh it too so a new
|
|
35861
|
+
// run appears at the top without a manual reload.
|
|
35862
|
+
if (currentPage === 'build' && typeof refreshRunList === 'function') {
|
|
35863
|
+
var runsPane = document.getElementById('build-tab-runs');
|
|
35864
|
+
if (runsPane && runsPane.style.display !== 'none') {
|
|
35865
|
+
try { refreshRunList(); } catch (err) { /* non-fatal */ }
|
|
35866
|
+
}
|
|
35867
|
+
}
|
|
35598
35868
|
}
|
|
35599
35869
|
// A delete on one tab should drop the card from every open dashboard
|
|
35600
35870
|
// without waiting for the next poll. cron_toggled is similar but lighter.
|