/** * The CLI's let-go coordination, as a unit: what happens when the MCP client * hangs up (stdin EOF) or the process is signalled, relative to where startup * is at that moment. Split from cli.ts so the states can be pinned by unit * tests — cli.ts runs `main()` on import and only the e2e suite can reach it. * * Three states, three answers: * * - Startup DONE (`holder.shutdown` set): orderly teardown — close the UTCP * client (which is what actually ends the spawned stdio grandchildren) and * exit. The status is `process.exitCode ?? 0`: a recorded startup failure * (say, `server.connect` rejecting) must not be laundered into a clean 0 * by the client's eventual hang-up. The teardown itself is BOUNDED by a * cleanup watchdog: a `shutdown()` that hangs (a transport close that * never resolves) would otherwise leave the spawned servers alive * indefinitely — exactly the orphans teardown exists to prevent. * * - Startup PENDING: only RECORD the request — there is nothing reachable to * close yet — and arm a bounded force-exit. When creation resolves in time, * main() sees `exitRequested` and runs the orderly path above, which exits * first; when creation is WEDGED (a local stdio server that never answers * initialize), the force-exit is the only exit there is, and it reports * failure. unref()d, so it never holds an otherwise-finished process open. * * - Already EXITING (main() took over, or a second 'end'/'close'/signal): * nothing — teardown must not be re-entered while `shutdown()` runs. */ /** Shared between the handler and main(): what exists, and what was asked. */ export interface ShutdownHolder { shutdown: (() => Promise) | null; exitRequested: boolean; /** Set by whichever path commits to exiting, so the other stands down. */ exiting: boolean; } /** * How long a let-go waits on a still-creating startup before giving up on the * orderly path. Generous against a healthy create (which resolves in well * under a second once its downloads are done), tight against a wedged one. */ export declare const STARTUP_LETGO_GRACE_MS = 5000; /** * How long the orderly teardown gets before the cleanup watchdog gives up on * it. Closing the transports is milliseconds when the children are healthy; * a shutdown still pending after this is wedged on one of them, and a forced * exit (which at least collapses the stdio pipes) beats servers that outlive * their client indefinitely. */ export declare const SHUTDOWN_GRACE_MS = 15000; /** * The orderly exit, wherever it starts (the let-go handler once startup is * done, or main() finding `exitRequested` after create resolves): flips * `exiting` so every other path stands down, runs `shutdown()`, and exits * with the recorded status. Bounded — the watchdog force-exits a hung * shutdown, and is cleared the moment shutdown completes (in tests, where * `process.exit` is mocked and does not actually terminate, the cleared * timer is also what keeps the exit from firing twice). */ export declare function beginOrderlyExit(holder: ShutdownHolder): void; /** Build the handler cli.ts installs for stdin 'end'/'close', SIGINT and SIGTERM. */ export declare function makeExitAfterShutdown(holder: ShutdownHolder): () => void; //# sourceMappingURL=teardown.d.ts.map