4cc433a1b8
Old instances may have auto-generated Docker Compose container names that don't match instance.name.lower(), causing DNS resolution failures for the tunnel. Also, old instances may not be on the backend network. - apps/api/src/services/docker.py: add get_container_ip_on_network() and is_container_on_network() helpers - apps/api/src/services/tunnel.py: start_tunnel() and recreate_tunnel() now accept an optional target_url parameter to override the default name-based URL - apps/api/src/api/tool_instances.py: recreate_tunnel_endpoint now: 1. Looks up the tool container (by stored container_id or name) 2. Ensures it's connected to the backend network 3. Gets the container's IP on that network 4. Passes the IP as the explicit tunnel target This guarantees the tunnel can reach the tool container regardless of naming or network state. Quality gates: ruff clean