import type { EventBus } from '../../events/index.js'; import type { ExecutionRecord } from '../../store/execution-repo.js'; /** Where a running execution's output.log lives — a local fs path or a remote device+path. */ export type LogLocation = { kind: 'local'; path: string; } | { kind: 'remote'; device: string; path: string; }; /** Reads the bytes appended since `offset`, returning the decoded chunk and the new offset. */ export type LogReader = (loc: LogLocation, offset: number) => Promise<{ chunk: string; nextOffset: number; }>; export interface ExecutionLogTailerOptions { /** Injectable reader (tests / custom transports). Defaults to fs-local + bash-remote. */ read?: LogReader; /** Poll cadence; <= 0 disables the internal timer (tests drive pollOnce manually). */ pollIntervalMs?: number; /** Max lines held between flushes; older lines are dropped (marked) when exceeded. */ maxBufferLines?: number; /** Max bytes held between flushes; older lines are dropped (marked) when exceeded. */ maxBufferBytes?: number; /** Max bytes read from the source per poll (throttles a huge backlog into successive events). */ maxReadBytes?: number; } /** * Registry-only resolution of a running execution's log location (B2-C). The cortex-run run name * is persisted on `dispatch.runName` at launch, so the executionId alone is sufficient: the run's * output.log is `//output.log`, and local-vs-remote is decided by * `dispatch.machine`. Returns null for an unknown id or one with no runName (nothing to tail). */ export declare function resolveExecutionLogLocation(executionId: string, opts?: { getExecution?: (id: string) => Pick | null; localMachine?: string; tmpBaseDir?: string; }): LogLocation | null; export declare class ExecutionLogTailer { private states; private bus; private readonly read; private readonly pollIntervalMs; private readonly maxBufferLines; private readonly maxBufferBytes; constructor(opts?: ExecutionLogTailerOptions); setBus(bus: EventBus): void; /** Current subscriber ref-count for an execution (0 if not tailing). */ refCount(executionId: string): number; /** * Register interest in an execution's live log. Ref-counted: the first call opens the tail * (and starts the poll timer); subsequent calls only bump the count. `location` is supplied by * the caller (resolveExecutionLogLocation is the registry-backed helper). */ startTail(executionId: string, location: LogLocation): void; /** Release interest. The last release tears down the underlying tail (timer + offset state). */ stopTail(executionId: string): void; /** * Read and publish one increment. Public so the poll timer and tests share one path. * No-op when the execution is not tailing, no bus is wired, or a read is already in flight. */ pollOnce(executionId: string): Promise; /** Split a chunk into complete lines, holding a trailing partial line for the next read. */ private ingest; /** Drop-oldest until within the line AND byte caps, accumulating the drop count. */ private enforceBound; private flush; } /** Process-wide singleton, wired to the EventBus in entry/app.ts. */ export declare const executionLogTailer: ExecutionLogTailer;