/**
 * Host provider registry + the single host/device resolver.
 *
 * Mirrors the cloud provider registry: a Map of provider id → implementation,
 * instantiated once. Registers `local` then `devices`; adding `rush`/`crabbox`
 * later is a one-line `providers.set(...)`.
 *
 * Resolution used to fork into two disagreeing chains — a local-provider-first
 * `resolveHost` (this file) and a devices-only `resolveSshTarget`
 * (`../devices/resolve-target.ts`). They dialed different boxes for the same
 * token (RUSH-1967). Now every caller goes through one core, {@link matchHost}:
 * it merges the two directories per-FIELD instead of letting one provider
 * shadow the other — the live devices registry supplies address/OS/presence,
 * the agents.yaml overlay supplies caps/hints, and ssh_config supplies hosts
 * Tailscale has never seen. The typed wrappers below (`resolveHost`) and in
 * resolve-target.ts (`resolveExplicitTargets`, `resolveDeviceTarget`) differ
 * only in what shape they hand back and which literal fallbacks they permit.
 */
import type { Host, HostProvider, HostProviderId } from './types.js';
import { DeviceOffloadUnsupportedError } from './types.js';
import { type DeviceProfile } from '../devices/registry.js';
import { type DeviceAffinityPlan } from '../smart-launch.js';
export { DeviceOffloadUnsupportedError };
export declare function getProvider(id: HostProviderId): HostProvider;
export declare function getAllProviders(): HostProvider[];
/**
 * A resolved host, carrying the live {@link DeviceProfile} when the token
 * matched a registered device (by normalized name). The device reference lets
 * the `agents ssh` wrapper reach auth/shell/tailscale metadata a plain `Host`
 * doesn't hold, and lets dispatch callers apply the device-only refusal
 * (password auth) without re-reading the registry.
 */
export interface ResolvedHost extends Host {
    device?: DeviceProfile;
    /** True when the host is a synthesized ad-hoc `user@host` / IP / FQDN literal
     * (never registered) rather than a device or overlay/ssh-config match. Lets the
     * strict `agents ssh` wrapper accept devices + literals but still report
     * "Unknown device" for a bare ssh-config-only alias, as it always has. */
    adhoc?: boolean;
}
/** Split a `user@host` / `host` token into its login user (if any) and host part. */
export declare function splitUserHost(token: string): {
    user?: string;
    host: string;
};
/** Literal-fallback policy for {@link matchHost}. */
export interface MatchHostOptions {
    /**
     * Also treat an unmatched dotted/colon host literal (a raw IP or FQDN with no
     * `user@`) as an ad-hoc target. `agents ssh 1.2.3.4` sets this; dispatch and
     * fan-out leave it off, so only a `user@host` is taken as an ad-hoc literal
     * (a bare/dotted unknown is a miss, keeping capability-tag routing and the
     * "Unknown device" verdict reachable).
     */
    allowBareLiteral?: boolean;
    /** Override the affinity pick for generic `auto` host resolution in tests.
     * Harness-aware run/team placement resolves `auto` before reaching this core. */
    resolveAuto?: () => DeviceAffinityPlan;
}
/**
 * The one place a `--device` / `--device` token becomes a resolved host. Reads the
 * devices registry, the agents.yaml overlay, and ssh_config, and merges them
 * per-field (see {@link deviceHost}). One grammar for every caller: `name`,
 * `user@name`, a tailnet FQDN, an ssh_config alias, and a literal `user@host`
 * all resolve the same way regardless of which subcommand called.
 *
 * Non-throwing: returns null on an injection-guard failure or an unresolved bare
 * token. The device-only refusal (password auth) is NOT applied
 * here — that is the dispatch wrapper's job ({@link resolveHost}), so the fan-out
 * and `agents ssh` paths, which handle those cases differently, aren't forced
 * into a dispatch verdict.
 */
export declare function matchHost(name: string, opts?: MatchHostOptions): Promise<ResolvedHost | null>;
/** Every host across all registered providers, merged by name so a device's
 * presence/dispatchable/address and an overlay's caps coexist on one row
 * (RUSH-1967: first-wins dedup used to drop the device row, and its status/caps
 * with it). Provider order still decides the base row (`local` first). */
export declare function listAllHosts(): Promise<Host[]>;
/**
 * Resolve a `--device`/`--device` token for DISPATCH — the shape every offload
 * caller consumes (`run --device`, the generic passthrough, teams placement, the
 * cloud host provider, doctor, funnel, remote secrets). Grammar and merge come
 * from {@link matchHost}; on top, this layer applies the device-only dispatch
 * refusal so it holds even when an inline overlay shadows the device:
 *   - a password-auth device throws the typed {@link DeviceOffloadUnsupportedError}
 *     (offload rides `BatchMode=yes` ssh, which can't answer a prompt);
 *   - an addressless device has nothing to dial.
 *
 * A bare unknown name returns null so capability-tag routing (`resolveHostByCap`,
 * e.g. `--device gpu`) stays reachable, and an ad-hoc `user@host` still resolves.
 */
export declare function resolveHost(name: string): Promise<Host | null>;
/**
 * Resolve a host by capability tag (e.g. `--device gpu`). Returns the single
 * matching host, or throws on 0 or >1 matches unless `any` is set (then first).
 */
export declare function resolveHostByCap(cap: string, any?: boolean): Promise<Host>;
