New Tunnel trait + 5 implementations:
- NoneTunnel: local-only, no external exposure (default)
- CloudflareTunnel: wraps cloudflared binary, extracts public URL
- TailscaleTunnel: tailscale serve (tailnet) or funnel (public)
- NgrokTunnel: wraps ngrok binary, supports custom domains
- CustomTunnel: user-provided command with {port}/{host} placeholders
Config schema:
- [tunnel] section with provider selector
- Provider-specific sub-configs: cloudflare, tailscale, ngrok, custom
- Backward compatible (serde default = "none")
Gateway integration:
- Tunnel starts automatically on 'zeroclaw gateway'
- Prints public URL on success, falls back to local on failure
20 new tests (factory, constructors, NoneTunnel async start/health)
649 tests passing, 0 clippy warnings, cargo fmt clean
29 lines
572 B
Rust
29 lines
572 B
Rust
use super::Tunnel;
|
|
use anyhow::Result;
|
|
|
|
/// No-op tunnel — direct local access, no external exposure.
|
|
pub struct NoneTunnel;
|
|
|
|
#[async_trait::async_trait]
|
|
impl Tunnel for NoneTunnel {
|
|
fn name(&self) -> &str {
|
|
"none"
|
|
}
|
|
|
|
async fn start(&self, local_host: &str, local_port: u16) -> Result<String> {
|
|
Ok(format!("http://{local_host}:{local_port}"))
|
|
}
|
|
|
|
async fn stop(&self) -> Result<()> {
|
|
Ok(())
|
|
}
|
|
|
|
async fn health_check(&self) -> bool {
|
|
true
|
|
}
|
|
|
|
fn public_url(&self) -> Option<String> {
|
|
None
|
|
}
|
|
}
|