plugins/plugin-native-mobile-agent-bridge/README.md
Outbound tunnel from a phone-hosted Eliza agent so a remote Mac client
can reach it. Phone-side companion to the Mac-side
TunnelToMobileClient in @elizaos/app-core.
This package owns the phone-to-relay tunnel for a phone-hosted Eliza
agent. The JS surface (startInboundTunnel, stopInboundTunnel,
getTunnelStatus, stateChange event) is stable, and the native
implementations now hold an outbound WebSocket to the relay. Relay
requests are proxied into the same local agent route surface used by the
rest of the mobile app.
| Platform | Status |
|---|---|
| Web | Stub. Returns state: "error" with an explanatory message. |
| iOS | Outbound WebSocket tunnel. Proxies path-only requests through the WebView IPC bridge; no listening port is opened. |
| Android | Outbound WebSocket tunnel. Proxies path-only requests to the token-protected local agent service. |
iOS apps cannot bind a publicly reachable listening socket. Today the device-bridge architecture only flows in one direction: phones dial out to Mac-hosted agents. To support the reverse — a Mac dialing an agent running on the user's phone — the phone must hold an outbound connection that a relay (Eliza Cloud) brokers to a matching connection from the Mac.
Tunnel frames use a path-only HTTP request envelope. The relay never
sends absolute URLs, and the plugin rejects //host and scheme-bearing
paths before dispatching to the agent. On iOS, dispatch goes through
window.__ELIZA_IOS_LOCAL_AGENT_REQUEST__, which is the same Capacitor
IPC bridge the UI uses for full-Bun local mode.
import { MobileAgentBridge } from "@elizaos/capacitor-mobile-agent-bridge";
await MobileAgentBridge.startInboundTunnel({
relayUrl: "wss://relay.elizacloud.ai/v1/agent-tunnel",
deviceId: "phone-abc123",
pairingToken: "...",
});
const status = await MobileAgentBridge.getTunnelStatus();
// { state: "registered" | "error" | ..., relayUrl, deviceId, lastError }
await MobileAgentBridge.stopInboundTunnel();