66 lines
1.7 KiB
JavaScript
66 lines
1.7 KiB
JavaScript
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();
|
|
}
|
|
}
|