Record an interactive session until it exits (asciinema rec-style record) #5

Closed
opened 2026-09-26 12:15:19 +09:00 by natsukium · 1 comment
Owner

Motivation

record only captures a non-interactive command. It creates a session running sh -c <command>, and stops when no frame arrives for --idle-ms (default 700 ms) or when --max-ms (default 5 s) is reached (docs/reference/cli.md:15-35). A demo that someone types by hand cannot be recorded this way:

  • The session id appears only in the stderr progress line attached id=… (src/main.rs, record), so another tool cannot reliably drive it with felis sessions send.
  • Any pause longer than --idle-ms between keystrokes ends the recording.
  • --max-ms is too short for an interactive demo.

What is missing is the asciinema rec workflow: run one command, use a shell normally, exit, and get a .fcast. felis-reel then turns that into a video, so the two tools split cleanly into "record by hand" and "render".

Proposal

An interactive mode for record, e.g. felis fcast record demo.fcast [-- cmd...]:

  1. Create a session running $SHELL (or cmd) and attach the recorder in the same round trip as today, so the rehydrate burst is not lost.
  2. Put the user in front of it:
    • inside a felis window ($FELIS_SESSION_ID is set), move the current window onto the new session with felis sessions switch;
    • otherwise, open a window with felis attach <id>.
  3. The user works in the session and exits.
  4. The recorder sees the session end and finishes the .fcast. The window unwinds back along its trail (felis/docs/reference/cli.md, "Window launches"), i.e. to the session it was switched from.

felis already provides the parts this needs:

  • Size: the client whose input most recently reached the session owns the PTY size (felis/docs/explanation/architecture/session-lifecycle.md, "Window-size negotiation"). The recorder never sends input, so the window the user types in sets the grid. Resizes during the session end up in the recording, and felis-reel already renders a recording that resizes mid-session (felis-reel#8).
  • Returning: switch plus the trail unwind brings the window back without the recorder having to manage it.

Changes in record

  • Stop condition. Interactive mode ignores --idle-ms and --max-ms and records until the session exits. record needs to observe the session's end, which it currently does not.
  • Streaming output. record currently buffers every frame in memory and writes the file at the end. An interactive recording can be long, so write each frame as it arrives. A crash then still leaves a valid prefix.
  • Socket. --socket becomes optional and defaults to the user's daemon, the one felis itself dials.

To decide

  • The CLI shape: a positional output path that selects interactive mode, or an explicit flag such as --interactive. --command keeps today's non-interactive behavior either way.
  • Whether a window moved by switch does unwind back to the original session on exit. The trail rule suggests it does. This has not been tried.
  • Whether exit grace delays the end of the recording, and whether the frames sent during the grace period belong in the recording.
  • Whether the help text should warn that everything shown on screen is recorded. Passwords are not echoed, but a pasted token is.

No explanation doc covers record yet. This decision would start one (for example docs/explanation/record.md), with the non-interactive mode and the rejected alternatives recorded there.

## Motivation `record` only captures a non-interactive command. It creates a session running `sh -c <command>`, and stops when no frame arrives for `--idle-ms` (default 700 ms) or when `--max-ms` (default 5 s) is reached (`docs/reference/cli.md:15-35`). A demo that someone types by hand cannot be recorded this way: - The session id appears only in the stderr progress line `attached id=…` (`src/main.rs`, `record`), so another tool cannot reliably drive it with `felis sessions send`. - Any pause longer than `--idle-ms` between keystrokes ends the recording. - `--max-ms` is too short for an interactive demo. What is missing is the `asciinema rec` workflow: run one command, use a shell normally, `exit`, and get a `.fcast`. felis-reel then turns that into a video, so the two tools split cleanly into "record by hand" and "render". ## Proposal An interactive mode for `record`, e.g. `felis fcast record demo.fcast [-- cmd...]`: 1. Create a session running `$SHELL` (or `cmd`) and attach the recorder in the same round trip as today, so the rehydrate burst is not lost. 2. Put the user in front of it: - inside a felis window (`$FELIS_SESSION_ID` is set), move the current window onto the new session with `felis sessions switch`; - otherwise, open a window with `felis attach <id>`. 3. The user works in the session and exits. 4. The recorder sees the session end and finishes the `.fcast`. The window unwinds back along its trail (`felis/docs/reference/cli.md`, "Window launches"), i.e. to the session it was switched from. felis already provides the parts this needs: - Size: the client whose input most recently reached the session owns the PTY size (`felis/docs/explanation/architecture/session-lifecycle.md`, "Window-size negotiation"). The recorder never sends input, so the window the user types in sets the grid. Resizes during the session end up in the recording, and felis-reel already renders a recording that resizes mid-session (felis-reel#8). - Returning: `switch` plus the trail unwind brings the window back without the recorder having to manage it. ## Changes in `record` - **Stop condition.** Interactive mode ignores `--idle-ms` and `--max-ms` and records until the session exits. `record` needs to observe the session's end, which it currently does not. - **Streaming output.** `record` currently buffers every frame in memory and writes the file at the end. An interactive recording can be long, so write each frame as it arrives. A crash then still leaves a valid prefix. - **Socket.** `--socket` becomes optional and defaults to the user's daemon, the one `felis` itself dials. ## To decide - The CLI shape: a positional output path that selects interactive mode, or an explicit flag such as `--interactive`. `--command` keeps today's non-interactive behavior either way. - Whether a window moved by `switch` does unwind back to the original session on exit. The trail rule suggests it does. This has not been tried. - Whether exit grace delays the end of the recording, and whether the frames sent during the grace period belong in the recording. - Whether the help text should warn that everything shown on screen is recorded. Passwords are not echoed, but a pasted token is. No explanation doc covers `record` yet. This decision would start one (for example `docs/explanation/record.md`), with the non-interactive mode and the rejected alternatives recorded there.
Author
Owner

Status after the felis-cast v1 rework (PR #8):

  • The tool is now felis-cast (felis cast record), and the envelope is felis-cast v1.
  • Observing the session's end is in place. record dials pull-paced; the daemon closes the connection once the recorded session exits, and the recorder now treats that (including a pull that fails on the closed socket) as the end of the recording instead of an error. Interactive mode can stop on that alone.
  • Still open from the proposal: streaming each unit to the file as it closes (the recorder still buffers in memory; units close atomically, so a crash would leave a valid prefix), ignoring --idle-ms / --max-ms in interactive mode, defaulting --socket to the user's daemon, and the CLI shape.
  • The format.md line references in the description predate the rewrite; the unit rules (docs/reference/format.md "Units") now constrain what an interactive recording writes.
Status after the felis-cast v1 rework (PR #8): - The tool is now `felis-cast` (`felis cast record`), and the envelope is `felis-cast` v1. - **Observing the session's end is in place.** `record` dials pull-paced; the daemon closes the connection once the recorded session exits, and the recorder now treats that (including a pull that fails on the closed socket) as the end of the recording instead of an error. Interactive mode can stop on that alone. - Still open from the proposal: streaming each unit to the file as it closes (the recorder still buffers in memory; units close atomically, so a crash would leave a valid prefix), ignoring `--idle-ms` / `--max-ms` in interactive mode, defaulting `--socket` to the user's daemon, and the CLI shape. - The `format.md` line references in the description predate the rewrite; the unit rules (`docs/reference/format.md` "Units") now constrain what an interactive recording writes.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
natsukium/felis-fcast#5
No description provided.