/**
 * Loopback HTTP redirect strategy for the CLI's local OAuth provider.
 *
 * Implements the runtime-neutral {@link MCPOAuthRedirect} interface from
 * `vcp-common/mcp-oauth.ts`. Mirrors what
 * `ServerCallbackRedirect` does for the editor flow, but for native-app
 * usage per RFC 8252 § 7.3:
 *
 *   1. Bind an HTTP listener on `127.0.0.1:0` at construction (so the
 *      assigned port is available *before* OAuth discovery — discovery
 *      sends `redirect_uris` to the auth server, which must include the
 *      live port).
 *   2. When the SDK calls `onAuthorizationRequired(url)`, open that URL in
 *      the user's browser (or print it to stderr if `noOpen` is set, e.g.
 *      for SSH workflows).
 *   3. Wait for `GET /oauth/callback?code=...&state=...` on the listener,
 *      respond with a small "you can close this window" page, then surface
 *      the `(code, state)` tuple to the caller via {@link awaitCallback}.
 *
 * Wiring back to {@link BaseMCPOAuthProvider.handleCallback} happens in the
 * caller (e.g. `builder mcp connect` in Phase 3): `awaitCallback()` resolves
 * with `{ code, state }`, then the caller invokes
 * `provider.handleCallback(code, state)`. Symmetric to how the Express
 * route handler at `/mcp/oauth/callback` drives the server-side flow —
 * neither redirect strategy needs to know about its provider.
 */
import type { MCPOAuthRedirect } from "#vcp-common/mcp-oauth.js";
export interface LoopbackRedirectOptions {
    /**
     * Optional fixed port. When provided, binds `127.0.0.1:<port>` instead of
     * letting the kernel pick. Used for IdPs that require pre-registered
     * redirect URIs (specified via `mcp.json`'s `oauth.redirectPort`).
     */
    port?: number;
    /**
     * If true, do not open the user's browser — print the authorization URL
     * to stderr instead. Used for `--no-open` on SSH / no-display
     * environments (Phase 3 wiring).
     */
    noOpen?: boolean;
    /**
     * Override the idle timeout. The listener tears itself down and
     * `awaitCallback()` rejects with a timeout error after this many
     * milliseconds with no callback received.
     */
    timeoutMs?: number;
    /**
     * Override the browser-opener (test injection). Default is the `open`
     * npm package.
     */
    openImpl?: (url: string) => Promise<void>;
    /**
     * Override the stderr write target (test injection). Default is
     * `process.stderr.write`.
     */
    stderrWrite?: (text: string) => void;
}
/**
 * Resolved on a successful callback. The caller (CLI command) hands this
 * back to {@link BaseMCPOAuthProvider.handleCallback} to complete the
 * authorization-code exchange.
 */
export interface LoopbackCallback {
    code: string;
    state: string;
}
/**
 * Loopback redirect strategy — see module docstring.
 *
 * Lifecycle:
 *   - `new LoopbackRedirect(...)` binds the port (pending — `redirectUrl`
 *     is unavailable until `start()` resolves). Most callers should use
 *     {@link LoopbackRedirect.create} which awaits the bind for them.
 *   - {@link onAuthorizationRequired} opens the browser; non-blocking.
 *   - {@link awaitCallback} resolves on the first matching `GET` to
 *     `/oauth/callback`; rejects on idle timeout or fatal listener error.
 *   - The HTTP listener tears itself down once a callback (success or
 *     hard-error) is delivered. `close()` is safe to call multiple times
 *     and is a no-op once teardown has happened.
 */
export declare class LoopbackRedirect implements MCPOAuthRedirect {
    /** http://127.0.0.1:<port>/oauth/callback — assigned during {@link start}. */
    redirectUrl: string;
    private readonly server;
    private readonly noOpen;
    private readonly timeoutMs;
    private readonly openImpl;
    private readonly stderrWrite;
    private readonly desiredPort;
    private waiter;
    private closed;
    private started;
    constructor(opts?: LoopbackRedirectOptions);
    /**
     * Convenience constructor: binds the loopback port and resolves once
     * `redirectUrl` is ready. Most callers want this — `redirect_uris` must
     * include `redirectUrl` *before* OAuth discovery runs.
     */
    static create(opts?: LoopbackRedirectOptions): Promise<LoopbackRedirect>;
    /**
     * Bind the listener on `127.0.0.1` and populate {@link redirectUrl}.
     * Idempotent: a second call resolves immediately.
     */
    start(): Promise<void>;
    /**
     * SDK hook: open the user's browser to `authorizationUrl` (or print the
     * URL when `noOpen` is set). Returns immediately — the actual wait for
     * the callback happens via {@link awaitCallback}, which the caller drives.
     */
    onAuthorizationRequired(authorizationUrl: URL): Promise<void>;
    /**
     * Resolve when a valid `GET /oauth/callback?code=...&state=...` arrives
     * on the loopback listener. Rejects on:
     *   - idle timeout
     *   - listener fatal error
     *   - a second concurrent `awaitCallback()` call (one-shot)
     *
     * After resolving or rejecting, the listener is torn down and the port
     * is released. The returned `(code, state)` tuple is for the caller to
     * pass into {@link BaseMCPOAuthProvider.handleCallback}.
     */
    awaitCallback(): Promise<LoopbackCallback>;
    /**
     * Tear down the listener. Idempotent and safe to call after the waiter
     * has already resolved or rejected.
     */
    close(): Promise<void>;
    private handleRequest;
    private resolveWaiter;
    private failWaiter;
}
