Skip to content

fourcolor/trec

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

trec

trec records terminal sessions into binary .trec files. It can replay a recording or generate a small C or Python script from the recorded input.

The project is still early. The commands below are meant to be run from the repository checkout. C examples include headers with -Iinclude; Python examples use PYTHONPATH=python and TREC_LIBRARY=$PWD/libtrec.so so they load the local binding and library.

Requirements

  • Linux
  • make
  • gcc
  • Python 3 for tests, the dump tool, and the Python binding
  • clang-format for make format

Build

make

This builds:

  • ./trec
  • ./libtrec.a
  • ./libtrec.so

Other common targets:

make test
make format
make clean

Quick start

Record a shell session:

./trec record --file session.trec

record starts $SHELL. Use the shell as usual, then press Ctrl-D to stop recording.

Replay the recorded input in a fresh shell:

./trec replay --file session.trec

Print the recorded output without running the commands again:

./trec replay --output-replay --file session.trec

Generate and run a C script:

./trec codegen --file session.trec --output session.c
cc -Wall -Wextra -g -Iinclude -L. -o session session.c -ltrec -lutil
./session

Generate and run a Python script:

./trec codegen --language python --file session.trec --output session.py
PYTHONPATH=python TREC_LIBRARY=$PWD/libtrec.so python3 session.py

Recording

./trec record [OPTIONS]
Option Meaning
-f, --file <file> Output file. Default: session.trec
-h, --help Show help

Recording stores input, output, resize events, and the shell exit status. It always starts $SHELL; if $SHELL is not set, it uses /bin/sh.

Replay

./trec replay [OPTIONS]
Option Meaning
-f, --file <file> Input .trec file. Required
--output-replay Print recorded output instead of injecting input into a shell
-h, --help Show help

Default replay starts a new $SHELL and sends the recorded input to it. It runs against the current filesystem, commands, and environment, so output can differ from the original session.

--output-replay prints the recorded output with recorded timing. It does not start a shell.

Codegen

./trec codegen [OPTIONS]
Option Meaning
-f, --file <file> Input .trec file. Required
-o, --output <file> Output file. Required
-l, --language <lang> Output language: c or python. Default: c
--timing-mode <mode> Timing mode: merge, interval, recorded, or none
--merge-threshold-ms <ms> Merge action gaps below this value. Default: 500
--interval-ms <ms> Fixed interval for interval timing mode
--no-timing Alias for --timing-mode none
--include-output-comments Include recorded output snippets as comments
--include-raw-comments Include undecoded input comments
--record-file <file> Make the generated script record its own run
--strict Fail if an input sequence cannot be decoded
-h, --help Show help

Basic C output:

./trec codegen --file demo.trec --output demo.c
cc -Wall -Wextra -g -Iinclude -L. -o demo demo.c -ltrec -lutil
./demo

Basic Python output:

./trec codegen --language python --file demo.trec --output demo.py
PYTHONPATH=python TREC_LIBRARY=$PWD/libtrec.so python3 demo.py

Record a generated script's run:

./trec codegen --file demo.trec --output demo.c --record-file demo-rerun.trec

If --record-file is omitted, generated scripts do not record their run.

Generated C sets the record file before starting the session:

struct trec_session s = {0};

trec_session_set_record_file(&s, "demo-rerun.trec");
trec_session_start_shell(&s, rows, cols);

Generated Python uses the same order:

with Session(record_file="demo-rerun.trec") as s:
    s.start_shell(rows, cols)

Decoded input becomes script calls:

trec_send_text(&s, "echo hello");
trec_send_key(&s, TREC_KEY_ENTER);
trec_click_at(&s, 10, 5, TREC_MOUSE_LEFT);
trec_drag(&s, 2, 3, 8, 9, TREC_MOUSE_LEFT);
trec_resize(&s, 40, 120);
trec_sleep_ms(&s, 250);
with Session() as s:
    s.start_shell()
    s.send_text("echo hello")
    s.send_key(Key.ENTER)
    s.click_at(10, 5, MouseButton.LEFT)

If input cannot be decoded, codegen writes raw bytes unless --strict is set.

Timing modes

The default mode is merge. It drops short gaps below 500 ms and combines adjacent text input:

./trec codegen --file demo.trec --output demo.c

Use a different merge threshold:

./trec codegen --timing-mode merge --merge-threshold-ms 1000 --file demo.trec --output demo.c

Use a fixed delay between actions:

./trec codegen --timing-mode interval --interval-ms 50 --file demo.trec --output demo.c

Preserve recorded action gaps:

./trec codegen --timing-mode recorded --file demo.trec --output demo.c

Emit no sleeps:

./trec codegen --timing-mode none --file demo.trec --output demo.c
./trec codegen --no-timing --file demo.trec --output demo.c

Runtime screen APIs

Scripts can wait for text, find text, click a found position, or read the visible screen.

C:

uint16_t x = 0;
uint16_t y = 0;

if (trec_expect_text(&s, "Submit", 3000) == 1 &&
    trec_find_text(&s, "Submit", &x, &y) == 1) {
    trec_click_at(&s, x, y, TREC_MOUSE_LEFT);
}

Python:

with Session() as s:
    s.start_shell()
    if s.expect_text("Submit", 3000):
        pos = s.find_text("Submit")
        if pos is not None:
            x, y = pos
            s.click_at(x, y)

        for x, y in s.find_all_text("Submit"):
            print("Submit at", x, y)

trec_find_text and Session.find_text() return the first match. trec_find_all_text and Session.find_all_text() return all visible matches. trec_snapshot_text and Session.snapshot_text() return the visible screen as plain text. trec_snapshot and Session.snapshot() return cell data, including size, cursor position, text, width, attributes, and supported colors.

Python binding

The Python binding uses ctypes and loads libtrec.so.

make
PYTHONPATH=python TREC_LIBRARY=$PWD/libtrec.so python3 script.py

Example:

from trec import Key, Session

with Session() as session:
    session.start_shell()
    session.send_text("printf 'button\\n'\n")
    session.expect_text("button", 3000)
    pos = session.find_text("button")
    if pos is not None:
        x, y = pos
        session.click_at(x, y)
    print(session.snapshot_text())
    session.send_text("exit\n")
    raise SystemExit(session.close())

Use Session as a context manager. If the with block raises, the session terminates the child process and restores the terminal state.

Inspect .trec files

Use tools/trec_dump.py:

python3 tools/trec_dump.py demo.trec
python3 tools/trec_dump.py --payload-limit 80 demo.trec
python3 tools/trec_dump.py --no-hex demo.trec
python3 tools/trec_dump.py --format json demo.trec
python3 tools/trec_dump.py --format yaml demo.trec

Mouse input

Mouse input is recorded when the application inside the shell enables mouse reporting. Decoded mouse input can produce trec_click_at, trec_mouse_down, trec_mouse_up, trec_drag, and trec_mouse_scroll calls.

Current limitations

  • Linux-only alpha
  • unstable .trec file format
  • unstable generated script APIs
  • no package manager installation yet
  • recording launches $SHELL; it does not accept a target command
  • terminal behavior is tested mainly against xterm-like terminals

TODO

  • snapshot image export
  • terminal profiles for iTerm2, Ghostty, WezTerm, kitty, and others
  • image protocols such as Sixel and kitty image protocol
  • Windows and macOS support

License

trec is licensed under Apache-2.0. The vendored vendor/libvterm dependency uses the MIT license in vendor/libvterm/LICENSE.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors