/**
 * Hetzner Cloud API client
 * @see https://docs.hetzner.cloud/
 */
import type { CloudConfig } from '@ts-cloud/core';
export interface HetznerApiErrorBody {
    error?: {
        code?: string;
        message?: string;
        details?: unknown;
    };
}
export interface HetznerServer {
    id: number;
    name: string;
    status: string;
    public_net: {
        ipv4?: {
            ip: string;
        };
        ipv6?: {
            ip: string;
        };
    };
    private_net?: Array<{
        ip: string;
    }>;
    labels?: Record<string, string>;
    server_type: {
        name: string;
    };
    datacenter: {
        name: string;
        location: {
            name: string;
        };
    };
}
export interface HetznerFirewall {
    id: number;
    name: string;
    labels?: Record<string, string>;
    rules?: HetznerFirewallRule[];
}
export interface HetznerFirewallRule {
    direction: 'in' | 'out';
    protocol: 'tcp' | 'udp' | 'icmp' | 'esp' | 'gre';
    port?: string;
    source_ips: string[];
    description?: string;
}
export interface HetznerSshKey {
    id: number;
    name: string;
    fingerprint: string;
    public_key: string;
    labels?: Record<string, string>;
}
export interface HetznerAction {
    id: number;
    status: 'running' | 'success' | 'error';
    progress?: number;
    error?: {
        code: string;
        message: string;
    };
}
export interface CreateServerOptions {
    name: string;
    serverType: string;
    image: string;
    location?: string;
    datacenter?: string;
    sshKeys?: number[];
    userData?: string;
    labels?: Record<string, string>;
    firewalls?: Array<{
        firewall: number;
    }>;
    /** Private networks to attach the server to (fleet topology). */
    networks?: number[];
}
export interface HetznerNetwork {
    id: number;
    name: string;
    ip_range: string;
}
export interface HetznerLoadBalancer {
    id: number;
    name: string;
    public_net?: {
        ipv4?: {
            ip?: string;
        };
    };
}
export interface CreateNetworkOptions {
    name: string;
    ipRange?: string;
    labels?: Record<string, string>;
}
export interface CreateLoadBalancerOptions {
    name: string;
    /** LB type (e.g. 'lb11'). @default 'lb11' */
    type?: string;
    location?: string;
    networkZone?: string;
    network?: number;
    labels?: Record<string, string>;
    /** Listener services (e.g. 80→80, 443→443). */
    services: Array<{
        listenPort: number;
        destinationPort: number;
        protocol?: 'tcp' | 'http';
    }>;
    /** Target app servers by label selector (e.g. `ts-cloud/role=app`). */
    labelSelector: string;
}
export interface CreateFirewallOptions {
    name: string;
    rules: HetznerFirewallRule[];
    labels?: Record<string, string>;
    applyTo?: Array<{
        type: 'server';
        server: number;
    }>;
}
export interface CreateSshKeyOptions {
    name: string;
    publicKey: string;
    labels?: Record<string, string>;
}
/** Minimal fetch signature the client relies on (always called with a string URL). */
export type HetznerFetch = (url: string, init?: RequestInit) => Promise<Response>;
export interface HetznerClientOptions {
    apiToken: string;
    baseUrl?: string;
    fetchImpl?: HetznerFetch;
}
export declare class HetznerClient {
    readonly name = "hetzner";
    private apiToken;
    private baseUrl;
    private fetchImpl;
    constructor(options: HetznerClientOptions);
    private request;
    /**
     * Fetch every page of a list endpoint. Hetzner paginates lists at 25 items by
     * default (max 50/page) and follows `meta.pagination.next_page`; without this,
     * an account with more than one page of resources silently truncates — target
     * lookups miss the project's own servers, and find-or-create attempts a
     * duplicate create (409) for firewalls/keys that already exist.
     */
    private requestAll;
    listServers(): Promise<HetznerServer[]>;
    getServer(id: number): Promise<HetznerServer>;
    createServer(options: CreateServerOptions): Promise<{
        server: HetznerServer;
        action: HetznerAction;
    }>;
    listNetworks(): Promise<HetznerNetwork[]>;
    createNetwork(options: CreateNetworkOptions): Promise<HetznerNetwork>;
    deleteNetwork(id: number): Promise<void>;
    listLoadBalancers(): Promise<HetznerLoadBalancer[]>;
    createLoadBalancer(options: CreateLoadBalancerOptions): Promise<HetznerLoadBalancer>;
    deleteLoadBalancer(id: number): Promise<void>;
    deleteServer(id: number): Promise<HetznerAction>;
    listFirewalls(): Promise<HetznerFirewall[]>;
    createFirewall(options: CreateFirewallOptions): Promise<{
        firewall: HetznerFirewall;
        actions: HetznerAction[];
    }>;
    /**
     * Replace a firewall's rule set in place. Used to keep an existing (reused)
     * firewall's rules in sync with the desired config without recreating it.
     */
    setFirewallRules(firewallId: number, rules: HetznerFirewallRule[]): Promise<HetznerAction[]>;
    /** Delete a firewall. A firewall still applied to a server cannot be deleted. */
    deleteFirewall(firewallId: number): Promise<void>;
    applyFirewallToResources(firewallId: number, applyTo: Array<{
        type: 'server';
        server: number;
    }>): Promise<HetznerAction[]>;
    listSshKeys(): Promise<HetznerSshKey[]>;
    createSshKey(options: CreateSshKeyOptions): Promise<HetznerSshKey>;
    waitForAction(actionId: number, options?: {
        pollIntervalMs?: number;
        maxWaitMs?: number;
    }): Promise<HetznerAction>;
    waitForServerRunning(serverId: number, options?: {
        pollIntervalMs?: number;
        maxWaitMs?: number;
    }): Promise<HetznerServer>;
}
/**
 * The Hetzner API token, or a clear error when none is configured.
 *
 * Resolution itself lives in {@link import('./config').resolveHetznerApiToken}
 * so the driver, the client and the dashboard cannot drift apart on where a
 * setting comes from.
 */
export declare function resolveHetznerApiToken(configToken?: string, config?: CloudConfig): string;
/**
 * Normalize an OpenSSH public key to its `<type> <base64>` body, dropping the
 * trailing comment. Lets us match a local key against keys already registered
 * in the Hetzner project regardless of differing comments/whitespace.
 */
export declare function normalizeSshPublicKey(publicKey: string): string;
