/** * Incremental reader for the Ansible progress side-channel. * * WHY THIS EXISTS: `runAnsiblePlaybook()` uses `execFile`, which only hands * over stdout once the process has exited, and the bundled `json` stdout * callback only emits its document at `v2_playbook_on_stats` anyway. Together * that meant a server-setup run reported nothing at all until it finished, so * the UI sat on "実行中" with an empty log for the whole run. * * Rather than restructure the authoritative result path (and risk regressing * `maxBuffer`/`timeout`/spawn-failure handling), the callback additionally * appends NDJSON events to a side file — see the PROGRESS CONTRACT in * `ansible/callback_plugins/json.py` — and this module tails that file while * the playbook is still running. */ /** How often the tailer looks for newly appended events. */ export declare const PROGRESS_POLL_INTERVAL_MS = 1000; /** * The per-host outcome fields the callback forwards on an `end` event. * * Structurally identical to `AnsibleJsonHostResult` in server-setup-runner.ts * (which consumes these via `taskResultFrom()`); declared here rather than * imported so the runner can depend on this module without a cycle. */ export interface AnsibleProgressResult { failed?: boolean; changed?: boolean; skipped?: boolean; unreachable?: boolean; msg?: string; } export interface AnsibleProgressEvent { /** 1-based, monotonic within a run; assigned by the callback plugin. */ seq: number; phase: 'start' | 'end'; name: string; /** Present on `end` events only. */ host?: string; /** * Present on `end` events only. Trimmed by the callback to just the fields * `taskResultFrom()` consumes, so the mapping to a `ServerSetupTaskResult` * stays in one place instead of being duplicated in Python. */ result?: AnsibleProgressResult; } /** * Reads whole NDJSON lines appended to a file since the previous call. * * Leftover bytes are kept as a `Buffer`, not a string: task names are * operator-authored and routinely non-ASCII, and decoding a half-read UTF-8 * sequence would replace it with U+FFFD before the rest of it ever arrives. */ export declare class ProgressFileReader { private readonly filePath; private offset; private leftover; constructor(filePath: string); /** * Returns every complete event appended since the last call. * * Best-effort by design: the file living in the run's temp dir may not exist * yet (nothing has run), or may already be gone (cleanup raced us). Neither * is worth failing a playbook over, so both yield an empty batch. */ read(): AnsibleProgressEvent[]; } export interface ProgressTailer { /** Stops polling and delivers anything written since the last tick. */ stop(): Promise; } export interface StartProgressTailerOptions { filePath: string; onEvents: (events: AnsibleProgressEvent[]) => Promise; intervalMs?: number; /** * Extra check to run on the same tick, after the batch has been delivered. * * The only other thing that has to be watched while ansible runs is the * self-restart marker (see self-restart-declaration.ts). Piggybacking here * rather than adding a second timer keeps it inside the same single-flight * loop, so it can never run alongside a delivery it should be ordered * against. Like `onEvents`, its rejections are absorbed. */ onPoll?: () => Promise; } /** * Polls `filePath` and hands each new batch of events to `onEvents`. * * Batching is deliberate: one delivery per tick rather than per event keeps a * long playbook from producing one API request (and one AppSync notification) * per task. * * Delivery is single-flight — a slow `onEvents` delays the next batch instead * of running alongside it, so events cannot be reordered in transit. */ export declare function startProgressTailer(options: StartProgressTailerOptions): ProgressTailer; //# sourceMappingURL=progress-tailer.d.ts.map