# TermHog
Record terminal sessions and stream them to [PostHog](https://posthog.com) 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
```bash
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.
```rust no_run
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
```bash
cargo install termhog-cli
```
Or download a prebuilt binary for Linux, macOS or Windows from
[GitHub Releases](https://github.com/fyder-ai/termhog/releases).
Put the command to record after `--`:
```bash
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](#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