import { DebuggerUI } from "../debugger/ui.js"; import { type AttachRecord } from "../debugger/attach/registry.js"; import type { Channel } from "../debugger/attach/channel.js"; /** `ps -o lstart=` has one-second resolution, and the record's time is * `performance.timeOrigin`, read a little after the fork, so the two may * differ by a little. */ export declare const START_TIME_TOLERANCE_MS = 2000; /** A live process that started later than its record by more than the * tolerance but less than this is refused without removing the record. * On Linux `lstart` is boot time plus ticks, and the kernel moves boot * time when the wall clock is stepped, so an NTP step after the program * started makes every `lstart` later by that much; the record of a * program that is still running must survive that. A gap this small can * also be a reused pid, which the refusal still guards against. */ export declare const LATER_START_KEEP_MS = 60000; /** How long before its record a live process may have started and still * be the one that wrote it: a wrapper script that does some work and * then `exec`s node keeps the wrapper's start time. A process older than * this is some other process with the same pid, which happens when the * sessions directory is shared between machines or pid namespaces (a * home on NFS, a container that mounts it); the record is then another * machine's and is left alone. */ export declare const EARLY_START_LIMIT_MS: number; /** How long to keep trying to connect after the signal: the socket * appears once the program's event loop handles the signal. */ export declare const CONNECT_RETRY_MS = 2000; export type AttachRefusal = { refused: string; }; /** The listing `agency debug --attach` prints with no pid. A program's * path is the one the process was started with, which `agency run` * makes absolute; under `cwd` it is shown relative to it. */ export declare function formatListing(records: readonly AttachRecord[], now: number, cwd?: string): string; export type StartTime = { started: number; } | { failed: string; }; /** The start time `ps` reports for `pid`, as a timestamp, or why it could * not be read: `ps` missing (a slim container), a `ps` that does not take * `-o lstart` (BusyBox), or a date the parser cannot read. `ps` is run * in the C locale, since `lstart` is otherwise printed in the user's * language (`Di. 20 Okt. 15:47:40 2026` under German) and `Date.parse` * reads only English, and in UTC, since a local wall time has no zone: * on the night the clocks go back the hour from 1:00 to 2:00 happens * twice, and a program started in the second pass would read as an hour * older than its record. `ps` writes its own complaints to stderr, which * is not shown. */ export declare function liveStartTime(pid: number): StartTime; /** `lstart` as `ps` prints it in the C locale and UTC, `Tue Oct 20 * 13:32:26 2026`, read as UTC. */ export declare function parseStartTime(output: string): number | null; /** The record for `pid`, when its process is the one the record is * about. A run that crashes leaves its record behind, and the operating * system may later give that pid to an unrelated process; so the live * process is checked before the record is shown as live or connected * to. (The check was written when attaching sent a signal that would * have killed the stranger; it now keeps the listing honest.) A * process that is gone has its record removed. One * that is alive must have started around when the record was written: * later than that, and it is a stranger that got the pid after the * registered process exited, so the record is removed; more than * `EARLY_START_LIMIT_MS` earlier, and it is a stranger that had the pid * all along, which the record from another machine or pid namespace * names by accident, so the record is kept and nothing is sent. A little * earlier is the process itself, started by a wrapper that `exec`ed node. * When `ps` cannot say, the attach is refused and the record kept: the * program may well be running. */ export declare function checkedRecord(dir: string, pid: number, startTime?: (pid: number) => StartTime): AttachRecord | AttachRefusal; /** Why `pid` cannot be connected to when its socket file is missing, or * null when the file is there. A registered program opens its socket * right after its record, so a missing file means the program could * not (a long path, a filesystem without Unix sockets) or is on a * version that still waits for a signal; either way there is nothing * to retry for, and the person is told at once. Shared by `--attach` * and `agency debug session attach`. */ export declare function missingSocket(dir: string, pid: number): string | null; /** Connect to `sock`, retrying while the program finishes opening it: the * file exists from `listen`, a tick before connections are taken. The retry's * error listener comes off once connected, so a later error on the * socket does not open a second connection. */ export declare function connectWithRetry(pid: number, sock: string, retryMs: number): Promise; export type AttachCommandOptions = { pid?: number; /** The UI to drive; the terminal UI when absent. A test passes its own. */ ui?: DebuggerUI; /** Where lines for the person go before the UI is up, and where a * refusal goes: stdout and stderr. */ print?: (line: string) => void; printError?: (line: string) => void; retryMs?: number; }; /** Run `agency debug --attach`. Resolves with the exit code: 0 when the * session ended, 1 on a refusal. */ export declare function attachCommand(options: AttachCommandOptions): Promise; /** The line printed when the run sent `finished`: its value, the failure * it returned (a node whose body threw returns a failure `Result`), or * the error it ended with (a cancelled run). */ export declare function finishedLine(value: unknown, error: string | undefined): string;