// popup.js — Dashboard UI for Agent Police Department
document.addEventListener('DOMContentLoaded', async () => {
await loadStats();
await loadViolations();
setupTabs();
setupSearch();
setupPolicyEditor();
setupFooter();
});
async function loadStats() {
try {
const response = await chrome.runtime.sendMessage({ type: 'GET_STATS' });
if (!response?.success) return;
const stats = response.stats;
document.getElementById('risk-score').textContent = stats.riskScore ?? 0;
const statusEl = document.getElementById('compliance-status');
statusEl.textContent = stats.complianceStatus || 'unknown';
statusEl.className = 'stat-badge ' + (stats.complianceStatus || '');
document.getElementById('active-count').textContent = stats.activeCount ?? 0;
document.getElementById('violation-count').textContent = stats.totalViolations ?? 0;
document.getElementById('event-count').textContent = stats.recentEvents ?? 0;
} catch (err) {
console.error('Stats load error:', err);
}
}
async function loadViolations() {
try {
const { violations = [] } = await chrome.storage.local.get(['violations']);
renderList(violations.slice(0, 50).reverse(), 'violations-list', formatViolation);
} catch (err) {
console.error('Violations load error:', err);
}
}
async function loadActivity() {
try {
const response = await chrome.runtime.sendMessage({ type: 'GET_AUDIT_LOG', limit: 50 });
if (!response?.success) return;
renderList(response.log || [], 'activity-list', formatEvent);
} catch (err) {
console.error('Activity load error:', err);
}
}
function renderList(items, containerId, formatter) {
const container = document.getElementById(containerId);
if (!container) return;
if (items.length === 0) {
container.innerHTML = '
No items found
';
return;
}
container.innerHTML = items.map(formatter).join('');
}
function formatViolation(v) {
const time = new Date(v.timestamp).toLocaleTimeString();
return `
${time} • ${v.rule_triggered || 'system'}
${v.message}
`;
}
function formatEvent(e) {
const time = new Date(e.timestamp).toLocaleTimeString();
return `
${time} • ${e.agent || 'unknown'} • ${e.url || 'unknown'}
${e.target || e.context || 'No details'}
`;
}
function setupTabs() {
const tabs = document.querySelectorAll('.tab-btn');
tabs.forEach(tab => {
tab.addEventListener('click', async () => {
tabs.forEach(t => t.classList.remove('active'));
tab.classList.add('active');
const tabName = tab.dataset.tab;
document.querySelectorAll('.tab-content').forEach(c => c.classList.add('hidden'));
document.getElementById(tabName + '-tab').classList.remove('hidden');
if (tabName === 'activity') await loadActivity();
if (tabName === 'policy') await loadPolicyUI();
});
});
}
function setupSearch() {
const vSearch = document.getElementById('violation-search');
if (vSearch) {
vSearch.addEventListener('input', async (e) => {
const q = e.target.value.toLowerCase();
const { violations = [] } = await chrome.storage.local.get(['violations']);
const filtered = violations.filter(v =>
v.detector.includes(q) || v.message.toLowerCase().includes(q) || v.severity.includes(q)
);
renderList(filtered.slice(0, 50).reverse(), 'violations-list', formatViolation);
});
}
const aSearch = document.getElementById('activity-search');
if (aSearch) {
aSearch.addEventListener('input', async (e) => {
const q = e.target.value.toLowerCase();
const response = await chrome.runtime.sendMessage({ type: 'GET_AUDIT_LOG', limit: 100 });
if (!response?.success) return;
const filtered = (response.log || []).filter(item =>
(item.agent || '').toLowerCase().includes(q) ||
(item.type || '').toLowerCase().includes(q) ||
(item.tool || '').toLowerCase().includes(q) ||
(item.target || '').toLowerCase().includes(q)
);
renderList(filtered, 'activity-list', formatEvent);
});
}
}
async function loadPolicyUI() {
const { policy } = await chrome.storage.local.get(['policy']);
if (!policy) return;
const container = document.getElementById('policy-rules');
if (!container) return;
container.innerHTML = policy.rules.map(rule => `
`).join('');
}
function setupPolicyEditor() {
document.getElementById('save-policy')?.addEventListener('click', async () => {
const { policy } = await chrome.storage.local.get(['policy']);
if (!policy) return;
const toggles = document.querySelectorAll('#policy-rules input[type="checkbox"]');
toggles.forEach(toggle => {
const rule = policy.rules.find(r => r.id === toggle.dataset.ruleId);
if (rule) rule.enabled = toggle.checked;
});
await chrome.storage.local.set({ policy });
showStatus('Policy saved');
});
document.getElementById('reset-policy')?.addEventListener('click', async () => {
await chrome.storage.local.remove(['policy']);
showStatus('Policy reset to default');
await loadPolicyUI();
});
}
function setupFooter() {
document.getElementById('export-btn')?.addEventListener('click', async () => {
const response = await chrome.runtime.sendMessage({ type: 'EXPORT_AUDIT' });
if (!response?.success) return;
const blob = new Blob([JSON.stringify(response.data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `agent-police-audit-${new Date().toISOString().slice(0, 10)}.json`;
a.click();
URL.revokeObjectURL(url);
showStatus('Audit log exported');
});
document.getElementById('verify-btn')?.addEventListener('click', async () => {
const { verifyIntegrity } = await import('./lib/audit.js');
const result = await verifyIntegrity();
showStatus(result.valid ? 'Integrity verified ✓' : 'Integrity check failed ✗');
});
}
function showStatus(msg) {
const el = document.createElement('div');
el.className = 'status-message';
el.textContent = msg;
document.body.appendChild(el);
setTimeout(() => el.remove(), 2000);
}