import { type DeviceProfile } from './registry.js';
/** Env var the askpass shim reads to know which bundle holds the password. */
export declare const ASKPASS_BUNDLE_ENV = "AGENTS_SSH_BUNDLE";
/** Env var the askpass shim reads to know which key in the bundle is the password. */
export declare const ASKPASS_KEY_ENV = "AGENTS_SSH_KEY";
/**
 * Env var that forces the askpass bundle resolve to be broker-only (`agentOnly`)
 * regardless of TTY. A read-only stats probe (`agents devices` load/mem columns)
 * sets this so an uncached password-auth device resolves from the already-unlocked
 * secrets broker or degrades to an unreachable row — never a foreground Touch ID
 * sheet. See {@link buildSshInvocation}'s `agentOnly` option and `runAskpass`. (RUSH-1970)
 */
export declare const ASKPASS_AGENT_ONLY_ENV = "AGENTS_SSH_AGENT_ONLY";
/**
 * Build the `user@host` (or bare `host`) ssh target for a device and validate
 * it against the shared injection guard. Throws if the device has no address.
 */
export declare function sshTargetFor(device: DeviceProfile): string;
/**
 * The address a fleet fan-out probe should hand to `ssh` for a device: the
 * registry's known-good Tailscale dnsName/IP (via {@link sshTargetFor}) when
 * present, else the bare name (an address-less manual device dials by name as
 * before — never worse than the old behaviour).
 *
 * Prefer the registry address so a stale `~/.ssh/config` alias can never shadow
 * it: dialing the bare device name lets ssh resolve it through the user's config,
 * where a hand-written `Host <name>` block with a DHCP-drifted LAN IP silently
 * shadows the correct entry, times out, and makes a reachable box look dead.
 * Pure/testable.
 */
export declare function fleetDialTarget(device: DeviceProfile): string;
/**
 * Wrap a remote command for the device's shell. Windows devices speak
 * PowerShell, so a bare command is run through `powershell -NoProfile
 * -EncodedCommand`; POSIX devices get the command verbatim (the remote login
 * shell parses it). Returns undefined when no command was given (interactive
 * login).
 */
export declare function wrapRemoteCommand(device: DeviceProfile, cmd: string[]): string | undefined;
/**
 * True when `cmd` is a browser drive: `agents browser …`, `ag browser …`, or
 * the standalone `browser` binary (`cli/package.json` `bin.browser` →
 * `src/browser.ts`), including the quoted single-string form
 * `agents ssh box 'browser …'`. The `--device` passthrough stamps
 * {@link markFleetRemote} on this shape so the far-side consent gate can fire;
 * `agents ssh` must too (PHNX-3065).
 */
export declare function isAgentsBrowserDrive(cmd: string[]): boolean;
/**
 * Prefix a remote command with the provenance the far-side browser needs: the
 * `AGENTS_FLEET_REMOTE=1` consent marker AND the caller's resolved actor
 * (`AGENTS_ACTOR*`/`GIT_*`). Without the actor, a task created over
 * `agents ssh <host> 'agents browser …'` is stamped `UNRESOLVED@<host>` because
 * the peer re-resolves identity from its own (empty) env — the same RUSH-2028
 * gap the `--device` dispatch already closes via `withActorEnv`, applied here to
 * the `agents ssh` browser-drive seam so ownership is honest across BOTH paths.
 *
 * `wrapRemoteCommand` joins the argv with spaces (POSIX) or base64-encodes it for
 * PowerShell, so a shell-appropriate prelude rides through both: `env VAR=v …` on
 * POSIX, `$env:VAR='v'; …` on PowerShell. The marker stays the FIRST token so the
 * consent gate and the idempotency guard below both still key on it.
 *
 * `provenanceEnv` is injectable so tests can pin a deterministic actor; it
 * defaults to the live resolved actor. Already-marked argv (the `--device`
 * fan-out stamps the marker before {@link buildSshInvocation}) is left unchanged
 * so nothing is doubled.
 */
export declare function markFleetRemote(cmd: string[], device: Pick<DeviceProfile, 'shell'>, provenanceEnv?: Record<string, string>): string[];
/**
 * Build the remote command that starts an INTERACTIVE LOGIN shell inside a
 * mirrored project directory, falling back to the remote home when that
 * directory is absent. Returns undefined when there is nothing to mirror
 * (`mirrorCwd` is undefined, or resolves to the home root itself) so the caller
 * keeps the plain no-command interactive login.
 *
 * This is the interactive analogue of `agents run --device`'s cwd mirroring, and
 * it reuses the SAME machinery so there is no second resolver: the portable
 * `mirrorCwd` comes from `deriveMirroredCwd`, and — for POSIX — the best-effort
 * `cd` comes from `remoteCdPrefix({ mirror: true })`, whose `|| cd "$HOME"`
 * guarantees a missing checkout never fails the login (acceptance #2). It then
 * replaces the wrapper with a login shell (`exec "$SHELL" -l`); running under
 * the forced tty (`-tt`, see {@link buildSshInvocation}) makes that login shell
 * interactive, so prompt, startup files, and login behavior match a plain
 * `ssh <host>` (acceptance #1).
 *
 * PowerShell hosts get a profile-loading interactive shell (`-NoExit`, and
 * deliberately NOT `-NoProfile` so the user's profile still runs) that
 * `Set-Location`s into the mirrored dir when it exists. The path is carried
 * through `-EncodedCommand` (base64 UTF-16LE), so it is literal and
 * injection-safe regardless of spaces or shell metacharacters (acceptance #3).
 */
export declare function buildInteractiveShellCommand(device: DeviceProfile, mirrorCwd: string | undefined): string | undefined;
/** Host-key posture for {@link buildSshInvocation}. */
export interface SshHostKeyOptions {
    /**
     * True when the device's host key is already pinned in the managed
     * known_hosts store — connections then verify with `StrictHostKeyChecking=yes`
     * (a key swap is refused). False (the default) keeps `accept-new` for a
     * genuine first enrollment, whose learned key lands in the managed store and
     * pins the host for every subsequent connect. See {@link hostKeyCheckingOpts}.
     */
    pinned?: boolean;
    /** Managed known_hosts path override (tests). Defaults to the CLI-managed store. */
    knownHostsFile?: string;
}
/** OpenSSH argv that makes an explicit device key authoritative. */
export declare function deviceIdentityArgs(device: DeviceProfile): string[];
/**
 * Build the argv (after the `ssh` program name) and the environment overlay
 * for connecting to a device. For password auth this points `SSH_ASKPASS` at
 * the shim and disables pubkey + the host's interactive password prompt so the
 * shim is the only auth path. Pure (no spawn) so it is unit-testable.
 *
 * Host-key checking runs against the CLI-managed known_hosts store (never the
 * user's `~/.ssh/known_hosts`): strict once `hostKey.pinned` is set, else
 * `accept-new` to learn+pin the key on first connect (RUSH-1767).
 *
 * `opts.agentOnly` marks the connection as a read-only probe: for password auth
 * it sets {@link ASKPASS_AGENT_ONLY_ENV} in the overlay so the askpass resolve
 * stays broker-only and never pops a foreground biometric (RUSH-1970).
 *
 * `opts.interactiveCwd` is the portable (`~/…`) directory to mirror on an
 * interactive login (no `cmd`) — from `deriveMirroredCwd(process.cwd())`. When
 * it names a real home-relative sub-path, the login starts there via
 * {@link buildInteractiveShellCommand} (best-effort — a missing dir falls back
 * to the remote home), matching `agents run --device`. It is ignored when a `cmd`
 * is given: an explicit command keeps its current cwd (the remote home) and its
 * behavior unchanged (RUSH-2412).
 *
 * An `agents browser …` / `ag browser …` / standalone `browser …` command is
 * prefixed with {@link markFleetRemote} so the far-side consent gate sees
 * `AGENTS_FLEET_REMOTE=1` the same way `browser --device` does. The local `env`
 * overlay here is for the ssh *client* (askpass); it cannot carry the marker —
 * OpenSSH does not forward arbitrary env (PHNX-3065).
 */
export declare function buildSshInvocation(device: DeviceProfile, cmd: string[], askpassShimPath: string, hostKey?: SshHostKeyOptions, opts?: {
    agentOnly?: boolean;
    interactiveCwd?: string;
}): {
    args: string[];
    env: Record<string, string>;
};
/**
 * Build the askpass shim's `#!/bin/sh` body: a script that re-invokes this CLI
 * as `agents ssh __askpass`. The relaunch argv comes from {@link getCliLaunch},
 * never a hand-rolled `[process.execPath, process.argv[1], …]` — on a Bun
 * standalone binary `process.argv[1]` is the *virtual* embedded entry
 * `/$bunfs/root/agents`, which the CLI would then receive as a bogus subcommand
 * (`unknown command '/$bunfs/root/agents'`), print nothing, and hand ssh an
 * empty password. `getCliLaunch` resolves the physical executable so the shim
 * works on both the standalone and JS/dev builds. Every argv element is
 * shell-quoted. Pure (takes the launch as a parameter) so it is unit-testable.
 */
export declare function buildAskpassShimBody(launch?: {
    command: string;
    args: string[];
}): string;
/**
 * Write (idempotently) the askpass shim — a tiny executable that re-invokes
 * this CLI as `agents ssh __askpass`. ssh execs `SSH_ASKPASS` with no usable
 * args, so the shim carries no secret itself; it only bridges ssh's askpass
 * protocol back into the CLI, which then resolves the bundle.
 */
export declare function writeAskpassShim(): string;
