feat(cli+push): Mac pairing QR (lisa pair) + ntfy deep-links#127
Merged
Conversation
The Mac-side counterpart to Lisa Pocket's QR scanner (docs/IOS_COMPANION_PLAN.md §5.3). Until now /api/pair/start minted a per-device token but nothing rendered the lisa-pair:// QR — so the phone scanner had nothing to scan and you had to paste the string by hand. `lisa pair [--host H] [--port N] [--name LABEL]` is a thin loopback client to a running serve: POSTs /api/pair/start (loopback-only), builds lisa-pair://v1?host=&port=&token=&name=, and renders it as a terminal QR (+ a pasteable URL fallback). --host defaults to the first non-internal LAN IPv4; pass a tailnet name to pair over Tailscale. Adds qrcode-terminal (zero runtime deps — bundles its own encoder). node-pty is kept in the lockfile (lock recomputed with --package-lock-only so the optional dep isn't pruned under Node 26; CI on Node 22 still builds it). Verified: typecheck + build clean; npm test -> 811 pass / 1 skip; pure helpers (parsePairArgs / detectLanHost / buildPairUrl) unit-tested; terminal QR render smoke-tested. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Agent-event pushes (done / error / needs-permission) now carry a `Click` deep-link — `lisapocket://session?agent=&id=` (agentDeepLink) — set as the ntfy `Click` header, so tapping the notification opens Lisa Pocket at that roster session instead of just the app root. Payload stays metadata-only. The iOS side registers the `lisapocket://` scheme and routes it (follow-up, on the iOS branch); the same scheme is reused for the home-screen Widget tap target. No-op until the app handles it — harmless if absent. Verified: typecheck clean; npm test -> all pass (+ new agentDeepLink and Click-header tests in push.test.ts). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This was referenced Jun 19, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Backend follow-ups for the iOS companion (Lisa Pocket), based on
main. Two independent commits.lisa pair— show a QR to pair a phoneUntil now
/api/pair/startminted a per-device token but nothing rendered thelisa-pair://QR, so Lisa Pocket's scanner had nothing to scan — you had to paste the string by hand. This closes that loop.lisa pair [--host H] [--port N] [--name LABEL]is a thin loopback client to a runningserve: it POSTs/api/pair/start(loopback-only), buildslisa-pair://v1?host=&port=&token=&name=, and renders it as a terminal QR (+ a pasteable URL fallback).--hostdefaults to the first non-internal LAN IPv4; pass a tailnet name to pair over Tailscale.--package-lock-onlyso the optional dep isn't pruned under Node 26; CI on Node 22 still builds it).ntfy push deep-links
Agent-event pushes (done / error / needs-permission) now carry a
Clickdeep-link —lisapocket://session?agent=&id=(agentDeepLink) set as the ntfyClickheader — so tapping a notification opens the app at that session instead of the app root. Payload stays metadata-only. The iOS scheme handler + the Widget reuse the samelisapocket://scheme (iOS-side follow-ups); no-op until the app handles it.Verification
npm run typecheck && npm run build && npm test→ 813 pass / 1 skip (the node-pty round-trip, unrunnable on Node 26). Pure helpers unit-tested (parsePairArgs/detectLanHost/buildPairUrl/agentDeepLink/ ntfyClickheader); terminal QR render smoke-tested.🤖 Generated with Claude Code