"""Emergency tab — offline-mode probe and connectivity status (M09).""" from __future__ import annotations def build_emergency_tab(bus=None, state_bus=None): import gradio as gr with gr.Column(): gr.HTML("""

🚨 Emergency Mode

Offline-first emergency routing · local resources only · civil defense alerts

""") gr.Markdown("""### 🚨 Emergency Mode HearthNet monitors internet connectivity and automatically switches modes: | Mode | Meaning | LLM routing | |------|---------|-------------| | `normal` | Internet reachable | Local preferred, online fallback allowed | | `degraded` | Partial connectivity | Local only, known-good peers only | | `offline` | No internet | Strict local-only, internet capabilities deregistered | Click **Check Status** to see the current mode. On a real node, the detector runs a background probe every 30 seconds against multiple endpoints. """) status_out = gr.JSON(label="Current Mode") refresh_btn = gr.Button("Check Status", variant="secondary") gr.Markdown("#### Local Resources") gr.Markdown("In offline mode, all capabilities route to local nodes only.") if bus is not None: with gr.Row(): probe_btn = gr.Button("Run Connectivity Probe", variant="secondary") probe_out = gr.JSON(label="Probe Results", visible=False) def get_status(): if state_bus is None: return {"mode": "unknown", "message": "State bus not connected"} s = state_bus.current() return { "mode": s.mode, "probe_results": s.probe_results, "label": s.mode_label, } def run_probe(): """Run a synchronous connectivity probe and update state_bus.""" import socket import urllib.request targets = { "dns:1.1.1.1": False, "dns:8.8.8.8": False, "http:cloudflare.com": False, } # DNS probes for host in ("1.1.1.1", "8.8.8.8"): try: socket.getaddrinfo(host, 53, timeout=3) targets[f"dns:{host}"] = True except Exception: pass # HTTP probe try: urllib.request.urlopen("https://cloudflare.com", timeout=5) # nosec B310 targets["http:cloudflare.com"] = True except Exception: pass if state_bus is not None: state_bus.emit_probe(targets) return get_status(), gr.update(visible=True, value=targets) refresh_btn.click(get_status, outputs=status_out) if bus is not None: probe_btn.click(run_probe, outputs=[status_out, probe_out])