feat(socktop): kill a local process from the TUI

btop-style process termination, for local agents only. The signal is sent by
socktop itself through a direct sysinfo call — nothing is transmitted to the
agent, and the agent and connector have no kill capability at all.

Why local-only: the PIDs on screen are reported by the agent, and the signal is
sent with socktop's own OS privileges. A PID is therefore only meaningful, and
only safe to act on, when the agent lives on this machine; acting on a remote
agent's PIDs would signal whatever unrelated local process happened to share
that number. local::agent_is_local treats an address as local when it is
loopback or when an ephemeral bind succeeds (which only works for an address on
one of our own interfaces, so it also covers reaching our own agent by LAN IP),
requires every address a hostname resolves to to be local, and fails closed.

  * `t` on the selected process, and `t` inside Process Details. One key for
    both: `k` scrolls the thread table in the modal, so it could not be reused
    there.
  * The confirmation offers Terminate (focused first, so a reflexive Enter is
    the safe one), Force kill, and Cancel. Keeping SIGKILL behind a second
    button rather than a second keybinding means the destructive option has to
    be chosen deliberately.
  * The selection hint gained the key, but only for a local agent — advertising
    a key that deliberately does nothing is worse than no hint. Same for the
    details modal's help line.

The list is reconciled after a signal rather than left to the next poll. A
signalled PID goes on a watch list re-checked each metrics tick, because SIGTERM
is a request: the process is usually still alive at signal time, and its row
should go when it actually exits (or stay, if it ignores the signal). PIDs
confirmed gone are remembered briefly, since the agent serves Processes from a
1500ms cache and would otherwise hand back a pre-kill snapshot. A selection
whose process has left the list is dropped, and the details view closes for a
process that no longer exists — including when it dies on its own, which
previously flipped that modal to "Agent Update Required" because the wire cannot
distinguish "no such PID" from "endpoint unsupported".

Also fixes two pre-existing UI faults found on the way:

  * The selection hint was sized from its full label including the process name
    and skipped entirely when that exceeded the pane width — so it vanished
    exactly when a long-named process was selected. The name is now the elastic
    part, and widths are measured in columns rather than bytes.
  * Confirmation and Info dialogs laid their content out over the whole modal
    rect instead of the block's inner rect, putting the first line of text on
    the border row, and fell into the catch-all 70%x50% sizing arm, so a
    one-line question got half the screen. They now size to their content and
    use the theme's colors like the connection-error modal does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-23 06:46:58 -07:00
co-authored by Claude Opus 5
parent 4188bcd334
commit 98594b7184
10 changed files with 1612 additions and 96 deletions
Generated
+1
View File
@@ -2423,6 +2423,7 @@ dependencies = [
"serde",
"serde_json",
"socktop_connector",
"sysinfo",
"tempfile",
"tokio",
"unicode-width",