/**
 * Credential bundle coordinator.
 *
 * Owns every concern around server-issued `credentialBundle` deliveries
 * over the heartbeat:
 *
 *   - exposing the locally-applied signature so the heartbeat layer can
 *     send it back to the server (which decides whether to re-ship)
 *   - materialising delivered bundles to disk (via `runtime-credentials.ts`)
 *   - bouncing cloudflared when a tunnel rotation lands, with restart
 *     coalescing so concurrent rotations don't race
 *
 * Replaces the old `TunnelCredentialHandler` + `bootstrapCredentials`
 * flag heuristic. The signature path is server-authoritative and
 * forward-compatible: when a future credential kind is added to
 * `CredentialBundleBody`, the same signature mechanism naturally
 * detects + delivers it without any new wire fields.
 *
 * Persistence model — deliberately none. The applied signature lives
 * only in memory, which means **every fresh process gets a full bundle
 * delivery on its first heartbeat**. That's the recovery path for any
 * disk drift we can't enumerate in advance: a corrupted creds JSON, a
 * stale config.yml, a wiped profile dir, an operator-edited manifest —
 * doesn't matter. Restart the agent, the server hands us the current
 * bundle, we re-apply, we're back in sync. The cost is one bundle
 * download per process lifetime (a few KB), which is negligible.
 */
import type { CredentialBundle } from "../onboarding/onboarding-service-client.js";
import type { VisionClawConfig } from "../config/types.js";
import { TunnelRotationManager } from "./tunnel-rotation-manager.js";
export interface CredentialBundleHandlerOptions {
    /**
     * Getter that returns the agent's current canonical name. Called on
     * every `handle()` invocation so the handler validates each bundle
     * against the up-to-date `config.agentName` — important because the
     * name can change mid-process when the heartbeat sync detects
     * server-side drift (see `HeartbeatManager.runSend`).
     */
    getAgentName: () => string;
    getConfig?: () => VisionClawConfig;
    /**
     * Callback that bounces cloudflared (typically the `restartTunnel`
     * returned from `startObsServer`). Invoked when a newly materialised
     * tunnel credential differs from what's on disk — otherwise
     * cloudflared keeps running against the UUID it was spawned with
     * (it does not hot-reload `config.yml`).
     *
     * Optional so test harnesses and non-obs builds can skip the restart
     * side-effect entirely.
     */
    restartTunnel?: () => Promise<string | null>;
    obsPort?: number;
    onBedrockChanged?: () => void;
}
export declare class CredentialBundleHandler {
    private readonly getAgentName;
    private readonly getConfig?;
    private readonly restartTunnel?;
    private readonly onBedrockChanged?;
    readonly tunnelRotation: TunnelRotationManager;
    /**
     * The signature the agent will send to the server on the next
     * heartbeat. `undefined` means "I have no opinion, please send me
     * the bundle" — which is the state every fresh process starts in,
     * by design. Updated only after a successful apply.
     */
    private _appliedSignature;
    private _restartInFlight;
    /**
     * Set whenever a `tunnelChanged` apply lands while a previous restart
     * is still in flight. The in-flight restart already snapshotted disk
     * state at its start (`resolveTunnelOpts()` in `obs/server.ts`), so
     * the second rotation needs its own bounce — we re-kick from the
     * restart's `finally` block when this flag is set.
     *
     * Stored as the latest `ApplyRuntimeCredentialsResult` so the
     * rotation label in the follow-up log line still reflects the
     * *latest* tunnel delivery, not the first one already restarted.
     */
    private _pendingRestart;
    constructor(options: CredentialBundleHandlerOptions);
    /**
     * The signature to attach to the next heartbeat as
     * `appliedCredentialSignature`. `undefined` until the first
     * successful apply in this process — which forces the server to
     * ship the bundle on the startup heartbeat (the recovery path).
     */
    get appliedSignature(): string | undefined;
    /**
     * Handle the optional `credentialBundle` from a heartbeat response.
     *
     * Defensive by contract: never throws — a failed materialisation must
     * not block heartbeats. A missing/undefined bundle means "server
     * agrees with our applied signature": no work to do.
     */
    handle(bundle: CredentialBundle | undefined): void;
    /**
     * Kick `restartTunnel` in the background, coalescing concurrent
     * invocations. Logged at `system` level because a restart is a
     * visible event for operators tailing pm2 logs.
     *
     * If a restart is already in flight when we're called, we don't drop
     * the trigger — the in-flight restart sampled disk before this newer
     * credential was materialised and would launch cloudflared against
     * the stale UUID. Instead we record the latest result in
     * `_pendingRestart` and re-kick from the in-flight restart's
     * `finally` block, which guarantees one extra restart that picks up
     * whatever's now on disk.
     */
    private triggerRestart;
}
//# sourceMappingURL=credential-bundle-handler.d.ts.map