diff --git a/omalocal.sh b/omalocal.sh index 2c8f366..5a54da2 100755 --- a/omalocal.sh +++ b/omalocal.sh @@ -804,13 +804,84 @@ headless box — ideal for pinging your desktop or phone from the server: omarchy-send --to "" --message "backup finished" omarchy-send --to "Slate Starburst" --message "deploy done" --wait 20s # peer-discovery timeout (default 15s) omarchy-send --to "" --message "hi" --send-pin 2468 # if the target requires a PIN +omarchy-send --to-ip 192.168.1.40 --message "on it" # reply straight to an IP (no discovery wait) +omarchy-send --to-fingerprint AB12… --message "on it" # reply to a stable fingerprint (robust) ``` Matches the target by display name (case-insensitive), discovers it over multicast (waits up to `--wait`), sends, prints a one-line result, and exits non-zero if the peer isn't found or the send fails. Discovery-only — it doesn't bind the receiver port, so it's safe to run while the TUI is also up. File -sending stays in the TUI for now. +sending stays in the TUI for now. To reply to someone who messaged you, prefer +`--to-ip`/`--to-fingerprint` with the values from the message log (below) — +aliases aren't unique and can change. + +### Reading messages people send you (where an agent looks) + +Two places: + +- **The Messages tab** in the TUI (interactive). +- **The message log** — a JSON Lines file at + **`~/.local/state/omarchy-send/messages.jsonl`** (honours `XDG_STATE_HOME`). + This is where a script/agent should look. One JSON object per received message + *or* file, appended and `fsync`'d the instant it arrives, recording **who** sent + it — not just the text: + + ```json + {"ts":"2026-05-30T10:42:01Z","dir":"in","type":"message","id":"01J…", + "text":"are the movies up?","from_alias":"Slate Starburst", + "from_fingerprint":"AB12…","from_ip":"192.168.1.40", + "device_model":"pixel","device_type":"mobile"} + ``` + + Files appear as `"type":"file"` with `filename`/`size`; the file bytes land in + `~/Omarchy-Send/`. To consume: `tail -F ~/.local/state/omarchy-send/messages.jsonl`, + filter `type=="message"`, dedupe by `id` (sortable by time), and reply with + `omarchy-send --to-ip --message "…"`. + +> **The receiver only runs while the TUI is open.** omarchy-send has NO background +> daemon: it receives, logs, and can be replied-to **only while `omarchy-send` is +> running in a terminal** (e.g. under `tmux new -s oms 'omarchy-send'`). When the +> TUI is closed, nothing is received and the log does not grow — treat "TUI not +> running" as "offline, nothing to do". + +### Auto-reply to messages (opt-in, AI-powered) + +omarchy-send has a built-in **AI auto-reply** toggle. Open the TUI, go to the +**Settings** tab, and press **`r`** to turn it on (the row shows `on`). While it's +on, each incoming message is answered by the local `claude` CLI and the reply is +sent back to the sender automatically. Run the TUI under tmux so it persists: + +```bash +tmux new -s oms 'omarchy-send' # then: Settings tab → press r to enable AI auto-reply +``` + +How it works: a reply *generator* (`~/.local/bin/omarchy-send-reply`, pre-seeded +as the config's `replyCmd`) gets the message in `OMARCHY_MSG_*` env and prints a +reply; omarchy-send sends it. It is: + +- **A runtime toggle** — flip it in Settings (`r`); the state persists in config. + Plain receiving (toggle off) never auto-replies. +- **Reply-only** — the generator gives the model no tools (`--tools ""`), so an + unauthenticated LAN sender can't make this box do anything but emit a text reply. +- **Tied to the TUI** — auto-reply (like all receiving) happens only while the TUI + is open; quit it (`q`) and the box stops replying. No background daemon. + +Requires the `claude` CLI installed and authenticated for this user (omaterm +installs it; run `claude` once interactively to log in). The generator resolves +`claude` from PATH or `mise`. + +> Don't enable AI auto-reply on two boxes pointed at each other — each would +> reply to the other's reply forever. Phones / desktops running plain LocalSend +> don't auto-reply, so messaging with them is fine. + +**If a user asks you (the AI) to "monitor and reply to messages": tell them to +enable AI auto-reply in the Settings tab** (or, if asked, `omarchy-send` with +`AutoReply` set). That is the supported mechanism and it switches off cleanly when +the TUI closes. Do NOT build a polling loop, a `tail -F` watcher, or a background +process to do it yourself — those miss messages in bursts, outlive their purpose, +and contradict the TUI-only model. (For a brief, supervised look within your own +session you may tail the JSON Lines log and stop when the user is done.) ### Desktop notifications @@ -966,24 +1037,39 @@ and TLS/https (no cert on a private box). If `omarchy-send` is installed (offered at first login), it's a LocalSend-compatible TUI for file transfer + plain-text messaging over the LAN. -Firewall already opens **53317/tcp+udp**. Anything anyone sends to this box -lands in **`~/Omarchy-Send/`** — that's the first place to look for files a -user says they "just sent over". Sub-folder structure is preserved. +Firewall already opens **53317/tcp+udp**. + +- **Files** anyone sends land in **`~/Omarchy-Send/`** (sub-folder structure + preserved) — first place to look for files a user "just sent over". +- **Messages** are appended to **`~/.local/state/omarchy-send/messages.jsonl`** + (JSON Lines, one object per message/file, with sender `from_ip`/`from_fingerprint`) + — that's where to look for messages, and where an agent should tail. ```bash -ls -la ~/Omarchy-Send/ # what's been received -omarchy-send # open the TUI (Devices / Transfers / Messages / Manage / Settings) +ls -la ~/Omarchy-Send/ # received files +tail -n 20 ~/.local/state/omarchy-send/messages.jsonl # received messages (and file events) +omarchy-send # open the TUI (Devices / Transfers / Messages / Manage / Settings) ``` +**Critical:** there is NO background receiver — omarchy-send only receives, +logs, and is reachable **while the TUI is running** (e.g. `tmux new -s oms +'omarchy-send'`). If it isn't open, nothing arrives and the log doesn't grow. + The TUI needs a real TTY — over SSH that means an interactive session (or `ssh -tt user@host omarchy-send`). To send a one-off message with NO TTY (from -a script/cron on this headless box — e.g. ping a desktop/phone), use the -headless mode added in v0.1.6: +a script/cron on this headless box — e.g. ping a desktop/phone), or to reply to +a logged sender, use headless send: ```bash omarchy-send --to "" --message "backup finished" # also --send-pin, --wait; file send stays TUI-only +omarchy-send --to-ip --message "on it" # reply to a sender from the message log ``` +To **auto-reply** to incoming messages, open `omarchy-send`, go to the Settings +tab and press `r` (built-in AI auto-reply, reply-only, live only while the TUI is +open). If a user asks you to "monitor and reply to messages", enable that toggle; +do NOT build a poll-loop, `tail -F` watcher, or background daemon to do it. + If install was skipped, run: ```bash @@ -1001,6 +1087,64 @@ CLAUDE_MD arch-chroot /mnt chown -R "$USERNAME:$USERNAME" "/home/$USERNAME/.claude" +# ---------------------------------------------- omarchy-send AI auto-reply ---- +# omarchy-send has a built-in "AI auto-reply" toggle (Settings tab → `r`): when +# on, it runs the configured ReplyCmd for each received message and sends the +# printed reply back to the sender. We ship a reply *generator* script and point +# ReplyCmd at it, so the user only has to flip the switch. The generator only +# prints text (omarchy-send does the sending) and gives the model no tools, so a +# LAN message can't make this box take any action. Auto-reply runs only while +# the TUI is open — close it and it stops. +echo "==> Installing omarchy-send AI reply generator for $USERNAME..." +install -d -m 755 "/mnt/home/$USERNAME/.local/bin" + +cat > "/mnt/home/$USERNAME/.local/bin/omarchy-send-reply" <<'OMS_REPLY' +#!/usr/bin/env bash +# omarchy-send-reply — reply generator for omarchy-send's AI auto-reply toggle. +# omarchy-send runs this for each received message (message in OMARCHY_MSG_* env) +# and sends whatever we print on stdout back to the sender. So this ONLY composes +# text: it does no sending and takes no actions. Toggle it from Settings (`r`). +set -uo pipefail + +text="${OMARCHY_MSG_TEXT:-}" +who="${OMARCHY_MSG_FROM_ALIAS:-a device}" +[ -n "$text" ] || exit 0 + +# Resolve the AI CLI: PATH first, then mise (omaterm installs agents as mise +# tools, whose shims aren't on a non-interactive PATH). +if command -v claude >/dev/null 2>&1; then + ai=(claude) +elif command -v mise >/dev/null 2>&1 && mise which claude >/dev/null 2>&1; then + ai=(mise exec -- claude) +else + exit 0 # no AI available; omarchy-send logs the empty reply and skips the send +fi + +prompt="You are an automatic responder for a LAN file-transfer tool. \"$who\" sent: \"$text\". Reply in one or two short, friendly plain-text sentences. Output only the reply." + +# --tools "" no tools, so the model can ONLY emit text — it +# cannot run a command a message asks for. +# --permission-mode bypass... never blocks on the first-run folder-trust prompt +# (no TTY to answer it). +"${ai[@]}" -p "$prompt" --tools "" --permission-mode bypassPermissions 2>/dev/null \ + | tr -d '\000' | head -c 1000 +OMS_REPLY + +arch-chroot /mnt chmod 755 "/home/$USERNAME/.local/bin/omarchy-send-reply" + +# Seed omarchy-send's config with ReplyCmd pointing at the generator (via $HOME, +# expanded at run time, so no username is baked in). omarchy-send fills the rest +# of the config — alias, TLS identity, etc. — on first run and preserves this. +# AutoReply stays off until the user turns it on in Settings. +install -d -m 755 "/mnt/home/$USERNAME/.config/omarchy-send" +cat > "/mnt/home/$USERNAME/.config/omarchy-send/config.json" <<'OMS_CONFIG' +{ + "replyCmd": "$HOME/.local/bin/omarchy-send-reply" +} +OMS_CONFIG +arch-chroot /mnt chmod 600 "/home/$USERNAME/.config/omarchy-send/config.json" +arch-chroot /mnt chown -R "$USERNAME:$USERNAME" "/home/$USERNAME/.local" "/home/$USERNAME/.config/omarchy-send" + echo "==> Writing pre-login console banner (hostname + IPv4 + ssh hint)..." # agetty expands these escapes when it prints /etc/issue: # \n = hostname, \4 = primary IPv4, \r = kernel, \l = tty