/** * SSH Executor * * Builds the command string (sudo, working directory) and hands it to the * transport. How the command actually reaches the server is the runner's * job; retries and timeouts live there too, because only the transport * knows what actually broke. */ import { type ServerPassport } from '../runner/passport.js'; import type { SSHConfig } from '../utils/ssh-config.js'; /** Entry point to the transport's default timeout for tools: they get it here, not from the runner */ export declare const DEFAULT_TIMEOUT_MS = 30000; export interface SSHExecuteOptions { /** * Command execution timeout (ms). Zero means "no ceiling": that's how * commands whose duration is set by the data volume are invoked — hashing * a multi-gigabyte tree is not obligated to fit in the usual 30 seconds. */ timeout?: number; /** Working directory */ cwd?: string; /** Use sudo */ sudo?: boolean; /** * Safe to repeat after a transport failure. * Set only for reads: repeating a mutating command is more dangerous than its failure. */ idempotent?: boolean; /** Data fed to the command's stdin (e.g. a manifest for `sha256sum -c -`) */ stdin?: string | Buffer; /** * Cancellation coming from the client. The command receives it only where * aborting is safe: cleanup and file replacement run without the signal, * otherwise cancellation would stop the very code that cleans up after cancellation. */ signal?: AbortSignal; } export interface SSHExecuteResult { /** Command output (stdout) */ stdout: string; /** Errors (stderr) */ stderr: string; /** Exit code */ exitCode: number; /** * The output did not fit the transport buffer and is shown partially. * Such an answer must not be treated as complete — it looks trustworthy. */ truncated: boolean; } /** * SSH Executor for command execution */ export declare class SSHExecutor { /** * Execute command on remote server. * * A non-zero exit code is part of the result, not an error: `grep` with * no matches returns 1, and the caller decides for itself what that means. * * @param config - SSH configuration * @param command - Command to execute * @param options - Execution options * @returns Execution result */ execute(config: SSHConfig, command: string, options?: SSHExecuteOptions): Promise; /** * Execute a command that must succeed. * * For steps after which there is no going forward: a directory wasn't * created, a file wasn't renamed, permissions weren't applied. The * places where a failure means the operation failed are named explicitly here. */ executeChecked(config: SSHConfig, command: string, options?: SSHExecuteOptions): Promise; /** * The server's passport: which utilities are available on it. * * Requested from the transport rather than assembled here with its own * command: only the transport can run the probe past the gate on the * first command. Probing via `exec` would close the loop — commands * waiting at the gate would wait for the passport, and the passport would wait for the gate. */ passport(config: SSHConfig): Promise; /** * Whether commands can be sent through this config right now. * * A server whose shell is a vendor CLI counts as usable: it runs commands, it simply has * no POSIX utilities for the probe to use. */ testConnection(config: SSHConfig): Promise; }