import { parseDocsIndex } from './docs-index-parse.mjs'; export { parseDocsIndex }; export interface DocSearchResult { title: string; url: string; description: string; section: string; score: number; } /** The shape scripts/build-docs-index.mjs writes to src/data/coolify-docs.json. */ export interface DocsBundle { source: string; fetched_at: string; entries: number; text: string; /** Sent as If-None-Match on the background refresh; a 304 means the bundle is current. */ etag?: string | null; last_modified?: string | null; } export interface DocsSearchStatus { /** Where the entries currently being served came from. */ source: 'bundled' | 'live'; entries: number; /** When the bundled copy was fetched from coolify.io; empty if the bundle could not be read. */ bundledAt: string; } export interface DocsSearchOptions { /** Loads the bundled index; defaults to the copy shipped in the package. */ loadBundle?: () => DocsBundle; /** Set false to never touch the network (tests, air-gapped installs). */ refresh?: boolean; } /** * Search over the official Coolify docs index (llms.txt). * * The index ships inside the package (#372): `src/data/coolify-docs.json`, * written by `npm run docs:index` and refreshed at release time, so * `search_docs` works offline, behind egress rules, and while coolify.io is * having a moment. The first search builds the in-memory index from that * bundle and starts one background refresh from the live URL; if that * succeeds and parses, the fresher entries replace the bundled ones for the * rest of the process. A search never waits on the network. * * The one exception: a bundle that is missing or parses to nothing is a * broken build, and then the live index is tried once, synchronously, so an * install that can heal itself does. Failing that, every search throws * with the rebuild command, never a silently empty result. * * Why llms.txt and not the full-content dump: ~46KB, a stable spec'd shape * (a markdown link list), and every page comes with a human-written one-line * description. The tool's job is routing the model to the right page, not * serving snippets — the caller can fetch the page itself for depth. */ export declare class DocsSearchEngine { private index; private entries; private source; private bundle; private refreshStarted; private readonly loadBundle; private readonly refresh; constructor(options?: DocsSearchOptions); ensureLoaded(): Promise; private startBackgroundRefresh; private install; /** * Replace the bundled entries with the live index when it can be fetched * and parsed. Every failure is swallowed on purpose (the bundle is the * answer), except that a live file which parses to nothing, or only * partly, is logged once: that is a format change upstream, which the * next `docs:index` refresh would refuse and the operator should know. */ private refreshFromLive; search(query: string, limit?: number): Promise; getEntryCount(): number; status(): DocsSearchStatus; }