Skip to content

hqsh 0.1.0: the daemon, the client and resume, for real - #1

Merged
ralyodio merged 1 commit into
mainfrom
feat/v0.1.0
Oct 6, 2026
Merged

ralyodio merged 1 commit into
mainfrom
feat/v0.1.0

Conversation

@ralyodio

@ralyodio ralyodio commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Turns the 0.0.1 stub into a working hqsh: hqsh dev2 gives a persistent shell that survives disconnects and passes the raw byte stream (Kitty / iTerm2 image escapes included).

What's in it

  • Daemon (hqsh server daemon): $SHELL -l in a PTY, 4 MiB resume ring, one attached client (a newer one takes over), HELLO → WELCOME + replay with the gap flag (and a resize nudge so the screen redraws), INPUT / RESIZE / ACK / PING, EXIT with the shell's status. A lock file allows one daemon per session, and the socket is recreated if its dir is removed.
  • Attach (hqsh server attach): bridges ssh stdio to the socket. If no daemon is running it starts one (setsid, /dev/null stdio) and waits up to 3 s for the socket.
  • hqsh server list [--json]: prints [{"name":"main","attached":true}] and removes stale sockets. This uses a new local-only STATUS frame.
  • Protocol: HELLO now carries the client's TERM after a 0 byte. HELLOs from 0.0.x still decode. The docs are updated to match.
  • Client: raw mode starts after the first WELCOME, so ssh can still prompt before it. Ctrl-^ . detaches and Ctrl-^ Ctrl-^ sends a literal Ctrl-^. Resizes come from SIGWINCH (polled on Windows). A ping and a 15 s watchdog trigger reconnects with backoff, and the reconnect status goes only in the window title. A gap clears the screen. The client exits with the shell's status. If the host has no hqsh on its PATH, the client tries ~/.local/bin/hqsh, and if that also fails it prints the curl -fsSL https://hqterm.sh/install | sh hint.
  • Release workflow: on a v* tag it builds raw hqsh-<os>-<arch> binaries for linux/darwin amd64 and arm64, plus windows/amd64 .exe, and a SHA256SUMS file.

Tests

  • Unit tests for the HELLO TERM field (encode/decode and backward compatibility), WELCOME/ACK/EXIT, list output (JSON and text), session-name validation, escape keys, and the missing-remote path (fake ssh that exits 127).
  • Integration tests with a real /bin/sh and a real daemon process, over pipes and without ssh. They cover: echo hi, disconnect, output produced while detached, reconnect with lastSeq (replay is contiguous and has no duplicates), takeover, and exit 3 → EXIT 3 with the socket removed. The real client is also tested reconnecting after its transport dies.
  • A manual end-to-end run on dev2 over ssh localhost, inside a real PTY:
    • command output came through;
    • Kitty APC and iTerm2 OSC 1337 bytes arrived unmodified;
    • after SIGKILLing ssh in the middle of a command, the client reconnected in 1.5 s and printed the missed output exactly once;
    • Ctrl-^ . detached, and list showed the session detached;
    • re-attaching got the same shell;
    • exit 5 made the client exit 5, and list then showed [].

🤖 Generated with Claude Code

- server: PTY daemon ($SHELL -l via creack/pty) with a 4 MiB resume ring,
  one attached client (newer takes over), HELLO/WELCOME with replay and the
  gap flag (redraw nudge), INPUT/RESIZE/ACK/PING, EXIT with the shell's
  status; attach starts the daemon detached (setsid) when absent; a lock
  keeps one daemon per session; the socket comes back if its dir is removed
- server list [--json]: live sessions as [{"name","attached"}], stale
  sockets removed (new local-only STATUS frame)
- proto: HELLO carries the client's TERM after a 0 byte (old HELLOs still
  decode); WELCOME/ACK/EXIT helpers
- client: raw mode after the first WELCOME, Ctrl-^ . detach, Ctrl-^ Ctrl-^
  literal, SIGWINCH resize (polling on Windows), ping/15 s watchdog,
  reconnect with backoff and the status in the window title, gap clears the
  screen, exit status passed through; falls back to ~/.local/bin/hqsh and
  says how to install hqsh when the host has none
- tests: HELLO TERM, list output, and real-shell integration tests over
  pipes (resume without loss or duplicates, takeover, exit; the real
  client reconnecting)
- release workflow: raw binaries for linux/darwin amd64+arm64 and
  windows/amd64 plus SHA256SUMS on v* tags

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedgithub.com/​creack/​pty@​v1.1.2497100100100100
Addedgolang.org/​x/​term@​v0.46.0100100100100100

View full report

@ralyodio
ralyodio merged commit de10586 into main Oct 6, 2026
5 checks passed
@ralyodio
ralyodio deleted the feat/v0.1.0 branch October 6, 2026 13:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant