import { existsSync, readFileSync, statSync } from "node:fs"; import { getWebSearchConfigPath } from "../config/path.ts"; export type WebAccessConfig = Record & { provider?: unknown; searchProvider?: unknown; maxInlineContentChars?: unknown; webSearch?: { enabled?: unknown }; tools?: { webSearch?: { enabled?: unknown }; webFetch?: { enabled?: unknown }; fetchContent?: { enabled?: unknown }; }; }; const CONFIG_PATH = getWebSearchConfigPath(); let cache: { signature: string; config: WebAccessConfig } | undefined; export function getConfig(): WebAccessConfig { if (!existsSync(CONFIG_PATH)) return {}; const stat = statSync(CONFIG_PATH); const signature = `${stat.mtimeMs}:${stat.size}`; if (cache?.signature === signature) return cache.config; let parsed: unknown; try { parsed = JSON.parse(readFileSync(CONFIG_PATH, "utf8")); } catch (error) { const message = error instanceof Error ? error.message : ""; const position = message.match(/at position \d+(?: \(line \d+ column \d+\))?/i)?.[0]; throw new Error(`Failed to parse ${CONFIG_PATH}: not valid JSON${position ? `, ${position}` : ""}`); } if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) { throw new Error(`Invalid config in ${CONFIG_PATH}: expected a JSON object`); } const config = parsed as WebAccessConfig; cache = { signature, config }; return config; } export function clearConfigCache(): void { cache = undefined; } export function getConfigOrEmpty(): WebAccessConfig { try { return getConfig(); } catch { return {}; } } export function isToolEnabled(config: WebAccessConfig, tool: "webSearch" | "webFetch"): boolean { if (tool === "webSearch") { const override = config.tools?.webSearch?.enabled; if (typeof override === "boolean") return override; return config.webSearch?.enabled !== false; } const override = config.tools?.webFetch?.enabled ?? config.tools?.fetchContent?.enabled; return override !== false; } const DEFAULT_MAX_INLINE_CONTENT_CHARS = 30_000; const MAX_INLINE_CONTENT_CHARS = 200_000; export function getMaxInlineContentChars(): number { const value = getConfigOrEmpty().maxInlineContentChars; if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) return DEFAULT_MAX_INLINE_CONTENT_CHARS; return Math.min(value, MAX_INLINE_CONTENT_CHARS); }