feat(remote): phone-first mobile console + push notifications#614
feat(remote): phone-first mobile console + push notifications#614bborn wants to merge 4 commits into
Conversation
Make TaskYou usable on a walk. Two pieces, both zero-dependency and always shipped from a plain `go build`: Mobile console (GET /m) - Self-contained HTML/CSS/vanilla-JS page embedded in the binary, driving the existing JSON API. No Node build, no React — `ty serve` over Tailscale now gives a usable phone UI instead of a cramped SSH/TUI session. - Phone-first board: "Needs you" (blocked) surfaced first, then Running, Queue, Recent. Tap a task to reply to a blocked agent, approve/continue, retry a finished task, or read the latest output. Polls every 5s; deep-links to a task. Push notifications (internal/notify) - Opt-in pushes when a task blocks, needs sign-in, completes, or fails — the moments where a walking user must act. ntfy (free iOS/Android app, no account) or arbitrary webhook (Slack/Discord/Telegram/Zapier). - Notifications deep-link into the mobile console (/m?task=N) so a tap opens the task ready to reply. - Wired into the events emitter (daemon executor + web routine runs), settings, and a new `ty notify` command (`ty notify test`). Config via `ty settings`: notify_enabled / notify_provider / notify_target / notify_events / notify_url. Tests: mobile route + console contents, notifier providers/filtering/deep-links, emitter fan-out to push (and no-notifier safety). Lint clean (golangci-lint v2.8.0). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
# Conflicts: # cmd/task/completion.go # cmd/task/completion_test.go # cmd/task/main.go
✅ Merge conflicts resolved + full QAMerged
QA results (against the merged tree)
Updated screenshots (390px-wide phone viewport, live data)Blocked-task detail — reply UI Detail (taller viewport) — Send reply / Approve / Continue + latest output 🤖 Generated with Claude Code |
Agent summaries are written in Markdown, but the mobile console showed them raw (literal **bold** and - bullets). Add a tiny, dependency-free, XSS-safe Markdown renderer (escape-first; bold/italic/inline-code/links + heading, bullet/ordered list, fenced-code, and paragraph blocks) and use it for the summary. Logs stay verbatim monospace. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
📝 Follow-up: render Markdown in the task summaryCaught during QA — agent summaries are written in Markdown, but the console was showing them raw (literal
Re-QA: 🤖 Generated with Claude Code |
…very race Ports the two wins from PR #621 onto the mobile-console notifier: 1. One-tap unblock. ntfy pushes for actionable events (blocked/auth_required) now carry an Actions header: an http button that POSTs the canned reply to the existing POST /api/tasks/{id}/input (resuming the agent without opening anything), plus a view button deep-linking into the mobile console (/m) for a custom reply — console and one-tap, together. Reply text is configurable via notify_reply (default "continue"); validated live against ntfy.sh. 2. CLI-exit delivery race fix. The notifier read its settings inside the async delivery goroutine, but short-lived CLI/MCP commands defer db.Close() the instant Run returns — before PersistentPostRun flushes the emitter wait group — so the goroutine hit a closed DB, saw notifications as disabled, and silently dropped the push (daemon/web runs were unaffected; their DB stays open). Notifier.Prepare now reads settings + builds the request synchronously and returns a network-only delivery closure; events.Emit calls it before spawning the goroutine. Existing header/webhook behavior and tests are unchanged; adds tests for the action button (default + custom reply, and no action when no base URL is set).
|
Pushed a commit onto this branch (e0a1894) that folds in the two unique wins from #621, so #621 can be closed as a duplicate: 1. One-tap unblock action button. ntfy pushes for actionable events (
So you get the console and true one-tap, together. Reply text is configurable via 2. CLI-exit delivery race fix. The notifier read its settings inside the async delivery goroutine, but short-lived CLI/MCP commands Existing header/webhook behavior and tests are unchanged; added tests for the action button. Skipped from #621 on purpose: a Telegram-native provider (your |





Why
Today, keeping TaskYou moving from a phone means SSH-ing into the box over Tailscale and fighting the TUI in Termius — almost unusable. The goal: leave the keyboard, go for a walk, and keep work moving — get pinged when an agent needs you, then reply/approve/retry from your phone.
What
Two pieces, both stdlib-only and always shipped from a plain
go build(no Node build, no React, no extra services):📱 Mobile console —
GET /mA self-contained, phone-first HTML/CSS/vanilla-JS page embedded in the binary that drives the existing JSON API.
ty serveover Tailscale now gives a real phone UI:/m?task=N).🔔 Push notifications —
internal/notifyOpt-in pushes at the moments a walking user must act — blocked, auth_required, completed, failed (noisy created/started/updated excluded by default):
/m?task=N) so a tap lands you on the task ready to reply.Wired into the events emitter (daemon executor and web routine runs), plus settings and a new command:
Files
internal/web/mobile.go,internal/web/mobile.html— embedded console +/mhandlerinternal/web/server.go— register/mroute (before the catch-all React handler)internal/notify/— provider-agnostic notifier (ntfy/webhook), event filtering, deep linksinternal/events/events.go—SetNotifier+ fan-out fromEmit/Waitinternal/executor/executor.go,internal/web/routines.go— attach notifier to emitterscmd/task/main.go—ty notifycommand +notify_*settings;completion.go— shell completionmobile_test.go,internal/notify/notify_test.go,internal/events/notify_test.goTesting
go build ./...✅go test ./internal/notify/ ./internal/events/ ./internal/web/✅golangci-lint run(v2.8.0, matches CI) — 0 issues ✅ty serve, confirmed/mrenders the real board and/api/boardreturns data; verified the rendered mobile UI in a 390×844 viewport.Notes / follow-ups
/mstill requires network access to the host (Tailscale/SSH tunnel) — auth/exposure is unchanged fromty servetoday./mto the desktop shell nav, badge counts in notifications, and an "open console" hint inty servestartup output.🤖 Generated with Claude Code