/** * Default rotation policy: rotate when the active file would exceed 5 MiB, * keep up to 5 rotated generations (`.1` … `.5`). Matches the * settings advertised by `service install` so operators don't need to * remember separate numbers for the two paths. */ export declare const DEFAULT_MAX_BYTES: number; export declare const DEFAULT_MAX_FILES = 5; export interface RotatingFileWriterOptions { /** Absolute path to the active log file. */ filePath: string; /** * Rotate AFTER a write would push the active file past this many bytes. * Writes are never split mid-buffer; a chunk that is itself larger than * `maxBytes` is allowed through (the next write will trigger rotation). * Must be > 0. */ maxBytes?: number; /** * Number of rotated generations to keep. `.1` is the most * recently rotated; older generations age out toward `.N` and * are removed when they would otherwise become `.(N+1)`. * `maxFiles <= 0` keeps zero generations (rotated file is unlinked * immediately). */ maxFiles?: number; } /** * Append-mode writer that rotates by size. * * Designed for the `ai-support-agent log-rotate` CLI: a single long-lived * process consumes its parent's stdout/stderr stream and persists it to * a bounded set of files. The CLI is the only entrypoint that exists in * practice, but the writer is separated so unit tests can exercise the * rotation arithmetic without spawning a real subprocess. * * Synchronous I/O is intentional: this writer sits on the hot path of the * agent's stdout, and an async backlog during a write storm would cause * the parent's pipe buffer to fill and block (which then stalls the agent). * The volumes involved (tens of MiB per day per project, max) are small * enough that the sync penalty is negligible — far cheaper than the * coordination cost of an async queue. */ export declare class RotatingFileWriter { private readonly filePath; private readonly maxBytes; private readonly maxFiles; private fd; private currentSize; constructor(options: RotatingFileWriterOptions); /** * Write a chunk to the active log file. Rotates beforehand if appending * the chunk would push the file past `maxBytes`. Returns the number of * bytes written (always `chunk.length` on success). */ write(chunk: Buffer | string): number; /** Flush + release the underlying file descriptor. Idempotent. */ close(): void; /** * Open the active file in append mode and record its current size. Called * lazily on first write so constructing a writer is side-effect-free * (helps tests). Ensures the parent directory exists. */ private ensureOpen; /** * Rename the active file to `.1`, shifting any pre-existing * generations down by one. The oldest generation that falls past * `maxFiles` is unlinked. After rotation the active path is empty. * * Rename order is high → low so we never overwrite a live generation * mid-rotation. ENOENT is tolerated (a generation may not exist yet); * any other error aborts the rotation by re-throwing — continuing past * e.g. EBUSY/EACCES on a mid-loop rename would overwrite the orphaned * next-younger generation and cause silent data loss. */ private rotate; } //# sourceMappingURL=log-rotator.d.ts.map