// ── Status ──────────────────────────────────────────────────────────────────
// mcpCount = agent clients on the socket. `sessions` = agents that have actually claimed a
// tab. They differ, and showing "Connected - 1 session" above "No active sessions" read as a
// contradiction at exactly the moment a new user checks whether the install worked.
function paintStatus(connected, count, sessionCount) {
document.getElementById('dot').className = `dot ${connected ? 'on' : 'off'}`;
document.getElementById('label').textContent = !connected
? 'Not connected - nothing on ports 9876-9895 yet'
: sessionCount > 0
? `Connected - ${sessionCount} active session${sessionCount === 1 ? '' : 's'}`
: `Connected - ready (${count} agent${count === 1 ? '' : 's'} listening)`;
// The Chrome Web Store can only install the extension. If nothing is listening on
// ports 9876-9895, the user almost certainly never ran the npx install - say so.
document.getElementById('setup').classList.toggle('show', !connected);
// Connected but idle: they got it working and now need to know what to say.
document.getElementById('try').classList.toggle('show', connected && sessionCount === 0);
}
function refreshStatus() {
chrome.storage.local.get({ mcpConnected: false, mcpCount: 0, sessions: {} }, (result) => {
paintStatus(result.mcpConnected === true, result.mcpCount || 0,
Object.keys(result.sessions || {}).length);
});
}
refreshStatus();
// ── Sessions with tabs ─────────────────────────────────────────────────────
function renderSessions() {
chrome.storage.local.get({ sessions: {} }, ({ sessions }) => {
const container = document.getElementById('sessions');
const entries = Object.entries(sessions);
if (!entries.length) {
container.innerHTML = '
No tabs claimed yet
';
return;
}
// Fetch tab info for each session
const promises = entries.map(async ([port, session]) => {
const tabInfos = [];
for (const tabId of session.tabIds || []) {
try {
const tab = await chrome.tabs.get(tabId);
const url = tab.url || '';
const display = url.length > 40 ? url.slice(0, 40) + '…' : url;
tabInfos.push(display);
} catch {}
}
return { port, session, tabInfos };
});
Promise.all(promises).then(results => {
container.innerHTML = results.map(({ port, session, tabInfos }) => {
const color = session.color || 'blue';
const tabHtml = tabInfos.length
? tabInfos.map(u => `• ${u}
`).join('')
: 'No tabs
';
return `
`;
}).join('');
});
});
}
renderSessions();
// ── Action Log ──────────────────────────────────────────────────────────────
function renderLog() {
chrome.storage.local.get({ actionLog: [] }, ({ actionLog }) => {
const container = document.getElementById('log');
if (!actionLog.length) {
container.innerHTML = 'No actions yet
';
return;
}
container.innerHTML = actionLog.slice(0, 30).map(entry => {
const time = new Date(entry.time).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' });
const cat = entry.category || 'safe';
const cls = cat === 'sensitive' ? 'log-sensitive' : 'log-safe';
return `${time}${entry.method}${entry.session || ''}
`;
}).join('');
});
}
renderLog();
// ── Buttons ─────────────────────────────────────────────────────────────────
document.getElementById('reconnect').addEventListener('click', () => {
chrome.runtime.sendMessage({ type: 'reconnect' });
document.getElementById('label').textContent = 'Reconnecting…';
setTimeout(() => {
refreshStatus();
renderSessions();
}, 3000);
});
document.getElementById('copyCmd').addEventListener('click', (e) => {
navigator.clipboard.writeText(document.getElementById('setupCmd').textContent.trim());
e.target.textContent = 'Copied!';
setTimeout(() => { e.target.textContent = 'Copy command'; }, 1500);
});
document.getElementById('clearLog').addEventListener('click', () => {
chrome.storage.local.set({ actionLog: [] }, renderLog);
});
// ── Parring (issue #10) ──────────────────────────────────────────────────────
// Feltet er tomt som standard, og tomt betyder praecis det der sker i dag: ingen noegle,
// ingen opsaetning. Skriver man én, vises den kommando serveren skal startes med - saa
// noeglen ikke skal skrives af i haanden to steder.
const pairKey = document.getElementById('pairKey');
const pairCmd = document.getElementById('pairCmd');
const copyPair = document.getElementById('copyPair');
function visPairCmd(noegle) {
const har = Boolean(noegle);
pairCmd.hidden = !har;
copyPair.hidden = !har;
if (har) pairCmd.textContent = `BROWSER_MCP_TOKEN=${noegle} npx @agent360/browser-mcp@latest`;
}
chrome.storage.local.get('parringsnoegle', (v) => {
const noegle = (v && v.parringsnoegle) || '';
pairKey.value = noegle;
visPairCmd(noegle);
});
document.getElementById('savePair').addEventListener('click', (e) => {
const noegle = pairKey.value.trim();
chrome.storage.local.set({ parringsnoegle: noegle }, () => {
visPairCmd(noegle);
e.target.textContent = 'Saved';
setTimeout(() => { e.target.textContent = 'Save'; }, 1500);
});
});
copyPair.addEventListener('click', (e) => {
navigator.clipboard.writeText(pairCmd.textContent.trim());
e.target.textContent = 'Copied!';
setTimeout(() => { e.target.textContent = 'Copy the server command'; }, 1500);
});