import { createDriver } from "./driver.js"; export class SearchProviderError extends Error { constructor(code, message) { super(message); this.code = code; } } // Kagi Search Extraction export const kagiSearchScript = `return Array.from(document.querySelectorAll("div > .__sri-title")) .map((i) => i.parentElement) .map((i) => ({ title: i.children[0].querySelector("a").innerText, url: i.children[0].querySelector("a").getAttribute("href"), description: i.querySelector(".__sri-body").innerText, }));`; // Build Kagi Search Url export function buildKagiSearchUrl(query, token) { return `https://kagi.com/search?token=${encodeURIComponent(token)}&q=${encodeURIComponent(query)}`; } // Search Kagi export async function searchKagi({ query, token = process.env.KAGI_TOKEN, headless = true, existingUrl, timeoutMs = 5000, intervalMs = 200, } = {}) { if (!query) { throw new SearchProviderError("QUERY_REQUIRED", "query is required"); } // Validate Kagi Token if (!token) { throw new SearchProviderError( "KAGI_TOKEN_REQUIRED", "Kagi search requires --token or the KAGI_TOKEN environment variable.", ); } const driver = await createDriver({ headless, existingUrl }); try { // Navigate To Kagi await driver.get(buildKagiSearchUrl(query, token)); // Poll For Results let result = []; const start = Date.now(); while (Date.now() - start < timeoutMs) { result = await driver.executeScript(kagiSearchScript); if (Array.isArray(result) && result.length > 0) break; await new Promise((resolve) => setTimeout(resolve, intervalMs)); } return result; } finally { await driver.quit(); } }