The WebSocket connection at /ws was not including the token parameter,
relying solely on the session cookie from the initial redirect. When
scanning the QR code on a phone, the token (longest part of URL) was
being truncated, so no cookie was ever set and all requests got 403.
Fix: embed the token directly into the page as a JS variable, and
append it to the WebSocket connection URL as a fallback. Now both
the HTTP page and the WebSocket upgrade work even if the cookie
isn't available.
- Add transport mode config (surge/tailscale) to remote-control.json
- Add detectTailscaleIp() with CLI and local API fallbacks
- Add startServerTailscale() binding to 0.0.0.0 (token-protected)
- Add Transport toggle in /remote-control menu
- Update README with Tailscale setup + Android connection guide
- Update ARCHITECTURE.md with dual transport documentation
Use client.terminate() instead of client.close() to avoid waiting for
unresponsive clients to acknowledge the WebSocket close handshake.
Add a 2-second safety timeout that closes the HTTP listener, destroys
lingering sockets, and resolves the promise so session_shutdown does not
block pi from exiting.
When the agent is streaming, the send button becomes a red stop button
that sends a { type: "stop" } WebSocket message. The server handles this
by calling ctx.abort() to cancel the current agent operation.
Use the 'qrcode' npm package instead of shelling out to the 'qrencode'
binary. Load via createRequire for ESM/CJS interop. Use margin: 2 to
avoid the utf8 renderer's invalid array length bug with odd margins.
/remote-control now opens a select menu with Turn on/off,
Configure URL, and Status instead of relying on subcommands.
Adds ability to stop the server. Shows current URL in the
Configure URL menu item and in the input dialog title.