const ext = globalThis.browser ?? globalThis.chrome; const nativeHost = "org.keepassgo.browser"; const defaultSettings = { grpcAddress: "127.0.0.1:47777", bearerToken: "" }; function storageGet(keys) { return new Promise((resolve, reject) => { ext.storage.local.get(keys, (value) => { const error = ext.runtime.lastError; if (error) { reject(new Error(error.message)); return; } resolve(value); }); }); } function storageSet(value) { return new Promise((resolve, reject) => { ext.storage.local.set(value, () => { const error = ext.runtime.lastError; if (error) { reject(new Error(error.message)); return; } resolve(); }); }); } function tabsQuery(query) { return new Promise((resolve, reject) => { ext.tabs.query(query, (tabs) => { const error = ext.runtime.lastError; if (error) { reject(new Error(error.message)); return; } resolve(tabs); }); }); } function tabsSendMessage(tabId, message) { return new Promise((resolve, reject) => { ext.tabs.sendMessage(tabId, message, (response) => { const error = ext.runtime.lastError; if (error) { reject(new Error(error.message)); return; } resolve(response); }); }); } function connectNative(message) { return new Promise((resolve, reject) => { ext.runtime.sendNativeMessage(nativeHost, message, (response) => { const error = ext.runtime.lastError; if (error) { reject(new Error(error.message)); return; } resolve(response); }); }); } async function loadSettings() { const stored = await storageGet(["grpcAddress", "bearerToken"]); return { grpcAddress: (stored.grpcAddress || defaultSettings.grpcAddress).trim(), bearerToken: (stored.bearerToken || "").trim() }; } async function activePageContext() { const [tab] = await tabsQuery({ active: true, currentWindow: true }); return { tabId: tab?.id ?? null, url: typeof tab?.url === "string" ? tab.url : "" }; } async function statusForPage() { const settings = await loadSettings(); const page = await activePageContext(); if (!settings.bearerToken) { return { success: false, configured: false, status: null, pageUrl: page.url, matches: [], error: "Set an API token in extension settings." }; } const status = await connectNative({ action: "status", grpcAddress: settings.grpcAddress, bearerToken: settings.bearerToken }); if (!status.success || status.status?.locked || !page.url.startsWith("http")) { return { success: status.success, configured: true, status: status.status ?? null, pageUrl: page.url, matches: [], error: status.error ?? "" }; } const matches = await connectNative({ action: "find-logins", grpcAddress: settings.grpcAddress, bearerToken: settings.bearerToken, url: page.url }); return { success: matches.success, configured: true, status: matches.status ?? status.status ?? null, pageUrl: page.url, matches: matches.matches ?? [], error: matches.error ?? "" }; } async function fillLogin(entryId) { const settings = await loadSettings(); const page = await activePageContext(); if (!settings.bearerToken) { throw new Error("API token is not configured."); } if (page.tabId == null) { throw new Error("No active tab is available."); } const response = await connectNative({ action: "get-login", grpcAddress: settings.grpcAddress, bearerToken: settings.bearerToken, entryId, url: page.url }); if (!response.success || !response.credential) { throw new Error(response.error || "KeePassGO did not return a credential."); } const fillResponse = await tabsSendMessage(page.tabId, { type: "keepassgo-fill-credential", credential: response.credential }); if (!fillResponse?.ok) { throw new Error(fillResponse?.error || "The current page could not be filled."); } return { credential: response.credential, pageUrl: page.url }; } ext.runtime.onMessage.addListener((message, _sender, sendResponse) => { (async () => { switch (message?.type) { case "keepassgo-popup-state": sendResponse(await statusForPage()); return; case "keepassgo-fill-entry": sendResponse({ success: true, ...(await fillLogin(message.entryId)) }); return; case "keepassgo-load-settings": sendResponse({ success: true, settings: await loadSettings() }); return; case "keepassgo-save-settings": await storageSet({ grpcAddress: String(message.settings?.grpcAddress || defaultSettings.grpcAddress).trim(), bearerToken: String(message.settings?.bearerToken || "").trim() }); sendResponse({ success: true }); return; default: sendResponse({ success: false, error: `Unsupported message ${message?.type || ""}`.trim() }); } })().catch((error) => { sendResponse({ success: false, error: error instanceof Error ? error.message : String(error) }); }); return true; });