termhog-cli 0.1.1

Record terminal sessions as PostHog session replays
termhog-cli-0.1.1 is not a library.

TermHog

Record terminal sessions and stream them to PostHog as session replays.

TermHog runs CLI commands, outputs verbatim to the host terminal, and in the background projects the terminal screen into rrweb events that PostHog's session replay player understands.

Library

cargo add termhog

termhog::HogCommand mirrors std::process::Command, and the child behaves as if std had spawned it. Streams left inherited are recorded on their way to the terminal. Anything you redirect (a file, a pipe, null) is left alone.

use termhog::{HogCommand, TermHog};

fn main() -> termhog::Result<()> {
    // Always first: see "Background uploads" below.
    termhog::init();

    let outcome = TermHog::new("phc_xxx")
        .distinct_id("user-123")
        .status(HogCommand::new("npm").arg("test"))?;

    println!("exit: {}", outcome.status);
    println!("replay: {}", outcome.replay_url);
    Ok(())
}

TermHog::spawn returns a Recording handle (like std::process::Child) for piped streams, kill, wait and try_wait. It's synchronous, with no async runtime: call it from spawn_blocking in async code.

Background uploads

A slow network never holds up your program. Shortly after a command exits (0.3 seconds, or 20 in CI), whatever isn't uploaded yet is saved to the user's cache folder (termhog/ in it), and your program's own executable is started again, detached and silent, to finish the upload. termhog::init() is what does that work in the relaunched process (and then exits), so it must be the first thing in main: anything before it runs again in the uploader. spawn panics if init wasn't called. Anything a background upload can't finish (the machine shut down, say) is picked up by the next init. In CI, where leftover processes don't outlive the job, nothing is started in the background.

Windows

TermHog writes the helper executables it needs into the user's cache folder and runs them from there. Where only some folders may run executables, set TermHog::executable_dir to one of them. It must not be writable by other users, since TermHog runs what it finds there.

CLI

cargo install termhog-cli

Or download a prebuilt binary for Linux, macOS or Windows from GitHub Releases.

Put the command to record after --:

POSTHOG_API_KEY=phc_xxx termhog -- nvim
POSTHOG_API_KEY=phc_xxx termhog -- npm test

It prints a link to the replay.

Each setting is a flag, or the matching environment variable (an empty value counts as unset). Flags go before the --:

  • --api-key / POSTHOG_API_KEY: project token (the public phc_ token). Required.
  • --host / POSTHOG_HOST: ingestion host (default https://us.i.posthog.com). The known Cloud hosts auto-resolve their app host for replay links.
  • --ui-host / POSTHOG_UI_HOST: replay-UI host, for ingestion behind a proxy. Defaults to the ingestion host.
  • --distinct-id / POSTHOG_DISTINCT_ID: the person ID. Without it, recordings are anonymous, and PostHog creates no person profile for them.
  • --session-id / POSTHOG_SESSION_ID: the session ID, so external events can share it. Defaults to a fresh UUIDv7.
  • --executable-dir / TERMHOG_EXECUTABLE_DIR (Windows only): a folder TermHog may run its helper executables from (see Windows). Defaults to the user's cache folder.

When output isn't a terminal (CI logs, pipes), the recorded screen size comes from COLUMNS and LINES if both are set, and is 80x24 otherwise.

License

MIT