export interface IngressRule {
    hostname: string;
    service: string;
}
export interface CloudflaredTunnelConfig {
    tunnel: string;
    credentialsFile: string;
    ingress: IngressRule[];
}
/**
 * Options for isolating cloudflared invocations and config files to a
 * non-default location (used by dev profiles). When omitted, the default
 * `~/.cloudflared/*` paths are used, matching production behaviour exactly.
 */
export interface CloudflaredIsolationOpts {
    /** Path to the cert.pem used for `TUNNEL_ORIGIN_CERT`. */
    originCert?: string;
    /** Path to the cloudflared config.yml to read/write. */
    configPath?: string;
    /** Explicit credentials-file path to write into config.yml. */
    credentialsFile?: string;
}
/**
 * Normalize an agent name into a valid Cloudflare tunnel name.
 * Must match the DNS label syntax: lowercase letters, digits and hyphens only,
 * no leading/trailing hyphens, no consecutive hyphens. Falls back to
 * "visionclaw" when the input sanitizes to an empty string.
 */
export declare function sanitizeTunnelName(agentName: string): string;
/**
 * Resolve the UUID for a named tunnel via `cloudflared tunnel info`.
 * Returns undefined if the tunnel doesn't exist or the command fails.
 */
export declare function resolveTunnelId(tunnelName: string, opts?: CloudflaredIsolationOpts): string | undefined;
/**
 * Ask cloudflared whether a named tunnel already has at least one active
 * connector registered with Cloudflare. Used to avoid spawning a second
 * connector for the same tunnel (which makes the edge flap between origins
 * and surfaces as ERR_CONNECTION_CLOSED / 530 in browsers).
 *
 * Implemented by parsing `cloudflared tunnel info <name>` rather than the
 * GraphQL API so it works with nothing more than the cert.pem we already
 * have. The output has two UUID-bearing sections:
 *
 *     NAME:     audreyblake
 *     ID:       <tunnel-uuid>                     ← Skip
 *     CREATED:  ...
 *
 *     CONNECTOR ID                         CREATED              ...
 *     <connector-uuid>                     <timestamp>          ...
 *
 * We detect the CONNECTOR table header and look for any UUID on a subsequent
 * line. When the tunnel has no connectors, the table body is empty.
 *
 * Returns:
 *   - `true` when at least one connector UUID is listed in the table
 *   - `false` when the table is empty (tunnel exists but nobody's connected)
 *   - `undefined` if `cloudflared tunnel info` failed entirely (so callers
 *      can decide whether to be optimistic or pessimistic rather than
 *      mistaking a transient failure for "no connectors").
 */
export declare function tunnelHasActiveConnector(tunnelName: string, opts?: CloudflaredIsolationOpts): boolean | undefined;
/**
 * Pure helper split out from `tunnelHasActiveConnector` so it can be unit
 * tested with fixtures of real `cloudflared tunnel info` output without
 * spawning the binary.
 */
export declare function parseHasActiveConnector(stdout: string): boolean;
/**
 * Parse a simple cloudflared config YAML.
 * Only supports the subset we write: tunnel, credentials-file, and ingress list.
 *
 * Exposed for the legacy-tunnel-adoption flow, which reads the user's
 * pre-existing `~/.cloudflared/config.yml` to materialise the same
 * fields into the agent profile's private tunnel dir.
 */
export declare function parseCloudflaredConfigYaml(content: string): CloudflaredTunnelConfig;
/**
 * Add or update an ingress rule for a hostname.
 * The catch-all rule (no hostname, typically `http_status:404`) is always kept last.
 * If a rule for the hostname already exists, its service is updated in place.
 */
export declare function upsertIngressRule(config: CloudflaredTunnelConfig, hostname: string, service: string): CloudflaredTunnelConfig;
/**
 * Write a fresh cloudflared config.yml for a profile-managed tunnel. Unlike
 * `ensureIngressRule` this always rewrites the file with the exact tunnel
 * UUID and credentials-file path — callers use it right after materializing
 * a runtime-delivered credential blob, where any pre-existing config.yml in
 * the profile dir is assumed to be stale.
 *
 * `rules` carries the full list of named ingress rules to write, each with
 * its own `service` target. This matters for legacy migration: one tunnel
 * may serve several hostnames (e.g. `jessica.visionclawobs.com` →
 * `http://localhost:3101` for obs, and `hailong-polymarket.visionclawobs.com`
 * → `http://localhost:8888` for an unrelated local service). Collapsing all
 * of those onto a single service would silently break the non-obs routes.
 *
 * Rules are de-duplicated by hostname case-insensitively, keeping the first
 * occurrence. Blank hostnames are dropped. The catch-all
 * `http_status:404` rule is always appended last.
 */
export declare function writeProfileTunnelConfig(opts: {
    configPath: string;
    tunnelId: string;
    credentialsFile: string;
    rules: {
        hostname: string;
        service: string;
    }[];
}): void;
/**
 * Ensure the target cloudflared `config.yml` exists and contains an ingress
 * entry for the given hostname. Creates the file if missing, merges the entry
 * if it exists, and does nothing if it already points to the correct service.
 *
 * When `opts.configPath` is provided, reads/writes that file (and mkdirs its
 * parent). Otherwise writes to `~/.cloudflared/config.yml`, matching legacy
 * production behaviour byte-for-byte.
 */
export declare function ensureIngressRule(tunnelId: string, hostname: string, service: string, opts?: CloudflaredIsolationOpts): void;
//# sourceMappingURL=cloudflared-config.d.ts.map