/** * Bounded process shutdown. * * Teardown reaches third-party code we do not control — MCP servers over stdio, * LSP servers, extension `deactivate()` hooks, Telegram long-polls. Any one of * them can hang forever, and an `await`-everything shutdown then never reaches * `process.exit`: the app appears to quit but the daemon keeps its port, or the * CLI keeps polling with nobody attached. Every long-running entry point arms a * deadline instead, so a wedged dependency costs a few seconds, not the process. * * `SIGHUP` matters as much as `SIGINT`: closing a terminal delivers exactly one * hangup and never a second key press, so a process that only force-exits on the * *second* Ctrl+C survives as an orphan. */ /** Signals that must terminate a long-running GG process. */ export declare const TERMINATION_SIGNALS: readonly ["SIGINT", "SIGTERM", "SIGHUP"]; export type TerminationSignal = (typeof TERMINATION_SIGNALS)[number]; /** How long teardown gets before the process exits anyway. */ export declare const DEFAULT_EXIT_TIMEOUT_MS = 5000; /** Env override, in seconds. `0` disables the deadline (debugging teardown). */ export declare const EXIT_TIMEOUT_ENV = "GG_EXIT_TIMEOUT_SECS"; /** * Resolve the teardown deadline from the environment. * * Anything unparseable, negative or absurd falls back to the default rather * than disabling the guard, because a typo in a launcher script must not be * able to reintroduce an unbounded hang. */ export declare function resolveExitTimeoutMs(env?: NodeJS.ProcessEnv): number; /** Conventional exit code for a signal-initiated exit (`128 + signal number`). */ export declare function exitCodeForSignal(signal: TerminationSignal): number; export interface ShutdownOptions { /** Releases resources. May hang — that is the entire point of the deadline. */ teardown: () => Promise | void; /** Deadline override, mainly for tests. Defaults to {@link resolveExitTimeoutMs}. */ timeoutMs?: number; /** Logging/reporting scope, e.g. `"app-sidecar"`. */ scope?: string; /** Runs when teardown misses the deadline, just before the forced exit. */ onTimeout?: (timeoutMs: number) => void; /** Runs when teardown throws. Shutdown continues regardless. */ onError?: (error: unknown) => void; /** Terminates the process. Injectable so tests do not exit the runner. */ exit?: (code: number) => never; } /** * Run `teardown`, then exit — but exit on the deadline even if teardown never * settles. * * The timer is deliberately **not** unref'd: an unref'd timer lets Node exit on * its own the moment the loop empties, which would race teardown and produce a * nondeterministic exit code. Held ref + explicit exit on both paths means the * code is always the one we chose. */ export declare function shutdownWithDeadline(code: number, options: ShutdownOptions): Promise; export interface TerminationHandlerOptions extends ShutdownOptions { /** Runs once per shutdown request, before teardown (e.g. print a banner). */ onShutdownStart?: (signal: TerminationSignal | null) => void; } /** * Wire `SIGINT`/`SIGTERM`/`SIGHUP` to a bounded shutdown. * * A second signal while teardown is still running exits immediately rather than * waiting out the deadline — a user hitting Ctrl+C twice is telling us they are * done being patient. * * @returns `requestShutdown`, for non-signal exits (a dead parent process, a * quit menu item) so they share the same guard and re-entrancy rules. */ export declare function installTerminationHandlers(options: TerminationHandlerOptions): (code?: number) => void; //# sourceMappingURL=shutdown.d.ts.map