diff --git a/src/tui/gateway-chat.test.ts b/src/tui/gateway-chat.test.ts index 4827c3f097b..01707ffbe6d 100644 --- a/src/tui/gateway-chat.test.ts +++ b/src/tui/gateway-chat.test.ts @@ -2,6 +2,8 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; const loadConfig = vi.fn(); const resolveGatewayPort = vi.fn(); +const pickPrimaryTailnetIPv4 = vi.fn(); +const pickPrimaryLanIPv4 = vi.fn(); const originalEnvToken = process.env.OPENCLAW_GATEWAY_TOKEN; const originalEnvPassword = process.env.OPENCLAW_GATEWAY_PASSWORD; @@ -15,13 +17,25 @@ vi.mock("../config/config.js", async (importOriginal) => { }; }); +vi.mock("../infra/tailnet.js", () => ({ + pickPrimaryTailnetIPv4, +})); + +vi.mock("../gateway/net.js", () => ({ + pickPrimaryLanIPv4, +})); + const { resolveGatewayConnection } = await import("./gateway-chat.js"); describe("resolveGatewayConnection", () => { beforeEach(() => { loadConfig.mockReset(); resolveGatewayPort.mockReset(); + pickPrimaryTailnetIPv4.mockReset(); + pickPrimaryLanIPv4.mockReset(); resolveGatewayPort.mockReturnValue(18789); + pickPrimaryTailnetIPv4.mockReturnValue(undefined); + pickPrimaryLanIPv4.mockReturnValue(undefined); delete process.env.OPENCLAW_GATEWAY_TOKEN; delete process.env.OPENCLAW_GATEWAY_PASSWORD; }); @@ -77,4 +91,24 @@ describe("resolveGatewayConnection", () => { password: "explicit-password", }); }); + + it("uses tailnet host when local bind is tailnet", () => { + loadConfig.mockReturnValue({ gateway: { mode: "local", bind: "tailnet" } }); + resolveGatewayPort.mockReturnValue(18800); + pickPrimaryTailnetIPv4.mockReturnValue("100.64.0.1"); + + const result = resolveGatewayConnection({}); + + expect(result.url).toBe("ws://100.64.0.1:18800"); + }); + + it("uses lan host when local bind is lan", () => { + loadConfig.mockReturnValue({ gateway: { mode: "local", bind: "lan" } }); + resolveGatewayPort.mockReturnValue(18800); + pickPrimaryLanIPv4.mockReturnValue("192.168.1.42"); + + const result = resolveGatewayConnection({}); + + expect(result.url).toBe("ws://192.168.1.42:18800"); + }); }); diff --git a/src/tui/gateway-chat.ts b/src/tui/gateway-chat.ts index 30016edc333..ef29c888d32 100644 --- a/src/tui/gateway-chat.ts +++ b/src/tui/gateway-chat.ts @@ -1,6 +1,10 @@ import { randomUUID } from "node:crypto"; -import { loadConfig, resolveGatewayPort } from "../config/config.js"; -import { ensureExplicitGatewayAuth, resolveExplicitGatewayAuth } from "../gateway/call.js"; +import { loadConfig } from "../config/config.js"; +import { + buildGatewayConnectionDetails, + ensureExplicitGatewayAuth, + resolveExplicitGatewayAuth, +} from "../gateway/call.js"; import { GatewayClient } from "../gateway/client.js"; import { GATEWAY_CLIENT_CAPS } from "../gateway/protocol/client-info.js"; import { @@ -227,7 +231,6 @@ export function resolveGatewayConnection(opts: GatewayConnectionOptions) { const remote = isRemoteMode ? config.gateway?.remote : undefined; const authToken = config.gateway?.auth?.token; - const localPort = resolveGatewayPort(config); const urlOverride = typeof opts.url === "string" && opts.url.trim().length > 0 ? opts.url.trim() : undefined; const explicitAuth = resolveExplicitGatewayAuth({ token: opts.token, password: opts.password }); @@ -236,12 +239,10 @@ export function resolveGatewayConnection(opts: GatewayConnectionOptions) { auth: explicitAuth, errorHint: "Fix: pass --token or --password when using --url.", }); - const url = - urlOverride || - (typeof remote?.url === "string" && remote.url.trim().length > 0 - ? remote.url.trim() - : undefined) || - `ws://127.0.0.1:${localPort}`; + const url = buildGatewayConnectionDetails({ + config, + ...(urlOverride ? { url: urlOverride } : {}), + }).url; const token = explicitAuth.token ||