/**
 * Runtime credential store.
 *
 * Phase 1 scope: consume `credentialBundle.body.tunnel` delivered by the
 * onboarding-service heartbeat and materialize it to the active profile's
 * private tunnel directory.
 *
 * Design notes:
 *   - The store is intentionally thin. It does not persist applied-version
 *     metadata on disk; instead it keeps a lightweight in-memory fingerprint
 *     to short-circuit redundant disk writes within a single process.
 *   - On a cold start the first heartbeat is a bootstrap heartbeat, so the
 *     server will always re-issue the current credential and the store will
 *     re-materialize files idempotently. We don't need a separate durable
 *     "applied version" marker.
 *   - Tunnel credentials ARE written to disk — `cloudflared` consumes them
 *     via `credentials-file`. Other credential kinds (Gemini, SerpAPI,
 *     and future LLM keys) are stored in two places: an in-memory
 *     tier-1 "bundle" variable inside their respective `*-credentials.ts`
 *     module (so resolvers see the new value immediately) AND a
 *     write-through cache in `config.json` (so the agent survives an
 *     offline onboarding service across a restart). Resolvers prefer
 *     the in-memory tier; the on-disk field is only consulted when both
 *     the bundle and the shared-keys tiers are empty.
 *   - Files are written with 0o600 permissions to keep the minimum exposure
 *     surface, and the enclosing directory is 0o700.
 */
import type { VisionClawConfig } from "../config/types.js";
import type { CredentialBundleBody, GoogleRuntimeCredential } from "../onboarding/onboarding-service-client.js";
export interface AppliedTunnelMaterialization {
    tunnelId: string;
    tunnelName: string;
    tunnelHostname: string;
    credentialsFile: string;
    configFile: string;
    configVersion?: string;
}
/**
 * Apply a server-delivered Google credential to disk.
 * Writes gmail password to config.json (gmailPassword field) and
 * atomically writes google-tokens.json if oauthTokensJson is provided.
 * Both writes are idempotent - only writes if content has changed.
 */
export declare function applyGoogleRuntimeCredential(cred: GoogleRuntimeCredential): boolean;
/**
 * Atomic read-mutate-write of a single top-level field in config.json.
 *
 * This is the single chokepoint for "authoritative server-issued credentials"
 * writes — currently per-agent bundle credentials (SerpAPI / Gemini / Bedrock,
 * future LLM keys). These overwrite whatever's on disk because the bundle is
 * the source of truth for that agent.
 *
 * Shared keys take the softer `cacheConfigFieldIfMissing` path below: they
 * only seed an empty slot and never overwrite an existing value, so admin or
 * per-agent configuration on disk cannot be silently flipped by a global
 * shared key.
 *
 * Routing all writers through these helpers avoids the previous race window
 * where heartbeat-manager re-serialised its in-memory `this.config`
 * snapshot while runtime-credentials was independently re-reading and
 * rewriting the same file. Each writer now touches only its own field.
 *
 * Empty/undefined values are ignored by client policy: server-issued null or
 * whitespace must never clear a local value.
 */
export declare function cacheConfigField(field: string, value: string | undefined, options?: {
    logLabel?: string;
}): boolean;
/**
 * "Persist only when missing" variant used by the shared-keys path.
 *
 * Writes `value` to `config.json[field]` only if the existing value is
 * absent or empty. Useful as a first-time seed for offline survival without
 * letting later heartbeats clobber values set by `reconfigure`, by a
 * per-agent bundle apply, or by an operator editing config.json directly.
 *
 * `value === undefined` is a no-op (we never clear an existing field via
 * this path; shared-key deletion is currently observation-only).
 */
export declare function cacheConfigFieldIfMissing(field: string, value: string | undefined, options?: {
    logLabel?: string;
}): boolean;
export interface ApplyRuntimeCredentialsResult {
    tunnelApplied: boolean;
    tunnel: AppliedTunnelMaterialization | null;
    /**
     * True iff the tunnel credential apply wrote at least one file to
     * disk in this call. Idempotent re-applies (or bundles without a
     * `tunnel` field) return false. Callers use this to decide whether
     * to bounce cloudflared so it picks up the new credential.
     */
    tunnelChanged: boolean;
    /**
     * The tunnel UUID observed on disk *before* this apply ran, or null
     * when no `config.yml` existed. Meaningful only when `tunnelApplied`
     * is true. Lets callers distinguish a tunnel rotation from a pure
     * credentialsJson refresh.
     */
    previousTunnelId: string | null;
    /** True iff this apply wrote at least one Google credential file to disk. */
    googleApplied: boolean;
    googleChanged: boolean;
    serpApiApplied: boolean;
    serpApiChanged: boolean;
    geminiApplied: boolean;
    geminiChanged: boolean;
    bedrockApplied: boolean;
    bedrockChanged: boolean;
    ninerouterApplied: boolean;
    ninerouterChanged: boolean;
}
export interface ApplyRuntimeCredentialsOptions {
    /**
     * The agent's own canonical name (i.e. `config.agentName`). When
     * provided, tunnel credentials whose `tunnelName` does not match are
     * rejected instead of materialised. Callers from the heartbeat path
     * should always pass this; tests may omit it to keep existing fixtures
     * terse.
     */
    agentName?: string;
    config?: VisionClawConfig;
}
/**
 * Apply a server-delivered credential bundle body. Tolerates a missing
 * or undefined body (returns a no-op result). Never throws on IO errors
 * — callers use this from the heartbeat critical path and a failed
 * materialisation must not block heartbeats or shut the agent down.
 */
export declare function applyRuntimeCredentials(bundle: CredentialBundleBody | undefined, options?: ApplyRuntimeCredentialsOptions): ApplyRuntimeCredentialsResult;
export interface LocalTunnelCredentialSnapshot {
    tunnelId: string;
    tunnelName: string;
    credentialsFile: string;
    configFile: string;
}
/**
 * Load the current in-memory snapshot of the applied tunnel credential,
 * if any. Returns `null` when the agent has not yet received or
 * materialized a tunnel credential in this process.
 */
export declare function getAppliedTunnelCredential(): AppliedTunnelMaterialization | null;
/**
 * Detect whether the active profile currently has valid tunnel credential
 * files on disk (written by a previous heartbeat or process). Used to
 * decide whether the agent should request a bootstrap refresh on startup.
 *
 * Returns a snapshot that callers can feed directly into the tunnel
 * runtime, or `null` if the files are missing or unreadable.
 */
export declare function probeLocalTunnelCredential(): LocalTunnelCredentialSnapshot | null;
/**
 * Reset all in-memory state. Exposed for tests only.
 */
export declare function __resetRuntimeCredentialStoreForTests(): void;
//# sourceMappingURL=runtime-credentials.d.ts.map