// ClawVault Background Service Worker // Get settings from storage async function getSettings() { const result = await chrome.storage.local.get(['vaultUrl', 'apiToken']); return { vaultUrl: result.vaultUrl || 'http://localhost:8765', apiToken: result.apiToken || '' }; } // API call helper async function apiCall(method, path, body) { const settings = await getSettings(); if (!settings.apiToken) { throw new Error('Not configured'); } const opts = { method, headers: { 'Authorization': 'Bearer ' + settings.apiToken, 'Content-Type': 'application/json' } }; if (body) { opts.body = JSON.stringify(body); } const res = await fetch(settings.vaultUrl + path, opts); if (!res.ok) { throw new Error('API error: ' + res.status); } return res.json(); } // Handle messages from popup and content scripts chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.action === 'getMatches') { apiCall('GET', '/api/ext/match?url=' + encodeURIComponent(request.url)) .then(matches => sendResponse({ success: true, matches })) .catch(err => sendResponse({ success: false, error: err.message })); return true; // async response } if (request.action === 'getEntry') { apiCall('GET', '/api/entries/' + request.id) .then(entry => sendResponse({ success: true, entry })) .catch(err => sendResponse({ success: false, error: err.message })); return true; } if (request.action === 'getTOTP') { apiCall('GET', '/api/ext/totp/' + request.id) .then(data => sendResponse({ success: true, data })) .catch(err => sendResponse({ success: false, error: err.message })); return true; } if (request.action === 'mapFields') { apiCall('POST', '/api/ext/map', request.data) .then(mapping => sendResponse({ success: true, mapping })) .catch(err => sendResponse({ success: false, error: err.message })); return true; } if (request.action === 'fill') { // Relay fill request to content script chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { if (tabs[0]) { chrome.tabs.sendMessage(tabs[0].id, { action: 'fillFields', fields: request.fields }); } }); sendResponse({ success: true }); return true; } if (request.action === 'getSettings') { getSettings().then(settings => sendResponse({ success: true, settings })); return true; } if (request.action === 'saveSettings') { chrome.storage.local.set({ vaultUrl: request.vaultUrl, apiToken: request.apiToken }).then(() => sendResponse({ success: true })); return true; } }); // Listen for form detection from content scripts chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { if (request.action === 'formsDetected') { // Update badge with form count if (request.count > 0) { chrome.action.setBadgeText({ text: String(request.count), tabId: sender.tab.id }); chrome.action.setBadgeBackgroundColor({ color: '#c9a84c', tabId: sender.tab.id }); } else { chrome.action.setBadgeText({ text: '', tabId: sender.tab.id }); } } });