@yemi33/minions 0.1.2109 → 0.1.2111
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/dashboard/js/command-center.js +36 -36
- package/dashboard/js/detail-panel.js +12 -12
- package/dashboard/js/fre.js +9 -9
- package/dashboard/js/live-stream.js +2 -2
- package/dashboard/js/modal-qa.js +17 -17
- package/dashboard/js/qa.js +3 -3
- package/dashboard/js/refresh.js +8 -8
- package/dashboard/js/render-agents.js +12 -12
- package/dashboard/js/render-dispatch.js +8 -8
- package/dashboard/js/render-inbox.js +5 -5
- package/dashboard/js/render-managed.js +16 -16
- package/dashboard/js/render-meetings.js +36 -36
- package/dashboard/js/render-other.js +23 -23
- package/dashboard/js/render-pinned.js +1 -1
- package/dashboard/js/render-pipelines.js +36 -36
- package/dashboard/js/render-plans.js +23 -23
- package/dashboard/js/render-prd.js +100 -100
- package/dashboard/js/render-prs.js +10 -10
- package/dashboard/js/render-schedules.js +23 -23
- package/dashboard/js/render-skills.js +7 -7
- package/dashboard/js/render-utils.js +22 -22
- package/dashboard/js/render-watches.js +37 -37
- package/dashboard/js/render-work-items.js +34 -34
- package/dashboard/js/utils.js +8 -8
- package/dashboard/layout.html +20 -20
- package/dashboard/pages/engine.html +4 -4
- package/dashboard/pages/home.html +4 -4
- package/dashboard/pages/inbox.html +3 -3
- package/dashboard/pages/meetings.html +2 -2
- package/dashboard/pages/pipelines.html +2 -2
- package/dashboard/pages/plans.html +3 -3
- package/dashboard/pages/prs.html +2 -2
- package/dashboard/pages/schedule.html +2 -2
- package/dashboard/pages/tools.html +1 -1
- package/dashboard/pages/watches.html +2 -2
- package/dashboard/pages/work.html +3 -3
- package/dashboard/styles.css +38 -38
- package/docs/onboarding.md +2 -2
- package/engine.js +52 -6
- package/package.json +1 -1
|
@@ -32,7 +32,7 @@ function _runtimeTagHtml(runtime) {
|
|
|
32
32
|
}
|
|
33
33
|
// Unknown runtime — fall back to a small text pill so the user still sees something
|
|
34
34
|
const fallback = runtime || 'unknown';
|
|
35
|
-
return '<span class="agent-runtime-tag" title="Runtime: ' + escapeHtml(fallback) + '" style="font-size:
|
|
35
|
+
return '<span class="agent-runtime-tag" title="Runtime: ' + escapeHtml(fallback) + '" style="font-size:var(--text-xs);font-weight:600;letter-spacing:0.4px;text-transform:uppercase;padding:1px 5px;margin-left:6px;border:1px solid var(--muted);border-radius:3px;color:var(--muted);background:transparent">' + escapeHtml(fallback) + '</span>';
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
// W-mpmwxk4y00053271 — compact text chip showing the resolved model next to
|
|
@@ -41,7 +41,7 @@ function _runtimeTagHtml(runtime) {
|
|
|
41
41
|
// "The model is really critical piece of information to me as a user."
|
|
42
42
|
function _modelChipHtml(model) {
|
|
43
43
|
if (!model || typeof model !== 'string') return '';
|
|
44
|
-
return '<span class="agent-model-tag" title="Model: ' + escapeHtml(model) + '" style="font-size:
|
|
44
|
+
return '<span class="agent-model-tag" title="Model: ' + escapeHtml(model) + '" style="font-size:var(--text-xs);font-weight:600;letter-spacing:0.4px;padding:1px 5px;margin-left:4px;border:1px solid var(--muted);border-radius:3px;color:var(--muted);background:transparent">' + escapeHtml(model) + '</span>';
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
function renderAgents(agents) {
|
|
@@ -59,12 +59,12 @@ function renderAgents(agents) {
|
|
|
59
59
|
<div class="agent-action" title="${escapeHtml(a.lastAction)}">${escapeHtml(a.lastAction)}</div>
|
|
60
60
|
${(function() {
|
|
61
61
|
var s = a.started_at, c = a.completed_at;
|
|
62
|
-
if (s && c) { var d = new Date(c) - new Date(s); if (d > 0) { var sec = Math.floor(d/1000)%60, min = Math.floor(d/60000)%60, hr = Math.floor(d/3600000); return '<div style="font-size:
|
|
63
|
-
if (s && a.status === 'working') return '<div class="agent-runtime-tick" data-started="' + s + '" style="font-size:
|
|
62
|
+
if (s && c) { var d = new Date(c) - new Date(s); if (d > 0) { var sec = Math.floor(d/1000)%60, min = Math.floor(d/60000)%60, hr = Math.floor(d/3600000); return '<div style="font-size:var(--text-xs);color:var(--muted)">Last run: ' + (hr > 0 ? hr + 'h ' : '') + min + 'm ' + sec + 's</div>'; } }
|
|
63
|
+
if (s && a.status === 'working') return '<div class="agent-runtime-tick" data-started="' + s + '" style="font-size:var(--text-xs);color:var(--yellow)"></div>';
|
|
64
64
|
return '';
|
|
65
65
|
})()}
|
|
66
|
-
${a._blockingToolCall ? `<div style="margin-top:4px;padding:4px 8px;background:rgba(130,160,210,0.13);border:1px solid rgba(130,160,210,0.3);border-radius:4px;font-size:
|
|
67
|
-
${a._warning ? `<div style="margin-top:4px;padding:4px 8px;background:rgba(210,153,34,0.15);border:1px solid rgba(210,153,34,0.3);border-radius:4px;font-size:
|
|
66
|
+
${a._blockingToolCall ? `<div style="margin-top:4px;padding:4px 8px;background:rgba(130,160,210,0.13);border:1px solid rgba(130,160,210,0.3);border-radius:4px;font-size:var(--text-sm);color:var(--muted)">⏳ Blocking tool call (${escapeHtml(a._blockingToolCall.tool)}) — silent ${Math.round(a._blockingToolCall.silentMs/60000)}min, timeout in ${Math.round(a._blockingToolCall.remainingMs/60000)}min</div>` : ''}
|
|
67
|
+
${a._warning ? `<div style="margin-top:4px;padding:4px 8px;background:rgba(210,153,34,0.15);border:1px solid rgba(210,153,34,0.3);border-radius:4px;font-size:var(--text-sm);color:var(--yellow)">⚠ ${escapeHtml(a._warning)}</div>` : ''}
|
|
68
68
|
${a.resultSummary ? `<div class="agent-result" title="${escapeHtml(a.resultSummary)}">${renderMd(a.resultSummary.slice(0, 200))}${a.resultSummary.length > 200 ? '...' : ''}</div>` : ''}
|
|
69
69
|
</div>
|
|
70
70
|
`).join('');
|
|
@@ -99,7 +99,7 @@ async function openAgentDetail(id) {
|
|
|
99
99
|
runtimeSpan.innerHTML = runtimeMeta.svg.replace('width="13"', 'width="18"').replace('height="13"', 'height="18"');
|
|
100
100
|
runtimeSpan.setAttribute('aria-label', runtimeMeta.label + ' runtime');
|
|
101
101
|
} else {
|
|
102
|
-
runtimeSpan.style.cssText += ';font-size:
|
|
102
|
+
runtimeSpan.style.cssText += ';font-size:var(--text-sm);font-weight:600;letter-spacing:0.4px;text-transform:uppercase;padding:2px 6px;border:1px solid var(--muted);border-radius:3px;color:var(--muted)';
|
|
103
103
|
runtimeSpan.textContent = agent.runtime || 'unknown';
|
|
104
104
|
}
|
|
105
105
|
// W-mpmwxk4y00053271 — mirror the model chip the card shows so the detail
|
|
@@ -109,7 +109,7 @@ async function openAgentDetail(id) {
|
|
|
109
109
|
? (() => {
|
|
110
110
|
const s = document.createElement('span');
|
|
111
111
|
s.title = 'Model: ' + agent.model;
|
|
112
|
-
s.style.cssText = 'display:inline-block;margin-left:6px;font-size:
|
|
112
|
+
s.style.cssText = 'display:inline-block;margin-left:6px;font-size:var(--text-sm);font-weight:600;letter-spacing:0.4px;padding:2px 6px;border:1px solid var(--muted);border-radius:3px;color:var(--muted)';
|
|
113
113
|
s.textContent = agent.model;
|
|
114
114
|
return s;
|
|
115
115
|
})()
|
|
@@ -127,8 +127,8 @@ async function openAgentDetail(id) {
|
|
|
127
127
|
document.getElementById('detail-status-line').innerHTML =
|
|
128
128
|
'<span class="status-badge ' + badgeClass + '">' + agent.status.toUpperCase() + '</span> ' +
|
|
129
129
|
'<span style="color:var(--muted)">' + escapeHtml(agent.lastAction) + '</span>' +
|
|
130
|
-
(agent._blockingToolCall ? '<div style="margin-top:4px;padding:4px 8px;background:rgba(130,160,210,0.13);border:1px solid rgba(130,160,210,0.3);border-radius:4px;font-size:
|
|
131
|
-
(agent.resultSummary ? '<div style="margin-top:4px;font-size:
|
|
130
|
+
(agent._blockingToolCall ? '<div style="margin-top:4px;padding:4px 8px;background:rgba(130,160,210,0.13);border:1px solid rgba(130,160,210,0.3);border-radius:4px;font-size:var(--text-base);color:var(--muted)">⏳ Blocking tool call (' + escapeHtml(agent._blockingToolCall.tool) + ') — silent ' + Math.round(agent._blockingToolCall.silentMs/60000) + 'min, timeout in ' + Math.round(agent._blockingToolCall.remainingMs/60000) + 'min</div>' : '') +
|
|
131
|
+
(agent.resultSummary ? '<div style="margin-top:4px;font-size:var(--text-base);color:var(--text);line-height:1.4">' + renderMd(agent.resultSummary.slice(0, 300)) + '</div>' : '');
|
|
132
132
|
|
|
133
133
|
// Show panel immediately with loading state — don't wait for API
|
|
134
134
|
document.getElementById('detail-content').innerHTML = '<div style="padding:24px;text-align:center;color:var(--muted)">Loading...</div>';
|
|
@@ -143,8 +143,8 @@ async function openAgentDetail(id) {
|
|
|
143
143
|
document.getElementById('detail-content').innerHTML =
|
|
144
144
|
'<div style="padding:24px;text-align:center">' +
|
|
145
145
|
'<div style="color:var(--red);margin-bottom:12px">Error loading agent detail: ' + escapeHtml(e.message) + '</div>' +
|
|
146
|
-
'<button data-agent-id="' + escapeHtml(id) + '" onclick="openAgentDetail(this.dataset.agentId)" style="padding:6px 16px;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer;font-size:
|
|
147
|
-
' <button onclick="closeDetail()" style="padding:6px 16px;background:var(--surface2);border:1px solid var(--border);border-radius:var(--radius-sm);cursor:pointer;font-size:
|
|
146
|
+
'<button data-agent-id="' + escapeHtml(id) + '" onclick="openAgentDetail(this.dataset.agentId)" style="padding:6px 16px;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer;font-size:var(--text-md)">Retry</button>' +
|
|
147
|
+
' <button onclick="closeDetail()" style="padding:6px 16px;background:var(--surface2);border:1px solid var(--border);border-radius:var(--radius-sm);cursor:pointer;font-size:var(--text-md);color:var(--text)">Close</button>' +
|
|
148
148
|
'</div>';
|
|
149
149
|
}
|
|
150
150
|
|
|
@@ -272,21 +272,21 @@ function renderDispatch(dispatch, opts) {
|
|
|
272
272
|
const activeEl = document.getElementById('dispatch-active');
|
|
273
273
|
if ((dispatch.active || []).length > 0) {
|
|
274
274
|
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; all user data wrapped in escHtml() (fields: dispatch type, agent, task)
|
|
275
|
-
activeEl.innerHTML = '<div style="font-size:
|
|
275
|
+
activeEl.innerHTML = '<div style="font-size:var(--text-base);color:var(--green);margin-bottom:6px;font-weight:600">ACTIVE</div><div class="dispatch-list">' +
|
|
276
276
|
dispatch.active.map(d => dispatchItemHtml(d,
|
|
277
277
|
'<span class="dispatch-time">' + shortTime(d.started_at) + '</span>'
|
|
278
278
|
)).join('') + '</div>';
|
|
279
279
|
} else {
|
|
280
|
-
activeEl.innerHTML = '<div style="color:var(--muted);font-size:
|
|
280
|
+
activeEl.innerHTML = '<div style="color:var(--muted);font-size:var(--text-base);margin-bottom:8px">No active dispatches</div>';
|
|
281
281
|
}
|
|
282
282
|
|
|
283
283
|
// Pending
|
|
284
284
|
const pendingEl = document.getElementById('dispatch-pending');
|
|
285
285
|
if ((dispatch.pending || []).length > 0) {
|
|
286
286
|
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; all user data wrapped in escHtml() (fields: dispatch type, agent, task, skip reason)
|
|
287
|
-
pendingEl.innerHTML = '<div style="font-size:
|
|
287
|
+
pendingEl.innerHTML = '<div style="font-size:var(--text-base);color:var(--yellow);margin:8px 0 6px;font-weight:600">PENDING</div><div class="dispatch-list">' +
|
|
288
288
|
dispatch.pending.map(d => dispatchItemHtml(d,
|
|
289
|
-
d.skipReason ? '<span style="font-size:
|
|
289
|
+
d.skipReason ? '<span style="font-size:var(--text-xs);color:var(--muted);margin-left:6px" title="' + escHtml(d.skipReason) + '">' + escHtml(d.skipReason.replace(/_/g, ' ')) + '</span>' : ''
|
|
290
290
|
)).join('') + '</div>';
|
|
291
291
|
} else {
|
|
292
292
|
pendingEl.innerHTML = '';
|
|
@@ -317,7 +317,7 @@ function renderDispatch(dispatch, opts) {
|
|
|
317
317
|
? ' <button class="error-details-btn" data-agent="' + escHtml(agentId) + '" data-reason="' + escHtml(d.reason || 'No reason recorded') + '" data-task="' + escHtml((d.task || '').slice(0, 100)) + '" onclick="showErrorDetails(this.dataset.agent, this.dataset.reason, this.dataset.task)" title="View error details">details</button>'
|
|
318
318
|
: '';
|
|
319
319
|
return '<tr>' +
|
|
320
|
-
'<td style="font-family:Consolas;font-size:
|
|
320
|
+
'<td style="font-family:Consolas;font-size:var(--text-sm)" title="' + escHtml(d.id || '') + '">' + escHtml(d.id || '') + '</td>' +
|
|
321
321
|
'<td><span class="dispatch-type ' + (d.type || '') + '">' + escHtml(d.type || '') + '</span></td>' +
|
|
322
322
|
'<td>' + escHtml(d.agentName || d.agent || '') + '</td>' +
|
|
323
323
|
'<td style="max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + escHtml((d.task || '').slice(0, 60)) + '</td>' +
|
|
@@ -452,7 +452,7 @@ function renderVersionBanner(version) {
|
|
|
452
452
|
|
|
453
453
|
const v = version.dashboardRunning || version.running || version.disk || '?';
|
|
454
454
|
const commitLabel = version.dashboardRunningCommit ? ' (' + version.dashboardRunningCommit + ')' : '';
|
|
455
|
-
const warnStyle = 'font-size:
|
|
455
|
+
const warnStyle = 'font-size:var(--text-xs);padding:2px 8px;background:rgba(210,153,34,0.15);border:1px solid rgba(210,153,34,0.3);border-radius:4px;color:var(--yellow);cursor:help';
|
|
456
456
|
|
|
457
457
|
// During the post-restart grace window the old engine's reported codeVersion
|
|
458
458
|
// can still be in the cached payload — silently swallow the engineStale flag
|
|
@@ -474,11 +474,11 @@ function renderVersionBanner(version) {
|
|
|
474
474
|
el.textContent = '\u26A0 Dashboard running v' + (version.dashboardRunning || '?') + ' — disk has v' + (version.disk || '?') + '. Run: minions restart';
|
|
475
475
|
el.title = 'The dashboard process is running older code. Run: minions restart';
|
|
476
476
|
} else if (version.updateAvailable) {
|
|
477
|
-
el.style.cssText = 'font-size:
|
|
477
|
+
el.style.cssText = 'font-size:var(--text-xs);padding:2px 8px;background:rgba(63,185,80,0.1);border:1px solid rgba(63,185,80,0.3);border-radius:4px;color:var(--green);cursor:help';
|
|
478
478
|
el.textContent = 'v' + v + commitLabel + ' — v' + version.latest + ' available. Run: minions update or npm install -g @yemi33/minions@latest';
|
|
479
479
|
el.title = 'A newer version is available on npm. Run minions update to upgrade and restart.';
|
|
480
480
|
} else {
|
|
481
|
-
el.style.cssText = 'font-size:
|
|
481
|
+
el.style.cssText = 'font-size:var(--text-xs);color:var(--muted)';
|
|
482
482
|
el.textContent = 'v' + v + commitLabel;
|
|
483
483
|
el.title = 'Minions v' + v + (version.latest ? ' (latest)' : '');
|
|
484
484
|
}
|
|
@@ -111,9 +111,9 @@ function renderInbox(inbox, opts) {
|
|
|
111
111
|
<div class="inbox-preview" onclick="openModal(${idx})" style="cursor:pointer">${escapeHtml(item.content.slice(0,200))}</div>
|
|
112
112
|
<div style="display:flex;gap:6px;margin-top:6px;align-items:center">
|
|
113
113
|
${pinButton(pk, pinned, 'inbox')}
|
|
114
|
-
<button class="pr-pager-btn" style="font-size:
|
|
115
|
-
<button class="pr-pager-btn" style="font-size:
|
|
116
|
-
<button class="pr-pager-btn" style="font-size:
|
|
114
|
+
<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px" data-inbox-name="${escapeHtml(item.name)}" onclick="event.stopPropagation();promoteToKB(this.dataset.inboxName)">Add to Knowledge Base</button>
|
|
115
|
+
<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px" data-inbox-name="${escapeHtml(item.name)}" onclick="event.stopPropagation();openInboxInExplorer(this.dataset.inboxName)">Open in Explorer</button>
|
|
116
|
+
<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px;color:var(--red)" data-inbox-name="${escapeHtml(item.name)}" onclick="event.stopPropagation();deleteInboxItem(this.dataset.inboxName)">Delete</button>
|
|
117
117
|
</div>
|
|
118
118
|
</div>`;
|
|
119
119
|
}).join('');
|
|
@@ -137,10 +137,10 @@ function promoteToKB(name) {
|
|
|
137
137
|
{ id: 'reviews', label: 'Reviews' },
|
|
138
138
|
];
|
|
139
139
|
const picker = '<div style="padding:16px 20px">' +
|
|
140
|
-
'<p style="font-size:
|
|
140
|
+
'<p style="font-size:var(--text-lg);color:var(--text);margin-bottom:12px">Choose a category for <strong>' + escapeHtml(name) + '</strong>:</p>' +
|
|
141
141
|
'<div style="display:flex;flex-direction:column;gap:8px">' +
|
|
142
142
|
categories.map(c =>
|
|
143
|
-
'<button class="pr-pager-btn" style="font-size:
|
|
143
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-md);padding:8px 16px;text-align:left" onclick="doPromoteToKB(\'' + escapeHtml(name) + '\',\'' + c.id + '\')">' + c.label + '</button>'
|
|
144
144
|
).join('') +
|
|
145
145
|
'</div></div>';
|
|
146
146
|
document.getElementById('modal-title').textContent = 'Add to Knowledge Base';
|
|
@@ -87,22 +87,22 @@ function _renderManagedTable(items) {
|
|
|
87
87
|
const ttlColor = (s.ttl_expires_at && s.ttl_expires_at - Date.now() < 5 * 60 * 1000) ? 'var(--yellow)' : 'var(--muted)';
|
|
88
88
|
const nameAttr = encodeURIComponent(s.name || '');
|
|
89
89
|
return '<tr>' +
|
|
90
|
-
'<td style="padding:4px 8px;font-family:monospace;font-size:
|
|
91
|
-
'<td style="padding:4px 8px;font-family:monospace;font-size:
|
|
92
|
-
'<td style="padding:4px 8px;font-size:
|
|
93
|
-
'<td style="padding:4px 8px;font-size:
|
|
94
|
-
'<td style="padding:4px 8px;font-size:
|
|
95
|
-
'<td style="padding:4px 8px;font-size:
|
|
96
|
-
'<td style="padding:4px 8px;font-size:
|
|
90
|
+
'<td style="padding:4px 8px;font-family:monospace;font-size:var(--text-base)">' + escHtml(s.name || '') + '</td>' +
|
|
91
|
+
'<td style="padding:4px 8px;font-family:monospace;font-size:var(--text-base)">' + (s.pid || '') + '</td>' +
|
|
92
|
+
'<td style="padding:4px 8px;font-size:var(--text-base)">' + healthBadge + '</td>' +
|
|
93
|
+
'<td style="padding:4px 8px;font-size:var(--text-base);font-family:monospace">' + escHtml(ports) + '</td>' +
|
|
94
|
+
'<td style="padding:4px 8px;font-size:var(--text-sm);color:var(--muted);max-width:280px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="' + escHtml(_fmtAttrs(s.attrs).replace(/<[^>]+>/g, '')) + '">' + _fmtAttrs(s.attrs) + '</td>' +
|
|
95
|
+
'<td style="padding:4px 8px;font-size:var(--text-base);color:var(--muted)">' + escHtml(uptime) + '</td>' +
|
|
96
|
+
'<td style="padding:4px 8px;font-size:var(--text-base);color:' + ttlColor + '">' + escHtml(ttl) + '</td>' +
|
|
97
97
|
'<td style="padding:4px 8px;text-align:right;white-space:nowrap">' +
|
|
98
|
-
'<button onclick="openManagedLog(decodeURIComponent(\'' + nameAttr + '\'))" style="font-size:
|
|
99
|
-
'<button onclick="restartManagedSpec(decodeURIComponent(\'' + nameAttr + '\'))" style="font-size:
|
|
100
|
-
'<button onclick="killManagedSpec(decodeURIComponent(\'' + nameAttr + '\'))" style="font-size:
|
|
98
|
+
'<button onclick="openManagedLog(decodeURIComponent(\'' + nameAttr + '\'))" style="font-size:var(--text-sm);padding:2px 6px;margin-right:4px;border:1px solid var(--border);background:var(--surface);color:var(--fg);border-radius:3px;cursor:pointer">Log</button>' +
|
|
99
|
+
'<button onclick="restartManagedSpec(decodeURIComponent(\'' + nameAttr + '\'))" style="font-size:var(--text-sm);padding:2px 6px;margin-right:4px;border:1px solid var(--blue);background:transparent;color:var(--blue);border-radius:3px;cursor:pointer">Restart</button>' +
|
|
100
|
+
'<button onclick="killManagedSpec(decodeURIComponent(\'' + nameAttr + '\'))" style="font-size:var(--text-sm);padding:2px 6px;border:1px solid var(--red);background:transparent;color:var(--red);border-radius:3px;cursor:pointer">Kill</button>' +
|
|
101
101
|
'</td>' +
|
|
102
102
|
'</tr>';
|
|
103
103
|
}).join('');
|
|
104
104
|
return '<table style="width:100%;border-collapse:collapse;margin-bottom:8px">' +
|
|
105
|
-
'<thead><tr style="text-align:left;font-size:
|
|
105
|
+
'<thead><tr style="text-align:left;font-size:var(--text-sm);color:var(--muted);text-transform:uppercase">' +
|
|
106
106
|
'<th style="padding:4px 8px">name</th>' +
|
|
107
107
|
'<th style="padding:4px 8px">pid</th>' +
|
|
108
108
|
'<th style="padding:4px 8px">health</th>' +
|
|
@@ -165,7 +165,7 @@ async function renderManagedProcesses() {
|
|
|
165
165
|
const group = groups[proj];
|
|
166
166
|
group.sort(function (a, b) { return (a.name || '').localeCompare(b.name || ''); });
|
|
167
167
|
return '<div style="border:1px solid var(--border);border-radius:4px;padding:8px;margin-bottom:8px;background:var(--surface2)">' +
|
|
168
|
-
'<div style="font-weight:600;font-size:
|
|
168
|
+
'<div style="font-weight:600;font-size:var(--text-base);margin-bottom:6px">' + escHtml(proj) +
|
|
169
169
|
' <span style="color:var(--muted);font-weight:400">(' + group.length + ')</span>' +
|
|
170
170
|
'</div>' +
|
|
171
171
|
_renderManagedTable(group) +
|
|
@@ -235,11 +235,11 @@ function _ensureManagedLogModal() {
|
|
|
235
235
|
const tpl = '' +
|
|
236
236
|
'<div style="background:var(--bg);border:1px solid var(--border);border-radius:6px;width:100%;max-width:1100px;height:80vh;display:flex;flex-direction:column">' +
|
|
237
237
|
'<div style="display:flex;align-items:center;padding:8px 12px;border-bottom:1px solid var(--border)">' +
|
|
238
|
-
'<div id="managed-log-title" style="flex:1;font-weight:600;font-size:
|
|
239
|
-
'<div id="managed-log-status" style="font-size:
|
|
240
|
-
'<button id="managed-log-close-btn" style="font-size:
|
|
238
|
+
'<div id="managed-log-title" style="flex:1;font-weight:600;font-size:var(--text-md);font-family:monospace">Managed log</div>' +
|
|
239
|
+
'<div id="managed-log-status" style="font-size:var(--text-sm);color:var(--muted);margin-right:12px">connecting...</div>' +
|
|
240
|
+
'<button id="managed-log-close-btn" style="font-size:var(--text-sm);padding:4px 10px;border:1px solid var(--border);background:transparent;color:var(--fg);border-radius:3px;cursor:pointer">Close</button>' +
|
|
241
241
|
'</div>' +
|
|
242
|
-
'<pre id="managed-log-body" style="flex:1;overflow:auto;margin:0;padding:8px 12px;font-family:monospace;font-size:
|
|
242
|
+
'<pre id="managed-log-body" style="flex:1;overflow:auto;margin:0;padding:8px 12px;font-family:monospace;font-size:var(--text-base);white-space:pre-wrap;word-break:break-all;background:var(--surface2)"></pre>' +
|
|
243
243
|
'</div>';
|
|
244
244
|
const frag = document.createRange().createContextualFragment(tpl);
|
|
245
245
|
overlay.appendChild(frag);
|
|
@@ -89,7 +89,7 @@ function renderMeetings(meetings, opts) {
|
|
|
89
89
|
if (visible.length === 0) {
|
|
90
90
|
el.innerHTML = '<p class="empty">No active meetings.</p>';
|
|
91
91
|
// eslint-disable-next-line no-unsanitized/method -- reason: composed from internal archived count and fixed toggle markup (no user data flows in)
|
|
92
|
-
if (archived.length) el.insertAdjacentHTML('beforeend', '<div style="text-align:center;margin-top:8px"><button class="pr-pager-btn" style="font-size:
|
|
92
|
+
if (archived.length) el.insertAdjacentHTML('beforeend', '<div style="text-align:center;margin-top:8px"><button class="pr-pager-btn" style="font-size:var(--text-sm)" onclick="_toggleArchivedMeetings()">Show ' + archived.length + ' archived</button></div>');
|
|
93
93
|
_mtgTotalPages = 1;
|
|
94
94
|
restoreDashboardScrollState(el, scrollState);
|
|
95
95
|
return;
|
|
@@ -109,7 +109,7 @@ function renderMeetings(meetings, opts) {
|
|
|
109
109
|
const hasFindings = m.findings?.[p];
|
|
110
110
|
const hasDebate = m.debate?.[p];
|
|
111
111
|
const icon = m.status === 'completed' ? '✓' : m.status === 'debating' ? (hasDebate ? '✓' : (hasFindings ? '⏳' : '○')) : (hasFindings ? '✓' : '⏳');
|
|
112
|
-
return '<span style="background:var(--surface2);border:1px solid var(--border);border-radius:4px;padding:2px 6px;font-size:
|
|
112
|
+
return '<span style="background:var(--surface2);border:1px solid var(--border);border-radius:4px;padding:2px 6px;font-size:var(--text-sm)">' + icon + ' ' + escHtml(p) + '</span>';
|
|
113
113
|
}).join(' ');
|
|
114
114
|
|
|
115
115
|
const dt = m.completedAt || m.createdAt;
|
|
@@ -117,18 +117,18 @@ function renderMeetings(meetings, opts) {
|
|
|
117
117
|
|
|
118
118
|
return '<div data-file="meetings/' + escHtml(m.id) + '.json" style="background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:12px 16px;margin-bottom:8px;cursor:pointer;position:relative" onclick="if(shouldIgnoreSelectionClick(event))return;openMeetingDetail(\'' + escHtml(m.id) + '\')">' +
|
|
119
119
|
'<div style="display:flex;justify-content:space-between;align-items:center">' +
|
|
120
|
-
'<strong style="font-size:
|
|
120
|
+
'<strong style="font-size:var(--text-lg)">' + escHtml(m.title) + '</strong>' +
|
|
121
121
|
'<div style="display:flex;align-items:center;gap:8px">' +
|
|
122
|
-
'<span style="color:' + statusColor + ';font-size:
|
|
122
|
+
'<span style="color:' + statusColor + ';font-size:var(--text-base);font-weight:600">' + statusLabel + '</span>' +
|
|
123
123
|
(m.status === 'archived'
|
|
124
|
-
? '<button class="pr-pager-btn" style="font-size:
|
|
125
|
-
: (m.status === 'completed' ? '<button class="pr-pager-btn" style="font-size:
|
|
126
|
-
'<button class="pr-pager-btn" style="font-size:
|
|
124
|
+
? '<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:1px 6px" onclick="event.stopPropagation();_unarchiveMeeting(\'' + escHtml(m.id) + '\')">Unarchive</button>'
|
|
125
|
+
: (m.status === 'completed' ? '<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:1px 6px" onclick="event.stopPropagation();_archiveMeeting(\'' + escHtml(m.id) + '\')">Archive</button>' : '')) +
|
|
126
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:1px 6px;color:var(--red);border-color:var(--red)" onclick="event.stopPropagation();_deleteMeeting(\'' + escHtml(m.id) + '\')">Delete</button>' +
|
|
127
127
|
'</div>' +
|
|
128
128
|
'</div>' +
|
|
129
|
-
(timeStr ? '<div style="margin-top:4px;font-size:
|
|
129
|
+
(timeStr ? '<div style="margin-top:4px;font-size:var(--text-sm);color:var(--muted)">' + escHtml(timeStr) + '</div>' : '') +
|
|
130
130
|
'<div style="margin-top:6px;display:flex;gap:6px;flex-wrap:wrap">' + participantBadges + '</div>' +
|
|
131
|
-
'<div style="margin-top:6px;font-size:
|
|
131
|
+
'<div style="margin-top:6px;font-size:var(--text-base);color:var(--muted)">' + escHtml((m.agenda || '').slice(0, 100)) + (m.agenda?.length > 100 ? '...' : '') + '</div>' +
|
|
132
132
|
'</div>';
|
|
133
133
|
}).join('');
|
|
134
134
|
|
|
@@ -144,7 +144,7 @@ function renderMeetings(meetings, opts) {
|
|
|
144
144
|
|
|
145
145
|
if (archived.length > 0) {
|
|
146
146
|
// eslint-disable-next-line no-unsanitized/method -- reason: composed from internal archived count and fixed toggle label (no user data flows in)
|
|
147
|
-
el.insertAdjacentHTML('beforeend', '<div style="text-align:center;margin-top:8px"><button class="pr-pager-btn" style="font-size:
|
|
147
|
+
el.insertAdjacentHTML('beforeend', '<div style="text-align:center;margin-top:8px"><button class="pr-pager-btn" style="font-size:var(--text-sm)" onclick="_toggleArchivedMeetings()">' +
|
|
148
148
|
(_showArchived ? 'Hide' : 'Show') + ' ' + archived.length + ' archived</button></div>');
|
|
149
149
|
}
|
|
150
150
|
restoreDashboardScrollState(el, scrollState);
|
|
@@ -193,35 +193,35 @@ function _renderMeetingDetail(m) {
|
|
|
193
193
|
// Status bar
|
|
194
194
|
html += '<div style="display:flex;justify-content:space-between;align-items:center">' +
|
|
195
195
|
'<span style="color:' + (statusColors[m.status] || 'var(--muted)') + ';font-weight:600">' + (statusLabels[m.status] || m.status) + '</span>' +
|
|
196
|
-
'<span style="font-size:
|
|
196
|
+
'<span style="font-size:var(--text-sm);color:var(--muted)">' + escHtml(m.createdAt?.slice(0, 16).replace('T', ' ') || '') + '</span>' +
|
|
197
197
|
'</div>';
|
|
198
198
|
|
|
199
199
|
// Agenda — render markdown-like formatting
|
|
200
|
-
html += '<div style="background:var(--surface2);padding:8px 12px;border-radius:6px;font-size:
|
|
200
|
+
html += '<div style="background:var(--surface2);padding:8px 12px;border-radius:6px;font-size:var(--text-md);white-space:pre-wrap;line-height:1.6">' +
|
|
201
201
|
'<strong>Agenda:</strong>\n' + renderMd(m.agenda || '') + '</div>';
|
|
202
202
|
|
|
203
203
|
// Per-agent panels
|
|
204
204
|
for (const agent of (m.participants || [])) {
|
|
205
205
|
html += '<div style="border:1px solid var(--border);border-radius:6px;overflow:hidden">';
|
|
206
|
-
html += '<div style="background:var(--surface2);padding:6px 12px;font-weight:600;font-size:
|
|
206
|
+
html += '<div style="background:var(--surface2);padding:6px 12px;font-weight:600;font-size:var(--text-md)">' + escHtml(agent) + '</div>';
|
|
207
207
|
|
|
208
208
|
// Findings
|
|
209
209
|
if (m.findings?.[agent]) {
|
|
210
|
-
html += '<div style="padding:8px 12px;font-size:
|
|
211
|
-
'<div style="color:var(--muted);font-size:
|
|
210
|
+
html += '<div style="padding:8px 12px;font-size:var(--text-base);border-bottom:1px solid var(--border)">' +
|
|
211
|
+
'<div style="color:var(--muted);font-size:var(--text-sm);margin-bottom:4px">Round 1 — Findings</div>' +
|
|
212
212
|
'<div class="meeting-scroll-panel" style="word-break:break-word;max-height:300px;overflow-y:auto">' + renderMd(m.findings[agent].content || '') + '</div></div>';
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
// Debate
|
|
216
216
|
if (m.debate?.[agent]) {
|
|
217
|
-
html += '<div style="padding:8px 12px;font-size:
|
|
218
|
-
'<div style="color:var(--muted);font-size:
|
|
217
|
+
html += '<div style="padding:8px 12px;font-size:var(--text-base);border-bottom:1px solid var(--border)">' +
|
|
218
|
+
'<div style="color:var(--muted);font-size:var(--text-sm);margin-bottom:4px">Round 2 — Debate</div>' +
|
|
219
219
|
'<div class="meeting-scroll-panel" style="word-break:break-word;max-height:300px;overflow-y:auto">' + renderMd(m.debate[agent].content || '') + '</div></div>';
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
// Status
|
|
223
223
|
if (!m.findings?.[agent] && m.status !== 'completed') {
|
|
224
|
-
html += '<div style="padding:6px 12px;font-size:
|
|
224
|
+
html += '<div style="padding:6px 12px;font-size:var(--text-sm);color:var(--muted)">⏳ Waiting...</div>';
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
html += '</div>';
|
|
@@ -230,45 +230,45 @@ function _renderMeetingDetail(m) {
|
|
|
230
230
|
// Conclusion
|
|
231
231
|
if (m.conclusion) {
|
|
232
232
|
html += '<div style="background:rgba(63,185,80,0.08);border:1px solid var(--green);border-radius:6px;padding:10px 14px">' +
|
|
233
|
-
'<div style="color:var(--green);font-weight:600;font-size:
|
|
234
|
-
'<div class="meeting-scroll-panel" style="font-size:
|
|
233
|
+
'<div style="color:var(--green);font-weight:600;font-size:var(--text-md);margin-bottom:6px">Conclusion (by ' + escHtml(m.conclusion.agent || '?') + ')</div>' +
|
|
234
|
+
'<div class="meeting-scroll-panel" style="font-size:var(--text-md);word-break:break-word;max-height:400px;overflow-y:auto">' + renderMd(m.conclusion.content || '') + '</div></div>';
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
// Human notes
|
|
238
238
|
if (m.humanNotes?.length > 0) {
|
|
239
239
|
html += '<div style="border-top:1px solid var(--border);padding-top:8px">' +
|
|
240
|
-
'<div style="color:var(--muted);font-size:
|
|
241
|
-
m.humanNotes.map(n => '<div style="font-size:
|
|
240
|
+
'<div style="color:var(--muted);font-size:var(--text-sm);margin-bottom:4px">Human Notes</div>' +
|
|
241
|
+
m.humanNotes.map(n => '<div style="font-size:var(--text-base);margin-bottom:2px">• ' + escHtml(n) + '</div>').join('') +
|
|
242
242
|
'</div>';
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
// Actions
|
|
246
246
|
if (m.status === 'archived') {
|
|
247
247
|
html += '<div style="display:flex;gap:8px;border-top:1px solid var(--border);padding-top:8px">' +
|
|
248
|
-
'<button class="pr-pager-btn" style="font-size:
|
|
249
|
-
'<button class="pr-pager-btn" style="font-size:
|
|
248
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px" onclick="_unarchiveMeeting(\'' + escHtml(m.id) + '\')">Unarchive</button>' +
|
|
249
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px;color:var(--red);border-color:var(--red)" onclick="_deleteMeeting(\'' + escHtml(m.id) + '\')">Delete</button>' +
|
|
250
250
|
'</div>';
|
|
251
251
|
} else if (m.status === 'completed') {
|
|
252
252
|
const linkedPlan = _findLinkedPlan(m);
|
|
253
253
|
html += '<div style="display:flex;gap:8px;flex-wrap:wrap;border-top:1px solid var(--border);padding-top:8px">' +
|
|
254
254
|
(linkedPlan
|
|
255
|
-
? '<button class="pr-pager-btn" style="font-size:
|
|
256
|
-
'<button class="pr-pager-btn" style="font-size:
|
|
257
|
-
: '<button class="pr-pager-btn" style="font-size:
|
|
258
|
-
'<button class="pr-pager-btn" style="font-size:
|
|
259
|
-
'<button class="pr-pager-btn" style="font-size:
|
|
255
|
+
? '<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px;color:var(--blue);border-color:var(--blue)" onclick="_viewPlanWithBack(\'' + escHtml(linkedPlan.file) + '\',\'' + escHtml(m.id) + '\')">View Plan</button>' +
|
|
256
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px;color:var(--green);border-color:var(--green)" onclick="_createPlanFromMeeting(\'' + escHtml(m.id) + '\',this)">New Plan</button>'
|
|
257
|
+
: '<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px;color:var(--green);border-color:var(--green)" onclick="_createPlanFromMeeting(\'' + escHtml(m.id) + '\',this)">Create Plan from Meeting</button>') +
|
|
258
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px" onclick="_archiveMeeting(\'' + escHtml(m.id) + '\')">Archive</button>' +
|
|
259
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px;color:var(--red);border-color:var(--red)" onclick="_deleteMeeting(\'' + escHtml(m.id) + '\')">Delete</button>' +
|
|
260
260
|
'</div>' +
|
|
261
261
|
'<div id="meeting-plan-toast" class="cmd-toast cmd-toast-inline" style="margin-top:6px"></div>' +
|
|
262
|
-
'<div style="font-size:
|
|
262
|
+
'<div style="font-size:var(--text-xs);color:var(--muted);margin-top:4px">Use the Q&A below to discuss action items' + (linkedPlan ? '' : ', then create a plan to execute them') + '.</div>';
|
|
263
263
|
} else {
|
|
264
264
|
html += '<div style="display:flex;gap:8px;border-top:1px solid var(--border);padding-top:8px">' +
|
|
265
|
-
'<input id="meeting-note-input" type="text" placeholder="Add context for all agents..." style="flex:1;padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text);font-size:
|
|
266
|
-
'<button onclick="_submitMeetingNote(\'' + escHtml(m.id) + '\',this)" style="padding:6px 12px;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer;font-size:
|
|
265
|
+
'<input id="meeting-note-input" type="text" placeholder="Add context for all agents..." style="flex:1;padding:6px 8px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--text);font-size:var(--text-md)" onkeydown="if(event.key===\'Enter\')_submitMeetingNote(\'' + escHtml(m.id) + '\')">' +
|
|
266
|
+
'<button onclick="_submitMeetingNote(\'' + escHtml(m.id) + '\',this)" style="padding:6px 12px;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer;font-size:var(--text-base)">Add Note</button>' +
|
|
267
267
|
'</div>' +
|
|
268
268
|
'<div style="display:flex;gap:8px;margin-top:4px">' +
|
|
269
|
-
'<button class="pr-pager-btn" style="font-size:
|
|
270
|
-
'<button class="pr-pager-btn" style="font-size:
|
|
271
|
-
'<button class="pr-pager-btn" style="font-size:
|
|
269
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px;color:var(--yellow);border-color:var(--yellow)" onclick="_advanceMeeting(\'' + escHtml(m.id) + '\',this)">Skip to Next Round</button>' +
|
|
270
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px;color:var(--red);border-color:var(--red)" onclick="_endMeeting(\'' + escHtml(m.id) + '\',this)">End Meeting</button>' +
|
|
271
|
+
'<button class="pr-pager-btn" style="font-size:var(--text-xs);padding:2px 8px;color:var(--red);border-color:var(--red)" onclick="_deleteMeeting(\'' + escHtml(m.id) + '\')">Delete</button>' +
|
|
272
272
|
'</div>';
|
|
273
273
|
}
|
|
274
274
|
|
|
@@ -465,7 +465,7 @@ function _viewPlanWithBack(file, meetingId) {
|
|
|
465
465
|
if (title && !title.querySelector('.mtg-back-btn')) {
|
|
466
466
|
const back = document.createElement('button');
|
|
467
467
|
back.className = 'pr-pager-btn mtg-back-btn';
|
|
468
|
-
back.style.cssText = 'font-size:
|
|
468
|
+
back.style.cssText = 'font-size:var(--text-xs);padding:2px 8px;margin-right:8px;vertical-align:middle';
|
|
469
469
|
back.textContent = '\u2190 Back to Meeting';
|
|
470
470
|
back.onclick = function() { openMeetingDetail(meetingId); };
|
|
471
471
|
title.prepend(back);
|
|
@@ -6,7 +6,7 @@ function renderProjects(projects) {
|
|
|
6
6
|
if (!visible.length) {
|
|
7
7
|
list.innerHTML = '<span style="color:var(--muted);font-style:italic">No projects linked.</span>' +
|
|
8
8
|
'<span onclick="addProject()" style="background:var(--surface2);border:1px solid var(--border);border-radius:4px;padding:3px 10px;color:var(--green);font-weight:500;cursor:pointer;border-style:dashed;margin-left:8px">+ Add Project</span>' +
|
|
9
|
-
'<span onclick="openScanProjectsModal()" style="background:var(--surface2);border:1px solid var(--border);border-radius:4px;padding:3px 10px;color:var(--blue);font-weight:500;cursor:pointer;border-style:dashed;font-size:
|
|
9
|
+
'<span onclick="openScanProjectsModal()" style="background:var(--surface2);border:1px solid var(--border);border-radius:4px;padding:3px 10px;color:var(--blue);font-weight:500;cursor:pointer;border-style:dashed;font-size:var(--text-sm)">Scan</span>';
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
12
|
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; all user data wrapped in escHtml() (fields: project name, path, branch metadata)
|
|
@@ -18,7 +18,7 @@ function renderProjects(projects) {
|
|
|
18
18
|
'</span>'
|
|
19
19
|
).join('') +
|
|
20
20
|
'<span onclick="addProject()" style="background:var(--surface2);border:1px solid var(--border);border-radius:4px;padding:3px 10px;color:var(--muted);font-weight:500;cursor:pointer;border-style:dashed">+ Add</span>' +
|
|
21
|
-
'<span onclick="openScanProjectsModal()" style="background:var(--surface2);border:1px solid var(--border);border-radius:4px;padding:3px 10px;color:var(--blue);font-weight:500;cursor:pointer;border-style:dashed;font-size:
|
|
21
|
+
'<span onclick="openScanProjectsModal()" style="background:var(--surface2);border:1px solid var(--border);border-radius:4px;padding:3px 10px;color:var(--blue);font-weight:500;cursor:pointer;border-style:dashed;font-size:var(--text-sm)">Scan</span>';
|
|
22
22
|
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -141,13 +141,13 @@ function renderMcpServers(servers) {
|
|
|
141
141
|
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; all user data wrapped in escHtml() (fields: server source, status, args, command, name)
|
|
142
142
|
el.innerHTML = '<div style="display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px">' +
|
|
143
143
|
servers.map(s =>
|
|
144
|
-
'<div style="font-size:
|
|
144
|
+
'<div style="font-size:var(--text-base);padding:5px 10px;background:var(--surface2);border:1px solid var(--border);border-radius:6px;color:var(--text)" title="' + escHtml([s.source, s.status, s.args || s.command].filter(Boolean).join(' · ')) + '">' +
|
|
145
145
|
escHtml(s.name) +
|
|
146
146
|
(s.source ? ' <span style="color:var(--muted)">(' + escHtml(s.source) + ')</span>' : '') +
|
|
147
147
|
'</div>'
|
|
148
148
|
).join('') +
|
|
149
149
|
'</div>' +
|
|
150
|
-
'<p style="font-size:
|
|
150
|
+
'<p style="font-size:var(--text-sm);color:var(--muted);margin:0">Mirrors <code style="color:var(--blue)">claude mcp list</code> + <code style="color:var(--blue)">copilot mcp list --json</code>, plus workspace <code>.mcp.json</code> from each registered project. Cached ~5 min.</p>';
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
function renderMetrics(metrics) {
|
|
@@ -247,7 +247,7 @@ function renderLlmPerf(metrics) {
|
|
|
247
247
|
if (perTask.length > 0) {
|
|
248
248
|
// Visual divider + caption row to distinguish per-call (above) from
|
|
249
249
|
// per-task (below) — column units differ.
|
|
250
|
-
html += '<tr><td colspan="5" style="border-top:2px solid var(--border);padding-top:6px;font-size:
|
|
250
|
+
html += '<tr><td colspan="5" style="border-top:2px solid var(--border);padding-top:6px;font-size:var(--text-sm);color:var(--muted);font-style:italic">— per agent task (calls = dispatched work items, time = wall-clock of agent process) —</td></tr>';
|
|
251
251
|
for (const [type, m] of perTask) {
|
|
252
252
|
html += renderRow(type + ' (per task)', m);
|
|
253
253
|
}
|
|
@@ -330,7 +330,7 @@ function renderTokenUsage(metrics) {
|
|
|
330
330
|
const days = Object.keys(daily).sort().slice(-14);
|
|
331
331
|
if (days.length > 1) {
|
|
332
332
|
const maxCost = Math.max(...days.map(d => daily[d].costUsd || 0), 0.01);
|
|
333
|
-
html += '<div style="font-size:
|
|
333
|
+
html += '<div style="font-size:var(--text-sm);color:var(--muted);margin:8px 0 4px">Daily Cost (last ' + days.length + ' days)</div>';
|
|
334
334
|
html += '<div class="token-chart">';
|
|
335
335
|
for (const day of days) {
|
|
336
336
|
const d = daily[day];
|
|
@@ -365,7 +365,7 @@ function renderTokenUsage(metrics) {
|
|
|
365
365
|
}
|
|
366
366
|
var agentsWithUsage = tokenRows.filter(function(a) { return (a[1].totalCostUsd || 0) > 0; });
|
|
367
367
|
if (agentsWithUsage.length > 0) {
|
|
368
|
-
html += '<div style="font-size:
|
|
368
|
+
html += '<div style="font-size:var(--text-sm);color:var(--muted);margin:12px 0 4px;font-weight:600;text-transform:uppercase;letter-spacing:0.5px">Agent Usage</div>';
|
|
369
369
|
html += '<table class="token-agent-table"><thead><tr><th style="width:20%">Agent</th><th style="width:10%">Model</th><th style="width:14%">Cost</th><th style="width:14%">Input</th><th style="width:14%">Output</th><th style="width:14%">Cache</th><th style="width:14%">$/task</th></tr></thead><tbody>';
|
|
370
370
|
for (const [id, m] of agentsWithUsage.sort((a, b) => (b[1].totalCostUsd || 0) - (a[1].totalCostUsd || 0))) {
|
|
371
371
|
const tasks = (m.tasksCompleted || 0) + (m.tasksErrored || 0);
|
|
@@ -373,7 +373,7 @@ function renderTokenUsage(metrics) {
|
|
|
373
373
|
const modelLabel = (m.model || '').replace(/^claude-/, '').replace(/\[.*\]$/, '') || '-';
|
|
374
374
|
html += '<tr>' +
|
|
375
375
|
'<td style="font-weight:600">' + escHtml(id) + '</td>' +
|
|
376
|
-
'<td style="color:var(--muted);font-size:
|
|
376
|
+
'<td style="color:var(--muted);font-size:var(--text-sm)">' + escHtml(modelLabel) + '</td>' +
|
|
377
377
|
'<td>' + fmtCost(m.totalCostUsd || 0) + '</td>' +
|
|
378
378
|
'<td>' + fmtTokens(m.totalInputTokens || 0) + '</td>' +
|
|
379
379
|
'<td>' + fmtTokens(m.totalOutputTokens || 0) + '</td>' +
|
|
@@ -388,7 +388,7 @@ function renderTokenUsage(metrics) {
|
|
|
388
388
|
const engineEntries = Object.entries(engine).filter(([, e]) => (e.costUsd || 0) > 0 || (e.calls || 0) > 0);
|
|
389
389
|
if (engineEntries.length > 0) {
|
|
390
390
|
const labels = { 'consolidation': 'Consolidation', 'command-center': 'Command Center', 'doc-chat': 'Doc Chat', 'kb-sweep': 'KB Sweep', 'schedule-parse': 'Schedule Parse' };
|
|
391
|
-
html += '<div style="font-size:
|
|
391
|
+
html += '<div style="font-size:var(--text-sm);color:var(--muted);margin:12px 0 4px;font-weight:600;text-transform:uppercase;letter-spacing:0.5px">Engine Usage</div>';
|
|
392
392
|
html += '<table class="token-agent-table"><thead><tr><th style="width:20%">Operation</th><th style="width:10%">Calls</th><th style="width:14%">Cost</th><th style="width:14%">Input</th><th style="width:14%">Output</th><th style="width:14%">Cache</th><th style="width:14%">$/call</th></tr></thead><tbody>';
|
|
393
393
|
for (const [cat, e] of engineEntries.sort((a, b) => (b[1].costUsd || 0) - (a[1].costUsd || 0))) {
|
|
394
394
|
const perCall = (e.calls || 0) > 0 ? fmtCost((e.costUsd || 0) / e.calls) : '-';
|
|
@@ -424,7 +424,7 @@ async function openScanProjectsModal() {
|
|
|
424
424
|
'</label>' +
|
|
425
425
|
'<button onclick="_runProjectScan()" style="padding:6px 16px;background:var(--blue);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer;white-space:nowrap">Scan</button>' +
|
|
426
426
|
'</div>' +
|
|
427
|
-
'<div id="scan-results" style="color:var(--muted);font-size:
|
|
427
|
+
'<div id="scan-results" style="color:var(--muted);font-size:var(--text-md)">Click Scan to find git repos in the directory.</div>' +
|
|
428
428
|
'<div class="cmd-toast" id="scan-toast" style="margin-top:0"></div>' +
|
|
429
429
|
'</div>';
|
|
430
430
|
document.getElementById('modal-body').style.whiteSpace = 'normal';
|
|
@@ -451,27 +451,27 @@ async function _runProjectScan() {
|
|
|
451
451
|
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; all user data wrapped in escHtml() (fields: scan path)
|
|
452
452
|
if (repos.length === 0) { resultsEl.innerHTML = '<span style="color:var(--muted)">No git repos found in ' + escHtml(scanPath) + '</span>'; return; }
|
|
453
453
|
|
|
454
|
-
var html = '<div style="margin-bottom:8px;font-size:
|
|
454
|
+
var html = '<div style="margin-bottom:8px;font-size:var(--text-base);color:var(--muted)">' + repos.length + ' repos found — select to add:</div>';
|
|
455
455
|
html += '<div style="max-height:400px;overflow-y:auto;display:flex;flex-direction:column;gap:4px">';
|
|
456
456
|
repos.forEach(function(r, i) {
|
|
457
457
|
var linked = r.linked;
|
|
458
|
-
var hostBadge = '<span style="font-size:
|
|
458
|
+
var hostBadge = '<span style="font-size:var(--text-xs);padding:1px 5px;border-radius:3px;background:' +
|
|
459
459
|
(r.host === 'GitHub' ? 'rgba(88,166,255,0.15);color:var(--blue)' : r.host === 'ADO' ? 'rgba(188,140,255,0.15);color:var(--purple)' : 'var(--surface2);color:var(--muted)') +
|
|
460
460
|
'">' + escHtml(r.host) + '</span>';
|
|
461
461
|
html += '<label style="display:flex;align-items:center;gap:8px;padding:6px 10px;background:var(--surface2);border:1px solid var(--border);border-radius:4px;cursor:' + (linked ? 'default' : 'pointer') + ';opacity:' + (linked ? '0.5' : '1') + '">' +
|
|
462
462
|
'<input type="checkbox" data-scan-idx="' + i + '" ' + (linked ? 'disabled checked' : '') + ' style="accent-color:var(--blue);width:16px;height:16px">' +
|
|
463
463
|
'<div style="flex:1;min-width:0">' +
|
|
464
|
-
'<div style="font-weight:600;font-size:
|
|
465
|
-
'<div style="font-size:
|
|
466
|
-
(r.description ? '<div style="font-size:
|
|
464
|
+
'<div style="font-weight:600;font-size:var(--text-md)">' + escHtml(r.name) + ' ' + hostBadge + (linked ? ' <span style="font-size:var(--text-xs);color:var(--green)">linked</span>' : '') + '</div>' +
|
|
465
|
+
'<div style="font-size:var(--text-sm);color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + escHtml(r.path) + '</div>' +
|
|
466
|
+
(r.description ? '<div style="font-size:var(--text-sm);color:var(--muted)">' + escHtml(r.description) + '</div>' : '') +
|
|
467
467
|
'</div>' +
|
|
468
468
|
'</label>';
|
|
469
469
|
});
|
|
470
470
|
html += '</div>';
|
|
471
471
|
html += '<div style="display:flex;justify-content:space-between;align-items:center;margin-top:8px">' +
|
|
472
472
|
'<div style="display:flex;gap:8px">' +
|
|
473
|
-
'<span onclick="_scanSelectAll()" style="font-size:
|
|
474
|
-
'<span onclick="_scanSelectNone()" style="font-size:
|
|
473
|
+
'<span onclick="_scanSelectAll()" style="font-size:var(--text-sm);color:var(--blue);cursor:pointer;text-decoration:underline">Select all</span>' +
|
|
474
|
+
'<span onclick="_scanSelectNone()" style="font-size:var(--text-sm);color:var(--muted);cursor:pointer;text-decoration:underline">Clear</span>' +
|
|
475
475
|
'</div>' +
|
|
476
476
|
'<button onclick="_addSelectedProjects()" style="padding:6px 16px;background:var(--green);color:#fff;border:none;border-radius:var(--radius-sm);cursor:pointer">Add Selected</button>' +
|
|
477
477
|
'</div>';
|
|
@@ -612,8 +612,8 @@ async function renderKeepProcesses() {
|
|
|
612
612
|
if (!it.valid) {
|
|
613
613
|
return '<div style="border:1px solid var(--border);border-radius:4px;padding:8px;margin-bottom:8px;background:var(--surface2)">' +
|
|
614
614
|
'<div style="color:var(--red);font-weight:600">' + escHtml(it.agentId) + ' INVALID</div>' +
|
|
615
|
-
'<div style="font-size:
|
|
616
|
-
'<div style="font-size:
|
|
615
|
+
'<div style="font-size:var(--text-base);color:var(--muted)">reason: ' + escHtml(it.reason || '?') + '</div>' +
|
|
616
|
+
'<div style="font-size:var(--text-sm);color:var(--muted)">file: ' + escHtml(it.filePath || '') + '</div>' +
|
|
617
617
|
'</div>';
|
|
618
618
|
}
|
|
619
619
|
const pidRows = (it.pids || []).map(function (p) {
|
|
@@ -622,10 +622,10 @@ async function renderKeepProcesses() {
|
|
|
622
622
|
: '<span style="color:var(--muted)">○</span> dead';
|
|
623
623
|
return '<tr>' +
|
|
624
624
|
'<td style="padding:2px 8px;font-family:monospace">' + p.pid + '</td>' +
|
|
625
|
-
'<td style="padding:2px 8px;font-size:
|
|
625
|
+
'<td style="padding:2px 8px;font-size:var(--text-base)">' + aliveBadge + '</td>' +
|
|
626
626
|
'<td style="padding:2px 8px;text-align:right">' +
|
|
627
627
|
(p.alive
|
|
628
|
-
? '<button onclick="killKeepPid(\'' + escHtml(it.agentId) + '\',' + p.pid + ')" style="font-size:
|
|
628
|
+
? '<button onclick="killKeepPid(\'' + escHtml(it.agentId) + '\',' + p.pid + ')" style="font-size:var(--text-sm);padding:2px 6px;color:var(--red);border:1px solid var(--red);background:transparent;border-radius:3px;cursor:pointer">Kill now</button>'
|
|
629
629
|
: '') +
|
|
630
630
|
'</td>' +
|
|
631
631
|
'</tr>';
|
|
@@ -635,8 +635,8 @@ async function renderKeepProcesses() {
|
|
|
635
635
|
'<div style="font-weight:600">' + escHtml(it.agentId) +
|
|
636
636
|
(it.wi_id ? ' <span style="color:var(--muted);font-weight:400">(' + escHtml(it.wi_id) + ')</span>' : '') +
|
|
637
637
|
'</div>' +
|
|
638
|
-
'<div style="font-size:
|
|
639
|
-
'<div style="font-size:
|
|
638
|
+
'<div style="font-size:var(--text-base);color:var(--muted)">' + escHtml(it.purpose || '(no purpose set)') + portsLine + '</div>' +
|
|
639
|
+
'<div style="font-size:var(--text-sm);color:var(--muted)">cwd: ' + escHtml(it.cwd || '?') +
|
|
640
640
|
' expires in ' + (it.expires_in_minutes != null ? it.expires_in_minutes : '?') + 'min age ' + (it.age_minutes != null ? it.age_minutes : '?') + 'min</div>' +
|
|
641
641
|
'<table style="margin-top:6px;width:100%;border-collapse:collapse">' + pidRows + '</table>' +
|
|
642
642
|
'</div>';
|