@yemi33/minions 0.1.2047 → 0.1.2049
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 +72 -0
- package/dashboard/js/command-history.js +2 -1
- package/dashboard/js/command-input.js +7 -3
- package/dashboard/js/command-parser.js +1 -0
- package/dashboard/js/detail-panel.js +7 -0
- package/dashboard/js/fre.js +1 -0
- package/dashboard/js/live-stream.js +2 -0
- package/dashboard/js/modal-qa.js +9 -0
- package/dashboard/js/modal.js +1 -0
- package/dashboard/js/qa.js +6 -0
- package/dashboard/js/refresh.js +3 -1
- package/dashboard/js/render-agents.js +30 -3
- package/dashboard/js/render-dispatch.js +12 -0
- package/dashboard/js/render-inbox.js +6 -0
- package/dashboard/js/render-kb.js +4 -0
- package/dashboard/js/render-managed.js +1 -0
- package/dashboard/js/render-meetings.js +6 -0
- package/dashboard/js/render-other.js +11 -0
- package/dashboard/js/render-pinned.js +2 -0
- package/dashboard/js/render-pipelines.js +5 -0
- package/dashboard/js/render-plans.js +9 -0
- package/dashboard/js/render-prd.js +8 -0
- package/dashboard/js/render-prs.js +4 -0
- package/dashboard/js/render-schedules.js +5 -0
- package/dashboard/js/render-skills.js +2 -0
- package/dashboard/js/render-watches.js +10 -0
- package/dashboard/js/render-work-items.js +9 -0
- package/dashboard/js/settings.js +10 -0
- package/dashboard/slim.html +8 -8
- package/docs/deprecated.json +7 -3
- package/engine/cleanup.js +24 -1
- package/engine/lifecycle.js +0 -1
- package/engine/queries.js +37 -5
- package/package.json +6 -3
- package/playbooks/fix.md +15 -5
- package/playbooks/shared-rules.md +29 -0
|
@@ -81,6 +81,7 @@ function _ccStripActionBlockFromText(value) {
|
|
|
81
81
|
var firstUser = legacyMessages.find(function(m) { return m.role === 'user'; });
|
|
82
82
|
if (firstUser) {
|
|
83
83
|
var tmp = document.createElement('div');
|
|
84
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: renderMd()/escHtml() produced the persisted user message HTML before this title extraction (see dashboard/js/utils.js)
|
|
84
85
|
tmp.innerHTML = firstUser.html;
|
|
85
86
|
var txt = (tmp.textContent || tmp.innerText || '').trim();
|
|
86
87
|
if (txt.length > 0) title = txt.slice(0, CC_TITLE_MAX_LENGTH);
|
|
@@ -115,6 +116,7 @@ function _ccBuildTranscript(tab) {
|
|
|
115
116
|
if (!m || (m.role !== 'user' && m.role !== 'assistant' && m.role !== 'action' && m.role !== 'system')) continue;
|
|
116
117
|
var html = typeof m.html === 'string' ? m.html : '';
|
|
117
118
|
var tmp = document.createElement('div');
|
|
119
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: renderMd()/escHtml() produced persisted message HTML before this transcript text extraction (see dashboard/js/utils.js)
|
|
118
120
|
tmp.innerHTML = html;
|
|
119
121
|
var text = (tmp.textContent || tmp.innerText || '').trim();
|
|
120
122
|
if (text) out.push({ role: m.role, text: text });
|
|
@@ -566,6 +568,7 @@ function ccSwitchTab(id) {
|
|
|
566
568
|
var restoreInterval = setInterval(function() {
|
|
567
569
|
var re = document.getElementById('cc-restore-thinking');
|
|
568
570
|
if (!re || !tab._sending || _ccActiveTabId !== tab.id) { clearInterval(restoreInterval); return; }
|
|
571
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: renderMd() and renderToolChip() escape user-controlled stream/tool fields before assembling restored stream HTML
|
|
569
572
|
re.innerHTML = _restoreStreamHtml();
|
|
570
573
|
if (el.scrollHeight - el.scrollTop - el.clientHeight < 150) el.scrollTop = el.scrollHeight;
|
|
571
574
|
}, 1000);
|
|
@@ -644,6 +647,7 @@ function ccRenderTabBar() {
|
|
|
644
647
|
}
|
|
645
648
|
html += '<div class="cc-tab cc-tab-new" draggable="false" ondragstart="event.preventDefault();event.stopPropagation();" onclick="ccNewTab()" title="New tab">+</div>';
|
|
646
649
|
html += '</div>';
|
|
650
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; tab titles are wrapped in escHtml() and tab ids are generated internally (fields: t.title)
|
|
647
651
|
bar.innerHTML = html;
|
|
648
652
|
}
|
|
649
653
|
|
|
@@ -739,6 +743,7 @@ function ccRestoreMessages() {
|
|
|
739
743
|
var thinking = document.createElement('div');
|
|
740
744
|
thinking.id = 'cc-thinking';
|
|
741
745
|
thinking.style.cssText = 'padding:8px 12px;border-radius:8px;font-size:11px;color:var(--muted);align-self:flex-start;display:flex;align-items:center;gap:8px';
|
|
746
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: composed from elapsed timer and compile-time thinking UI strings (no user data flows in)
|
|
742
747
|
thinking.innerHTML = '<span class="dot-pulse" style="display:inline-flex;gap:3px"><span style="width:4px;height:4px;background:var(--blue);border-radius:50%;animation:dotPulse 1.2s infinite"></span><span style="width:4px;height:4px;background:var(--blue);border-radius:50%;animation:dotPulse 1.2s infinite;animation-delay:0.2s"></span><span style="width:4px;height:4px;background:var(--blue);border-radius:50%;animation:dotPulse 1.2s infinite;animation-delay:0.4s"></span></span> <span id="cc-thinking-text">Still working...</span> <span id="cc-thinking-time" style="font-size:10px;color:var(--border)">' + Math.floor(elapsed / 1000) + 's</span>' +
|
|
743
748
|
' <button onclick="ccNewTab()" style="font-size:9px;padding:2px 8px;background:var(--surface2);border:1px solid var(--border);border-radius:4px;color:var(--red);cursor:pointer">Reset</button>';
|
|
744
749
|
el.appendChild(thinking);
|
|
@@ -828,6 +833,7 @@ function ccAddMessage(role, html, skipSave, targetTabId, meta) {
|
|
|
828
833
|
if (meta && meta.retryId) div.setAttribute('data-cc-retry-id', meta.retryId);
|
|
829
834
|
div.style.cssText = 'padding:8px 12px;border-radius:8px;font-size:12px;line-height:1.6;max-width:95%;' +
|
|
830
835
|
(isUser ? 'background:var(--blue);color:#fff;align-self:flex-end' : isSystem ? 'align-self:center;max-width:100%' : isAction ? 'align-self:flex-start;padding:2px 0' : 'background:var(--surface2);color:var(--text);align-self:flex-start;border:1px solid var(--border);position:relative');
|
|
836
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: renderMd()/escHtml() call sites escape user-controlled message HTML before ccAddMessage() assigns it (see dashboard/js/utils.js)
|
|
831
837
|
div.innerHTML = (isAssistant && !html.includes('color:var(--red)') && !html.includes('cc-queued-pill') ? llmCopyBtn() : '') + html;
|
|
832
838
|
var wasNearBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 150;
|
|
833
839
|
el.appendChild(div);
|
|
@@ -845,6 +851,7 @@ function ccAddMessage(role, html, skipSave, targetTabId, meta) {
|
|
|
845
851
|
// current topic, not whatever the user happened to type first.
|
|
846
852
|
if (role === 'user') {
|
|
847
853
|
var tmp = document.createElement('div');
|
|
854
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: renderMd()/escHtml() produced the saved user message HTML before this title extraction (see dashboard/js/utils.js)
|
|
848
855
|
tmp.innerHTML = html;
|
|
849
856
|
var txt = (tmp.textContent || tmp.innerText || '').trim();
|
|
850
857
|
if (txt.length > 0) {
|
|
@@ -902,6 +909,7 @@ function _renderQueueIndicator() {
|
|
|
902
909
|
var el = document.createElement('div');
|
|
903
910
|
el.className = 'cc-queue-item';
|
|
904
911
|
el.style.cssText = 'padding:8px 12px;border-radius:8px;font-size:12px;line-height:1.6;max-width:95%;align-self:flex-end;background:var(--blue);color:#fff;opacity:0.5;order:9999';
|
|
912
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; queued message text wrapped in escHtml() (fields: m.message)
|
|
905
913
|
el.innerHTML = escHtml(typeof m === 'string' ? m : m.message) + '<div style="font-size:9px;opacity:0.7;font-style:italic;margin-top:2px">queued</div>';
|
|
906
914
|
msgs.appendChild(el);
|
|
907
915
|
});
|
|
@@ -1018,6 +1026,7 @@ async function _ccDoSend(message, skipUserMsg, forceTabId, intentMetadata) {
|
|
|
1018
1026
|
html += '<div style="margin-top:6px;font-size:10px;color:var(--muted)">' + escHtml(streamStatusNote) + '</div>';
|
|
1019
1027
|
}
|
|
1020
1028
|
html += '<div style="margin-top:' + (streamedText ? '6px' : '0') + '">' + _getThinkingHtml() + '</div>';
|
|
1029
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: renderMd() and renderToolChip() escape streamed text/tool fields before assembling live stream HTML
|
|
1021
1030
|
streamDiv.innerHTML = html;
|
|
1022
1031
|
// Re-append queue indicators so they stay below the streaming content
|
|
1023
1032
|
if (activeTab._queue && activeTab._queue.length > 0) _renderQueueIndicator();
|
|
@@ -1285,6 +1294,7 @@ function ccRetryLast(tabId, retryId) {
|
|
|
1285
1294
|
if (!last) return;
|
|
1286
1295
|
// Backward-compatible fallback for retry buttons rendered before retry context existed.
|
|
1287
1296
|
var tmp = document.createElement('div');
|
|
1297
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: renderMd()/escHtml() produced the saved retry message HTML before this text extraction (see dashboard/js/utils.js)
|
|
1288
1298
|
tmp.innerHTML = last.html;
|
|
1289
1299
|
text = tmp.textContent || tmp.innerText || '';
|
|
1290
1300
|
}
|
|
@@ -1377,6 +1387,7 @@ function _ccAppendHtmlToMessage(tabOrId, messageId, html) {
|
|
|
1377
1387
|
msg.html = (msg.html || '') + html;
|
|
1378
1388
|
if (typeof _ccActiveTabId !== 'undefined' && tab.id === _ccActiveTabId && typeof document !== 'undefined') {
|
|
1379
1389
|
var el = document.getElementById(_ccMessageDomId(messageId));
|
|
1390
|
+
// eslint-disable-next-line no-unsanitized/method -- reason: structural HTML is a string literal; appended action status fragments wrap user fields in escHtml() before status.outerHTML is passed here
|
|
1380
1391
|
if (el && typeof el.insertAdjacentHTML === 'function') el.insertAdjacentHTML('beforeend', html);
|
|
1381
1392
|
}
|
|
1382
1393
|
return true;
|
|
@@ -1418,12 +1429,14 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1418
1429
|
if (action._serverExecuted) {
|
|
1419
1430
|
if (action._serverHidden) return;
|
|
1420
1431
|
if (action._serverError) {
|
|
1432
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; server action type and error wrapped in escHtml() (fields: action.type, action._serverError)
|
|
1421
1433
|
status.innerHTML = '✗ ' + escHtml(action.type) + ' failed: ' + escHtml(action._serverError);
|
|
1422
1434
|
status.style.color = 'var(--red)';
|
|
1423
1435
|
} else {
|
|
1424
1436
|
var label = action._serverId ? escHtml(action._serverId) : escHtml(action.title || action.type);
|
|
1425
1437
|
var serverActionType = action.type || '';
|
|
1426
1438
|
var successLabel = serverActionType === 'dispatch' ? 'Dispatched' : serverActionType;
|
|
1439
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; label and warning wrapped in escHtml(), success label is a normalized server action type (fields: label, action._serverWarning)
|
|
1427
1440
|
status.innerHTML = '✓ ' + escHtml(successLabel) + ': <strong>' + label + '</strong>' +
|
|
1428
1441
|
(action._serverDuplicate ? '<div style="font-size:10px;color:var(--orange);margin-top:2px">Already existed from a previous request; no duplicate work item was created.</div>' : '') +
|
|
1429
1442
|
(action._serverWarning ? '<div style="font-size:10px;color:var(--muted);margin-top:2px">' + escHtml(action._serverWarning) + '</div>' : '');
|
|
@@ -1461,6 +1474,7 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1461
1474
|
agents: action.agents || [],
|
|
1462
1475
|
});
|
|
1463
1476
|
var d = await res.json();
|
|
1477
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; dispatch id/title wrapped in escHtml() (fields: d.id, action.title)
|
|
1464
1478
|
status.innerHTML = '✓ Dispatched: <strong>' + escHtml(d.id || action.title) + '</strong>';
|
|
1465
1479
|
status.style.color = 'var(--green)';
|
|
1466
1480
|
wakeEngine();
|
|
@@ -1468,6 +1482,7 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1468
1482
|
}
|
|
1469
1483
|
case 'note': {
|
|
1470
1484
|
await _ccFetch('/api/notes', { title: action.title, what: action.content || action.description, author: 'command-center' });
|
|
1485
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; note title wrapped in escHtml() (fields: action.title)
|
|
1471
1486
|
status.innerHTML = '✓ Note saved: <strong>' + escHtml(action.title) + '</strong>';
|
|
1472
1487
|
status.style.color = 'var(--green)';
|
|
1473
1488
|
var notePageLink = document.querySelector('.sidebar-link[data-page="inbox"]');
|
|
@@ -1477,6 +1492,7 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1477
1492
|
case 'pin':
|
|
1478
1493
|
case 'pin-to-pinned': {
|
|
1479
1494
|
await _ccFetch('/api/pinned', { title: action.title, content: action.content || action.description, level: action.level || '' });
|
|
1495
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; pinned note title wrapped in escHtml() (fields: action.title)
|
|
1480
1496
|
status.innerHTML = '📌 Pinned: <strong>' + escHtml(action.title) + '</strong> — visible to all agents';
|
|
1481
1497
|
status.style.color = 'var(--green)';
|
|
1482
1498
|
break;
|
|
@@ -1484,6 +1500,7 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1484
1500
|
case 'plan': {
|
|
1485
1501
|
var branchStrategy = action.branch_strategy || action.branchStrategy || 'parallel';
|
|
1486
1502
|
await _ccFetch('/api/plan', { title: action.title, description: action.description, project: action.project, branch_strategy: branchStrategy, branchStrategy: branchStrategy });
|
|
1503
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; plan title wrapped in escHtml() (fields: action.title)
|
|
1487
1504
|
status.innerHTML = '✓ Plan queued: <strong>' + escHtml(action.title) + '</strong>';
|
|
1488
1505
|
status.style.color = 'var(--green)';
|
|
1489
1506
|
wakeEngine();
|
|
@@ -1492,6 +1509,7 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1492
1509
|
case 'cancel': {
|
|
1493
1510
|
var cancelAgent = action.agent || action.agentId || '';
|
|
1494
1511
|
await _ccFetch('/api/agents/cancel', { agent: cancelAgent, agentId: cancelAgent, task: action.task || action.cancelTask || '', reason: action.reason || 'Cancelled via command center' });
|
|
1512
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; cancel target wrapped in escHtml() (fields: cancelAgent, action.task, action.cancelTask)
|
|
1495
1513
|
status.innerHTML = '✓ Cancelled agent: <strong>' + escHtml(cancelAgent || action.task || action.cancelTask || '') + '</strong>';
|
|
1496
1514
|
status.style.color = 'var(--orange)';
|
|
1497
1515
|
break;
|
|
@@ -1500,6 +1518,7 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1500
1518
|
for (var ri = 0; ri < (action.ids || []).length; ri++) {
|
|
1501
1519
|
await _ccFetch('/api/work-items/retry', { id: action.ids[ri], source: '' });
|
|
1502
1520
|
}
|
|
1521
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; retried ids wrapped in escHtml() (fields: action.ids)
|
|
1503
1522
|
status.innerHTML = '✓ Retried: <strong>' + escHtml((action.ids || []).join(', ')) + '</strong>';
|
|
1504
1523
|
status.style.color = 'var(--green)';
|
|
1505
1524
|
wakeEngine();
|
|
@@ -1507,12 +1526,14 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1507
1526
|
}
|
|
1508
1527
|
case 'pause-plan': {
|
|
1509
1528
|
await _ccFetch('/api/plans/pause', { file: action.file });
|
|
1529
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; plan file wrapped in escHtml() (fields: action.file)
|
|
1510
1530
|
status.innerHTML = '✓ Paused plan: <strong>' + escHtml(action.file) + '</strong>';
|
|
1511
1531
|
status.style.color = 'var(--orange)';
|
|
1512
1532
|
break;
|
|
1513
1533
|
}
|
|
1514
1534
|
case 'approve-plan': {
|
|
1515
1535
|
await _ccFetch('/api/plans/approve', { file: action.file });
|
|
1536
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; plan file wrapped in escHtml() (fields: action.file)
|
|
1516
1537
|
status.innerHTML = '✓ Approved plan: <strong>' + escHtml(action.file) + '</strong>';
|
|
1517
1538
|
status.style.color = 'var(--green)';
|
|
1518
1539
|
wakeEngine();
|
|
@@ -1534,6 +1555,7 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1534
1555
|
}
|
|
1535
1556
|
await _ccFetch('/api/plans/approve', { file: action.file });
|
|
1536
1557
|
var resumeCount = (action.updates || []).length + (action.newItems || []).length;
|
|
1558
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; plan file wrapped in escHtml(), item count is numeric (fields: action.file)
|
|
1537
1559
|
status.innerHTML = '✓ Resumed plan: <strong>' + escHtml(action.file) + '</strong>' + (resumeCount > 0 ? ' (' + resumeCount + ' item(s) updated)' : '');
|
|
1538
1560
|
status.style.color = 'var(--green)';
|
|
1539
1561
|
wakeEngine();
|
|
@@ -1541,24 +1563,28 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1541
1563
|
}
|
|
1542
1564
|
case 'edit-prd-item': {
|
|
1543
1565
|
await _ccFetch('/api/prd-items/update', { source: action.source, itemId: action.itemId, name: action.name, description: action.description, priority: action.priority, estimated_complexity: action.complexity });
|
|
1566
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; PRD item id wrapped in escHtml() (fields: action.itemId)
|
|
1544
1567
|
status.innerHTML = '✓ Updated PRD item: <strong>' + escHtml(action.itemId) + '</strong>';
|
|
1545
1568
|
status.style.color = 'var(--green)';
|
|
1546
1569
|
break;
|
|
1547
1570
|
}
|
|
1548
1571
|
case 'remove-prd-item': {
|
|
1549
1572
|
await _ccFetch('/api/prd-items/remove', { source: action.source, itemId: action.itemId });
|
|
1573
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; PRD item id wrapped in escHtml() (fields: action.itemId)
|
|
1550
1574
|
status.innerHTML = '✓ Removed PRD item: <strong>' + escHtml(action.itemId) + '</strong>';
|
|
1551
1575
|
status.style.color = 'var(--orange)';
|
|
1552
1576
|
break;
|
|
1553
1577
|
}
|
|
1554
1578
|
case 'delete-work-item': {
|
|
1555
1579
|
await _ccFetch('/api/work-items/delete', { id: action.id, source: action.source || '' });
|
|
1580
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; work item id wrapped in escHtml() (fields: action.id)
|
|
1556
1581
|
status.innerHTML = '✓ Deleted work item: <strong>' + escHtml(action.id) + '</strong>';
|
|
1557
1582
|
status.style.color = 'var(--orange)';
|
|
1558
1583
|
break;
|
|
1559
1584
|
}
|
|
1560
1585
|
case 'cancel-work-item': {
|
|
1561
1586
|
await _ccFetch('/api/work-items/cancel', { id: action.id, source: action.source || '', reason: action.reason || 'cc' });
|
|
1587
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; work item id wrapped in escHtml() (fields: action.id)
|
|
1562
1588
|
status.innerHTML = '✓ Cancelled work item: <strong>' + escHtml(action.id) + '</strong>';
|
|
1563
1589
|
status.style.color = 'var(--orange)';
|
|
1564
1590
|
wakeEngine();
|
|
@@ -1579,9 +1605,11 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1579
1605
|
});
|
|
1580
1606
|
var data = await res2.json();
|
|
1581
1607
|
if (data.ok && data.edited) {
|
|
1608
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; plan file wrapped in escHtml() (fields: action.file)
|
|
1582
1609
|
status.innerHTML = '✓ Plan edited: <strong>' + escHtml(action.file) + '</strong>';
|
|
1583
1610
|
status.style.color = 'var(--green)';
|
|
1584
1611
|
} else {
|
|
1612
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: renderMd() escapes doc-chat answer text before assembling HTML (see dashboard/js/utils.js)
|
|
1585
1613
|
status.innerHTML = data.answer ? renderMd(data.answer) : '✗ Could not edit plan';
|
|
1586
1614
|
status.style.color = data.answer ? 'var(--muted)' : 'var(--red)';
|
|
1587
1615
|
}
|
|
@@ -1589,6 +1617,7 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1589
1617
|
}
|
|
1590
1618
|
case 'execute-plan': {
|
|
1591
1619
|
await _ccFetch('/api/plans/execute', { file: action.file, project: action.project || '' });
|
|
1620
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; plan file wrapped in escHtml() (fields: action.file)
|
|
1592
1621
|
status.innerHTML = '✓ Plan execution queued: <strong>' + escHtml(action.file) + '</strong>';
|
|
1593
1622
|
status.style.color = 'var(--green)';
|
|
1594
1623
|
wakeEngine();
|
|
@@ -1607,9 +1636,11 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1607
1636
|
});
|
|
1608
1637
|
var data3 = await res3.json();
|
|
1609
1638
|
if (data3.ok && data3.edited) {
|
|
1639
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; file path wrapped in escHtml() (fields: action.file)
|
|
1610
1640
|
status.innerHTML = '✓ Edited: <strong>' + escHtml(action.file) + '</strong>';
|
|
1611
1641
|
status.style.color = 'var(--green)';
|
|
1612
1642
|
} else {
|
|
1643
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: renderMd() escapes doc-chat answer text before assembling HTML (see dashboard/js/utils.js)
|
|
1613
1644
|
status.innerHTML = data3.answer ? renderMd(data3.answer) : '✗ Could not edit file';
|
|
1614
1645
|
status.style.color = data3.answer ? 'var(--muted)' : 'var(--red)';
|
|
1615
1646
|
}
|
|
@@ -1628,6 +1659,7 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1628
1659
|
})
|
|
1629
1660
|
});
|
|
1630
1661
|
if (!res4.ok) { var d4 = await res4.json().catch(function() { return {}; }); throw new Error(d4.error || 'Schedule create failed'); }
|
|
1662
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; schedule id wrapped in escHtml() and action label is a fixed branch (fields: action.id)
|
|
1631
1663
|
status.innerHTML = '✓ Schedule ' + (action._update ? 'updated' : 'created') + ': <strong>' + escHtml(action.id) + '</strong>';
|
|
1632
1664
|
status.style.color = 'var(--green)';
|
|
1633
1665
|
break;
|
|
@@ -1638,6 +1670,7 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1638
1670
|
body: JSON.stringify({ id: action.id })
|
|
1639
1671
|
});
|
|
1640
1672
|
if (!res5.ok) { var d5 = await res5.json().catch(function() { return {}; }); throw new Error(d5.error || 'Schedule delete failed'); }
|
|
1673
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; schedule id wrapped in escHtml() (fields: action.id)
|
|
1641
1674
|
status.innerHTML = '✓ Deleted schedule: <strong>' + escHtml(action.id) + '</strong>';
|
|
1642
1675
|
status.style.color = 'var(--orange)';
|
|
1643
1676
|
break;
|
|
@@ -1650,6 +1683,7 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1650
1683
|
});
|
|
1651
1684
|
if (!res6.ok) { var d6 = await res6.json().catch(function() { return {}; }); throw new Error(d6.error || 'Meeting create failed'); }
|
|
1652
1685
|
var d6r = await res6.json();
|
|
1686
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; meeting title and returned id wrapped in escHtml() (fields: action.title, d6r.id)
|
|
1653
1687
|
status.innerHTML = '✓ Meeting started: <strong>' + escHtml(action.title) + '</strong>' + (d6r.id ? ' (' + escHtml(d6r.id) + ')' : '');
|
|
1654
1688
|
status.style.color = 'var(--green)';
|
|
1655
1689
|
wakeEngine();
|
|
@@ -1663,6 +1697,7 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1663
1697
|
body: JSON.stringify(payload)
|
|
1664
1698
|
});
|
|
1665
1699
|
if (!res7.ok) { var d7 = await res7.json().catch(function() { return {}; }); throw new Error(d7.error || 'Config update failed'); }
|
|
1700
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; setting name/value wrapped in escHtml() (fields: action.setting, action.value)
|
|
1666
1701
|
status.innerHTML = '✓ Set <strong>' + escHtml(action.setting) + '</strong> = ' + escHtml(String(action.value));
|
|
1667
1702
|
status.style.color = 'var(--green)';
|
|
1668
1703
|
break;
|
|
@@ -1677,36 +1712,42 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1677
1712
|
body: JSON.stringify(body)
|
|
1678
1713
|
});
|
|
1679
1714
|
if (!res8.ok) { var d8 = await res8.json().catch(function() { return {}; }); throw new Error(d8.error || 'Pipeline update failed'); }
|
|
1715
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; pipeline id wrapped in escHtml() (fields: action.id)
|
|
1680
1716
|
status.innerHTML = '✓ Updated pipeline: <strong>' + escHtml(action.id) + '</strong>';
|
|
1681
1717
|
status.style.color = 'var(--green)';
|
|
1682
1718
|
break;
|
|
1683
1719
|
}
|
|
1684
1720
|
case 'unpin': {
|
|
1685
1721
|
await _ccFetch('/api/pinned/remove', { title: action.title });
|
|
1722
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; pinned title wrapped in escHtml() (fields: action.title)
|
|
1686
1723
|
status.innerHTML = '✓ Unpinned: <strong>' + escHtml(action.title) + '</strong>';
|
|
1687
1724
|
status.style.color = 'var(--green)';
|
|
1688
1725
|
break;
|
|
1689
1726
|
}
|
|
1690
1727
|
case 'archive-plan': {
|
|
1691
1728
|
await _ccFetch('/api/plans/archive', { file: action.file });
|
|
1729
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; plan file wrapped in escHtml() (fields: action.file)
|
|
1692
1730
|
status.innerHTML = '✓ Archived plan: <strong>' + escHtml(action.file) + '</strong>';
|
|
1693
1731
|
status.style.color = 'var(--green)';
|
|
1694
1732
|
break;
|
|
1695
1733
|
}
|
|
1696
1734
|
case 'reject-plan': {
|
|
1697
1735
|
await _ccFetch('/api/plans/reject', { file: action.file, reason: action.reason || '' });
|
|
1736
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; plan file wrapped in escHtml() (fields: action.file)
|
|
1698
1737
|
status.innerHTML = '✓ Rejected plan: <strong>' + escHtml(action.file) + '</strong>';
|
|
1699
1738
|
status.style.color = 'var(--orange)';
|
|
1700
1739
|
break;
|
|
1701
1740
|
}
|
|
1702
1741
|
case 'steer-agent': {
|
|
1703
1742
|
await _ccFetch('/api/agents/steer', { agent: action.agent, message: action.message || action.content });
|
|
1743
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; agent id wrapped in escHtml() (fields: action.agent)
|
|
1704
1744
|
status.innerHTML = '✓ Steering message sent to <strong>' + escHtml(action.agent) + '</strong>';
|
|
1705
1745
|
status.style.color = 'var(--green)';
|
|
1706
1746
|
break;
|
|
1707
1747
|
}
|
|
1708
1748
|
case 'add-meeting-note': {
|
|
1709
1749
|
await _ccFetch('/api/meetings/note', { id: action.id, note: action.note || action.content });
|
|
1750
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; meeting id wrapped in escHtml() (fields: action.id)
|
|
1710
1751
|
status.innerHTML = '✓ Note added to meeting <strong>' + escHtml(action.id) + '</strong>';
|
|
1711
1752
|
status.style.color = 'var(--green)';
|
|
1712
1753
|
break;
|
|
@@ -1717,6 +1758,7 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1717
1758
|
body: JSON.stringify({ id: action.id })
|
|
1718
1759
|
});
|
|
1719
1760
|
if (!res9.ok) { var d9 = await res9.json().catch(function() { return {}; }); throw new Error(d9.error || 'Pipeline trigger failed'); }
|
|
1761
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; pipeline id wrapped in escHtml() (fields: action.id)
|
|
1720
1762
|
status.innerHTML = '✓ Pipeline triggered: <strong>' + escHtml(action.id) + '</strong>';
|
|
1721
1763
|
status.style.color = 'var(--green)';
|
|
1722
1764
|
wakeEngine();
|
|
@@ -1725,6 +1767,7 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1725
1767
|
case 'link-pr': {
|
|
1726
1768
|
var prLinkRes = await _ccFetch('/api/pull-requests/link', { url: action.url, title: action.title || '', project: action.project || '', autoObserve: action.autoObserve !== false });
|
|
1727
1769
|
var prLinkData = await prLinkRes.json().catch(function() { return {}; });
|
|
1770
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; PR URL and server message wrapped in escHtml() (fields: action.url, prLinkData.message)
|
|
1728
1771
|
status.innerHTML = '✓ PR linked: <strong>' + escHtml(action.url) + '</strong>' +
|
|
1729
1772
|
(prLinkData.message ? '<div style="font-size:11px;color:var(--muted);margin-top:4px">' + escHtml(prLinkData.message) + '</div>' : '');
|
|
1730
1773
|
status.style.color = 'var(--green)';
|
|
@@ -1732,6 +1775,7 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1732
1775
|
}
|
|
1733
1776
|
case 'archive-meeting': {
|
|
1734
1777
|
await _ccFetch('/api/meetings/archive', { id: action.id });
|
|
1778
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; meeting id wrapped in escHtml() (fields: action.id)
|
|
1735
1779
|
status.innerHTML = '✓ Meeting archived: <strong>' + escHtml(action.id) + '</strong>';
|
|
1736
1780
|
status.style.color = 'var(--green)';
|
|
1737
1781
|
break;
|
|
@@ -1747,8 +1791,10 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1747
1791
|
var d10 = await res10.json();
|
|
1748
1792
|
var labelWarning = d10.warning ? ' <span style="color:var(--orange)">(' + escHtml(d10.warning) + ')</span>' : '';
|
|
1749
1793
|
if (d10.url) {
|
|
1794
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; issue URL, title, and warning wrapped in escHtml() (fields: d10.url, action.title, d10.warning)
|
|
1750
1795
|
status.innerHTML = '🐛 Bug filed: <a href="' + escHtml(d10.url) + '" target="_blank" style="color:var(--blue)">' + escHtml(action.title) + '</a>' + labelWarning;
|
|
1751
1796
|
} else {
|
|
1797
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; issue title and warning wrapped in escHtml(), fallback URL is static (fields: action.title, d10.warning)
|
|
1752
1798
|
status.innerHTML = '🐛 Bug filed: <strong>' + escHtml(action.title) + '</strong> — <a href="https://github.com/yemi33/minions/issues" target="_blank" style="color:var(--blue)">view issues</a>' + labelWarning;
|
|
1753
1799
|
}
|
|
1754
1800
|
status.style.color = 'var(--green)';
|
|
@@ -1757,10 +1803,12 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1757
1803
|
case 'create-pipeline': {
|
|
1758
1804
|
try {
|
|
1759
1805
|
await _ccFetch('/api/pipelines', { id: action.id, title: action.title, stages: action.stages || [], trigger: action.trigger || null, stopWhen: action.stopWhen || null, monitoredResources: action.monitoredResources || null });
|
|
1806
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; pipeline id wrapped in escHtml() (fields: action.id)
|
|
1760
1807
|
status.innerHTML = '✓ Pipeline created: <strong>' + escHtml(action.id) + '</strong>';
|
|
1761
1808
|
status.style.color = 'var(--green)';
|
|
1762
1809
|
} catch (e) {
|
|
1763
1810
|
if (e.status === 409) {
|
|
1811
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; pipeline id wrapped in escHtml() (fields: action.id)
|
|
1764
1812
|
status.innerHTML = '✓ Pipeline already exists: <strong>' + escHtml(action.id) + '</strong>';
|
|
1765
1813
|
status.style.color = 'var(--orange)';
|
|
1766
1814
|
} else {
|
|
@@ -1771,18 +1819,21 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1771
1819
|
}
|
|
1772
1820
|
case 'delete-pipeline': {
|
|
1773
1821
|
await _ccFetch('/api/pipelines/delete', { id: action.id });
|
|
1822
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; pipeline id wrapped in escHtml() (fields: action.id)
|
|
1774
1823
|
status.innerHTML = '✓ Pipeline deleted: <strong>' + escHtml(action.id) + '</strong>';
|
|
1775
1824
|
status.style.color = 'var(--green)';
|
|
1776
1825
|
break;
|
|
1777
1826
|
}
|
|
1778
1827
|
case 'abort-pipeline': {
|
|
1779
1828
|
await _ccFetch('/api/pipelines/abort', { id: action.id });
|
|
1829
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; pipeline id wrapped in escHtml() (fields: action.id)
|
|
1780
1830
|
status.innerHTML = '✓ Pipeline aborted: <strong>' + escHtml(action.id) + '</strong>';
|
|
1781
1831
|
status.style.color = 'var(--green)';
|
|
1782
1832
|
break;
|
|
1783
1833
|
}
|
|
1784
1834
|
case 'retrigger-pipeline': {
|
|
1785
1835
|
await _ccFetch('/api/pipelines/retrigger', { id: action.id });
|
|
1836
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; pipeline id wrapped in escHtml() (fields: action.id)
|
|
1786
1837
|
status.innerHTML = '✓ Pipeline retriggered: <strong>' + escHtml(action.id) + '</strong>';
|
|
1787
1838
|
status.style.color = 'var(--green)';
|
|
1788
1839
|
wakeEngine();
|
|
@@ -1790,6 +1841,7 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1790
1841
|
}
|
|
1791
1842
|
case 'advance-meeting': {
|
|
1792
1843
|
await _ccFetch('/api/meetings/advance', { id: action.id });
|
|
1844
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; meeting id wrapped in escHtml() (fields: action.id)
|
|
1793
1845
|
status.innerHTML = '✓ Meeting advanced: <strong>' + escHtml(action.id) + '</strong>';
|
|
1794
1846
|
status.style.color = 'var(--green)';
|
|
1795
1847
|
wakeEngine();
|
|
@@ -1797,18 +1849,21 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1797
1849
|
}
|
|
1798
1850
|
case 'end-meeting': {
|
|
1799
1851
|
await _ccFetch('/api/meetings/end', { id: action.id });
|
|
1852
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; meeting id wrapped in escHtml() (fields: action.id)
|
|
1800
1853
|
status.innerHTML = '✓ Meeting ended: <strong>' + escHtml(action.id) + '</strong>';
|
|
1801
1854
|
status.style.color = 'var(--green)';
|
|
1802
1855
|
break;
|
|
1803
1856
|
}
|
|
1804
1857
|
case 'delete-meeting': {
|
|
1805
1858
|
await _ccFetch('/api/meetings/delete', { id: action.id });
|
|
1859
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; meeting id wrapped in escHtml() (fields: action.id)
|
|
1806
1860
|
status.innerHTML = '✓ Meeting deleted: <strong>' + escHtml(action.id) + '</strong>';
|
|
1807
1861
|
status.style.color = 'var(--green)';
|
|
1808
1862
|
break;
|
|
1809
1863
|
}
|
|
1810
1864
|
case 'trigger-verify': {
|
|
1811
1865
|
await _ccFetch('/api/plans/trigger-verify', { file: action.file });
|
|
1866
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; plan file wrapped in escHtml() (fields: action.file)
|
|
1812
1867
|
status.innerHTML = '✓ Verification triggered for: <strong>' + escHtml(action.file) + '</strong>';
|
|
1813
1868
|
status.style.color = 'var(--green)';
|
|
1814
1869
|
wakeEngine();
|
|
@@ -1816,6 +1871,7 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1816
1871
|
}
|
|
1817
1872
|
case 'regenerate-plan': {
|
|
1818
1873
|
await _ccFetch('/api/plans/approve', { file: action.file, forceRegen: true });
|
|
1874
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; plan file wrapped in escHtml() (fields: action.file)
|
|
1819
1875
|
status.innerHTML = '✓ PRD regeneration queued: <strong>' + escHtml(action.file) + '</strong>';
|
|
1820
1876
|
status.style.color = 'var(--green)';
|
|
1821
1877
|
wakeEngine();
|
|
@@ -1823,12 +1879,14 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1823
1879
|
}
|
|
1824
1880
|
case 'unarchive-plan': {
|
|
1825
1881
|
await _ccFetch('/api/plans/unarchive', { file: action.file });
|
|
1882
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; plan file wrapped in escHtml() (fields: action.file)
|
|
1826
1883
|
status.innerHTML = '✓ Plan unarchived: <strong>' + escHtml(action.file) + '</strong>';
|
|
1827
1884
|
status.style.color = 'var(--green)';
|
|
1828
1885
|
break;
|
|
1829
1886
|
}
|
|
1830
1887
|
case 'continue-pipeline': {
|
|
1831
1888
|
await _ccFetch('/api/pipelines/continue', { id: action.id });
|
|
1889
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; pipeline id wrapped in escHtml() (fields: action.id)
|
|
1832
1890
|
status.innerHTML = '✓ Pipeline continued: <strong>' + escHtml(action.id) + '</strong>';
|
|
1833
1891
|
status.style.color = 'var(--green)';
|
|
1834
1892
|
wakeEngine();
|
|
@@ -1836,18 +1894,21 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1836
1894
|
}
|
|
1837
1895
|
case 'work-item-feedback': {
|
|
1838
1896
|
await _ccFetch('/api/work-items/feedback', { id: action.id, rating: action.rating || 'up', comment: action.comment || '' });
|
|
1897
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; feedback id and rating wrapped in escHtml() (fields: action.id, action.rating)
|
|
1839
1898
|
status.innerHTML = '✓ Feedback submitted for: <strong>' + escHtml(action.id) + '</strong> (' + escHtml(action.rating || 'up') + ')';
|
|
1840
1899
|
status.style.color = 'var(--green)';
|
|
1841
1900
|
break;
|
|
1842
1901
|
}
|
|
1843
1902
|
case 'archive-work-item': {
|
|
1844
1903
|
await _ccFetch('/api/work-items/archive', { id: action.id });
|
|
1904
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; work item id wrapped in escHtml() (fields: action.id)
|
|
1845
1905
|
status.innerHTML = '✓ Work item archived: <strong>' + escHtml(action.id) + '</strong>';
|
|
1846
1906
|
status.style.color = 'var(--green)';
|
|
1847
1907
|
break;
|
|
1848
1908
|
}
|
|
1849
1909
|
case 'reopen-work-item': {
|
|
1850
1910
|
await _ccFetch('/api/work-items/reopen', { id: action.id, project: action.project || action.source || '', description: action.description });
|
|
1911
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; work item id wrapped in escHtml() (fields: action.id)
|
|
1851
1912
|
status.innerHTML = '✓ Work item reopened: <strong>' + escHtml(action.id) + '</strong>';
|
|
1852
1913
|
status.style.color = 'var(--green)';
|
|
1853
1914
|
wakeEngine();
|
|
@@ -1855,6 +1916,7 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1855
1916
|
}
|
|
1856
1917
|
case 'reopen-prd-item': {
|
|
1857
1918
|
await _ccFetch('/api/prd-items/update', { source: action.file, itemId: action.id, status: 'updated' });
|
|
1919
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; PRD item id wrapped in escHtml() (fields: action.id)
|
|
1858
1920
|
status.innerHTML = '✓ PRD item reopened: <strong>' + escHtml(action.id) + '</strong>';
|
|
1859
1921
|
status.style.color = 'var(--green)';
|
|
1860
1922
|
wakeEngine();
|
|
@@ -1862,12 +1924,14 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1862
1924
|
}
|
|
1863
1925
|
case 'promote-to-kb': {
|
|
1864
1926
|
await _ccFetch('/api/inbox/promote-kb', { name: action.file, category: action.category || 'project-notes' });
|
|
1927
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; inbox file name wrapped in escHtml() (fields: action.file)
|
|
1865
1928
|
status.innerHTML = '✓ Promoted to KB: <strong>' + escHtml(action.file) + '</strong>';
|
|
1866
1929
|
status.style.color = 'var(--green)';
|
|
1867
1930
|
break;
|
|
1868
1931
|
}
|
|
1869
1932
|
case 'revise-plan': {
|
|
1870
1933
|
await _ccFetch('/api/plans/revise', { file: action.file, feedback: action.feedback || action.description, requestedBy: 'command-center' });
|
|
1934
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; plan file wrapped in escHtml() (fields: action.file)
|
|
1871
1935
|
status.innerHTML = '✓ Plan revision dispatched: <strong>' + escHtml(action.file) + '</strong>';
|
|
1872
1936
|
status.style.color = 'var(--green)';
|
|
1873
1937
|
wakeEngine();
|
|
@@ -1881,6 +1945,7 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1881
1945
|
}
|
|
1882
1946
|
case 'toggle-kb-pin': {
|
|
1883
1947
|
await _ccFetch('/api/kb-pins/toggle', { key: action.key });
|
|
1948
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; KB key wrapped in escHtml() (fields: action.key)
|
|
1884
1949
|
status.innerHTML = '✓ KB pin toggled: <strong>' + escHtml(action.key) + '</strong>';
|
|
1885
1950
|
status.style.color = 'var(--green)';
|
|
1886
1951
|
break;
|
|
@@ -1888,6 +1953,7 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1888
1953
|
case 'add-project': {
|
|
1889
1954
|
var projectPath = action.path || action.localPath;
|
|
1890
1955
|
await _ccFetch('/api/projects/add', { path: projectPath, localPath: projectPath, name: action.name || '', repoHost: action.repoHost || 'github', allowNonRepo: action.allowNonRepo, confirmToken: action.confirmToken });
|
|
1956
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; project name/path wrapped in escHtml() (fields: action.name, projectPath)
|
|
1891
1957
|
status.innerHTML = '✓ Project added: <strong>' + escHtml(action.name || projectPath) + '</strong>';
|
|
1892
1958
|
status.style.color = 'var(--green)';
|
|
1893
1959
|
break;
|
|
@@ -1900,12 +1966,14 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1900
1966
|
}
|
|
1901
1967
|
case 'delete-pr': {
|
|
1902
1968
|
await _ccFetch('/api/pull-requests/delete', { id: action.id, project: action.project || '' });
|
|
1969
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; PR id wrapped in escHtml() (fields: action.id)
|
|
1903
1970
|
status.innerHTML = '✓ PR unlinked: <strong>' + escHtml(action.id) + '</strong>';
|
|
1904
1971
|
status.style.color = 'var(--green)';
|
|
1905
1972
|
break;
|
|
1906
1973
|
}
|
|
1907
1974
|
case 'unarchive-meeting': {
|
|
1908
1975
|
await _ccFetch('/api/meetings/unarchive', { id: action.id });
|
|
1976
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; meeting id wrapped in escHtml() (fields: action.id)
|
|
1909
1977
|
status.innerHTML = '✓ Meeting unarchived: <strong>' + escHtml(action.id) + '</strong>';
|
|
1910
1978
|
status.style.color = 'var(--green)';
|
|
1911
1979
|
break;
|
|
@@ -1929,9 +1997,11 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1929
1997
|
summary = 'response — ' + preview + (preview.length >= 240 ? '...' : '');
|
|
1930
1998
|
}
|
|
1931
1999
|
if (summary) {
|
|
2000
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; generic action type and response summary wrapped in escHtml() (fields: action.type, summary)
|
|
1932
2001
|
status.innerHTML = '✓ ' + escHtml(action.type) + ': ' + escHtml(summary);
|
|
1933
2002
|
status.style.color = 'var(--green)';
|
|
1934
2003
|
} else {
|
|
2004
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; generic action type and endpoint wrapped in escHtml() (fields: action.type, action.endpoint)
|
|
1935
2005
|
status.innerHTML = '? <strong>' + escHtml(action.type) + '</strong> hit ' + escHtml(action.endpoint) + ' but returned no data — answer inline next time, do not invent actions for read-only queries';
|
|
1936
2006
|
status.style.color = 'var(--orange)';
|
|
1937
2007
|
}
|
|
@@ -1939,12 +2009,14 @@ async function ccExecuteAction(action, targetTabId, opts) {
|
|
|
1939
2009
|
status.innerHTML = '✗ Blocked: endpoint must be a local /api/ path';
|
|
1940
2010
|
status.style.color = 'var(--red)';
|
|
1941
2011
|
} else {
|
|
2012
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; unknown action type wrapped in escHtml() (fields: action.type)
|
|
1942
2013
|
status.innerHTML = '? Unknown action: <strong>' + escHtml(action.type) + '</strong>';
|
|
1943
2014
|
status.style.color = 'var(--muted)';
|
|
1944
2015
|
}
|
|
1945
2016
|
}
|
|
1946
2017
|
}
|
|
1947
2018
|
} catch (e) {
|
|
2019
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; action error message wrapped in escHtml() (fields: e.message)
|
|
1948
2020
|
status.innerHTML = '✗ Action failed: ' + escHtml(e.message);
|
|
1949
2021
|
status.style.color = 'var(--red)';
|
|
1950
2022
|
}
|
|
@@ -27,6 +27,7 @@ function cmdShowHistory() {
|
|
|
27
27
|
} else {
|
|
28
28
|
const intentColors = { 'work-item': 'var(--blue)', 'note': 'var(--green)', 'plan': 'var(--purple,#a855f7)' };
|
|
29
29
|
const intentLabels = { 'work-item': 'Work Item', 'note': 'Note', 'plan': 'Plan' };
|
|
30
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; all user data wrapped in escHtml() (fields: command text, intent label)
|
|
30
31
|
body.innerHTML = '<ul class="cmd-history-list">' + history.map((item, i) => {
|
|
31
32
|
const date = new Date(item.timestamp);
|
|
32
33
|
const ago = timeSinceStr(date);
|
|
@@ -36,7 +37,7 @@ function cmdShowHistory() {
|
|
|
36
37
|
'<div class="cmd-history-item-body">' +
|
|
37
38
|
'<div class="cmd-history-item-text">' + escHtml(item.text) + '</div>' +
|
|
38
39
|
'<div class="cmd-history-item-meta">' +
|
|
39
|
-
'<span class="chip" style="color:' + intentColor + '">' + intentLabel + '</span>' +
|
|
40
|
+
'<span class="chip" style="color:' + intentColor + '">' + escHtml(intentLabel) + '</span>' +
|
|
40
41
|
'<span>' + ago + '</span>' +
|
|
41
42
|
'<span>' + formatLocalDateTime(date) + '</span>' +
|
|
42
43
|
'</div>' +
|
|
@@ -18,6 +18,7 @@ function cmdUpdateHighlight() {
|
|
|
18
18
|
html = html.replace(/(![a-z]+\b)/gi, '<span class="hl-priority">$1</span>');
|
|
19
19
|
html = html.replace(/(#\S+)/g, '<span class="hl-project">$1</span>');
|
|
20
20
|
html = html.replace(/(--(?:stack|parallel)\b)/gi, '<span class="hl-flag">$1</span>');
|
|
21
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: cmdUpdateHighlight() escapes all user-controlled text before assembling fixed highlight spans
|
|
21
22
|
hl.innerHTML = html + '\n'; // trailing newline prevents layout shift
|
|
22
23
|
}
|
|
23
24
|
|
|
@@ -179,7 +180,7 @@ function cmdRenderMeta() {
|
|
|
179
180
|
for (const agentId of parsed.agents) {
|
|
180
181
|
const agent = cmdAgents.find(a => a.id === agentId);
|
|
181
182
|
if (agent) {
|
|
182
|
-
chips.push('<span class="cmd-chip agent-chip">' + agent.emoji + ' @' + agent.name + '</span>');
|
|
183
|
+
chips.push('<span class="cmd-chip agent-chip">' + escHtml(agent.emoji || '') + ' @' + escHtml(agent.name) + '</span>');
|
|
183
184
|
}
|
|
184
185
|
}
|
|
185
186
|
|
|
@@ -190,6 +191,7 @@ function cmdRenderMeta() {
|
|
|
190
191
|
chips.push('<span class="cmd-chip project-chip">#' + escHtml(parsed.project) + '</span>');
|
|
191
192
|
}
|
|
192
193
|
|
|
194
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; all user data wrapped in escHtml() (fields: project names, agent emoji, agent name)
|
|
193
195
|
el.innerHTML = chips.join('');
|
|
194
196
|
}
|
|
195
197
|
|
|
@@ -214,9 +216,10 @@ function cmdShowMentions(query) {
|
|
|
214
216
|
if (items.length === 0) { popup.classList.remove('visible'); return; }
|
|
215
217
|
|
|
216
218
|
cmdMentionIdx = 0;
|
|
219
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; all user data wrapped in escHtml() (fields: agent id, emoji, name, role)
|
|
217
220
|
popup.innerHTML = items.map((a, i) =>
|
|
218
|
-
'<div class="cmd-mention-item' + (i === 0 ? ' active' : '') + '" data-id="' + a.id + '" onclick="cmdInsertPopupItem(\'' + escHtml(a.id) + '\')">' +
|
|
219
|
-
'<span class="mention-emoji">' + a.emoji + '</span>' +
|
|
221
|
+
'<div class="cmd-mention-item' + (i === 0 ? ' active' : '') + '" data-id="' + escHtml(a.id) + '" onclick="cmdInsertPopupItem(\'' + escHtml(a.id) + '\')">' +
|
|
222
|
+
'<span class="mention-emoji">' + escHtml(a.emoji || '') + '</span>' +
|
|
220
223
|
'<span class="mention-name">@' + escHtml(a.name) + '</span>' +
|
|
221
224
|
'<span class="mention-role">' + escHtml(a.role) + '</span>' +
|
|
222
225
|
'</div>'
|
|
@@ -234,6 +237,7 @@ function cmdShowProjects(query) {
|
|
|
234
237
|
if (items.length === 0) { popup.classList.remove('visible'); return; }
|
|
235
238
|
|
|
236
239
|
cmdMentionIdx = 0;
|
|
240
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; all user data wrapped in escHtml() (fields: project name, description)
|
|
237
241
|
popup.innerHTML = items.map((p, i) =>
|
|
238
242
|
'<div class="cmd-mention-item' + (i === 0 ? ' active' : '') + '" data-id="' + escHtml(p.name) + '" onclick="cmdInsertPopupItem(\'' + escHtml(p.name) + '\')">' +
|
|
239
243
|
'<span class="mention-emoji">\u{1F4C1}</span>' +
|
|
@@ -25,6 +25,7 @@ function showToast(id, msg, ok, durationMs) {
|
|
|
25
25
|
el.classList.remove('success', 'error');
|
|
26
26
|
el.classList.add(ok ? 'success' : 'error');
|
|
27
27
|
if (msg.includes('<a ') || msg.includes('<strong>')) {
|
|
28
|
+
// eslint-disable-next-line no-unsanitized/property -- reason: structural HTML is a string literal; all user data wrapped in escHtml() before showToast() is invoked (fields: rich toast labels and URLs)
|
|
28
29
|
el.innerHTML = msg;
|
|
29
30
|
} else {
|
|
30
31
|
el.textContent = msg;
|