feat(remote): real QR codes, live tailscale URL, TUI status & shortcut

* Replace placeholder /remote/qr SVG (URL drawn as text) with real QR
  rendered via the qrcode package; add format=terminal returning ASCII
  QR for the TUI.
* Resolve the public tailscale funnel URL from captured CLI output
  instead of constructing http://<hostname>:<port> from a configured
  hostname label — that label was never used by `tailscale funnel` and
  produced a non-public URL in the auth/QR link.
* Drop hostname requirement from engine + UI; only target port matters.
* Tighten tailscale parseReadiness to require a URL on the matched line
  so the tunnel manager doesn't lock in `running` before the URL line.
* TUI: poll remote status, show ● tunnel indicator + URL in MainHeader,
  bind Ctrl+Q to a global QR overlay (terminal ASCII), and switch the
  in-Settings K shortcut to render the same ASCII QR.
* Auto-poll remote status in the dashboard while in `starting`/`stopping`
  so the UI flips to running without reopening the modal.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-04-29 13:20:03 -07:00
parent 72ae0b46bf
commit 8bbd956fa5
9 changed files with 178 additions and 31 deletions

View File

@@ -811,8 +811,8 @@ export class ProjectEngine {
if (!tailscale.enabled) {
return { provider, reason: "provider_not_enabled", message: "Tailscale provider is disabled" };
}
if (!tailscale.hostname?.trim() || !Number.isFinite(tailscale.targetPort) || tailscale.targetPort <= 0) {
return { provider, reason: "provider_not_configured", message: "Tailscale hostname and target port must be configured" };
if (!Number.isFinite(tailscale.targetPort) || tailscale.targetPort <= 0) {
return { provider, reason: "provider_not_configured", message: "Tailscale target port must be configured" };
}
const executable = await this.checkExecutableAvailable("tailscale");