/** * Maximum estimated payload bytes per push batch. The `/scan/push` route runs * with a 52,428,800-byte (50 MB) Fastify bodyLimit; exceeding it fails the * whole batch, not one file. With a 10 MB per-file ceiling, 25 files no longer * fit by construction, so batches flush on accumulated bytes as well as count. * The estimate sums whole-file `content` plus chunk content — the two bodies * that dominate the JSON payload — and the ~12 MB of remaining headroom * absorbs JSON escaping and per-chunk metadata. */ export declare const MAX_BATCH_PAYLOAD_BYTES: number; import { type ScanPushFile } from './client.js'; import { type UnregisteredRoot } from './discovery.js'; /** * Split files into push batches bounded by BOTH file count and estimated * payload bytes. A file whose own payload exceeds the byte threshold is * emitted alone rather than dropped — the per-file ceiling * (MAX_SCANNED_FILE_BYTES, 10 MB) leaves a lone file far under the route's * 50 MB bodyLimit, so isolating it is always the correct move. Order is * preserved and no file is ever omitted. */ export declare function assemblePushBatches(files: ScanPushFile[], maxFiles?: number, maxBytes?: number): ScanPushFile[][]; /** * The one-line warning for a watched-but-unregistered directory. Pure so the * exact words are testable — this text is the whole point of the fix: a * directory under a watch root that has no usable `.mnemonik.json` is walked * and then skipped, and used to be skipped in total silence, which is how * "15 watched" could mean 13 scanned. It names the directory and the missing * file, and it never registers anything: whether a directory should be indexed * is the owner's decision, not the daemon's. */ export declare function formatUnregisteredWarning(entry: UnregisteredRoot): string; /** * The per-refresh summary. States WATCHED and REGISTERED separately, because * they are different numbers and reporting only the first one is the defect * this file was fixed for. */ export declare function formatWatchSummary(registered: number, unregistered: number): string; export interface DaemonConfig { serverUrl: string; apiKey: string; roots: string[]; refreshIntervalMs?: number; maxConcurrentScans?: number; } export declare class ScannerDaemon { private config; private client; private scanner; private projects; private refreshTimer; private heartbeatTimer; private discovery; private refreshIntervalMs; private scannerVersion; /** Watched directories that are NOT registered projects — reported, never scanned. */ private unregistered; /** Keys already warned about, so a 5-minute refresh doesn't repeat itself. */ private warnedUnregistered; constructor(config: DaemonConfig); start(): Promise; stop(): Promise; private sendHeartbeats; private getScannerVersion; /** * The REGISTERED projects — the ones actually scanned. Watched directories * without a usable `.mnemonik.json` are not here; see * `getUnregisteredRoots()`. The two lists are deliberately separate: they * were conflated once, and "N watched" then overstated what was indexed. */ getRegisteredProjects(): Array<{ projectId: string; path: string; name?: string; }>; /** Watched-but-unregistered directories as of the last refresh. */ getUnregisteredRoots(): UnregisteredRoot[]; /** * Discover projects from configured roots and reconcile with current watch list. */ refreshProjects(): Promise; /** * Warn once per watched-but-unregistered directory. Once, not per refresh: * the refresh runs every 5 minutes and a repeated warning is noise that * teaches the reader to ignore it. A directory that drops off the list and * comes back warns again, so a regression is never swallowed by the memo. */ private reportUnregistered; private addProject; private waitForServer; private initialScan; /** * Send the explicit list of paths that vanished since the previous scan. * Empty lists short-circuit so we don't pay an HTTP round-trip when * nothing was removed this tick. */ private sendRemovedFiles; /** * Fetch commits since the last mine. Swallows errors — a git failure must * not block the file scan push. */ private collectCommits; private handleChanges; private startRetryLoop; /** * Collect the project's root `.gitignore` + `.mnemonikignore` patterns * (non-empty, non-comment lines, deduped, capped) to upload for server-side * ignore enforcement. Root-level only — the hierarchical walk already applies * nested rules client-side; the server net cares about the root privacy list. */ private collectIgnorePatterns; private collectAuthorityPushFiles; private groupChunksByFile; }