From 7be89dbf8cc3986ff76ac4d6f20097ef41612dc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20Emre=20Kabakc=C4=B1?= Date: Thu, 14 May 2026 23:49:19 +0100 Subject: [PATCH] fix(cli): connector run uses agent API origin, not memory MCP URL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit connector-run REST routes (/api/:orgSlug/connector-run/*) live on the main app (mounted at /), which is the same origin as the agent API URL configured per context. Before this fix, the CLI resolved the base URL via getMemoryUrl() — which defaults to https://lobu.ai/mcp — so the origin collapsed to https://lobu.ai (the marketing site), and every hosted-prod user got 404 HTML back. Switch to resolveContext().apiUrl so the origin is correct on every configured context (community.lobu.ai, app.lobu.ai, local dev). --url remains an explicit override but now requires LOBU_API_TOKEN — refuse to forward stored credentials to a URL the user typed on the command line. --- .../src/commands/_lib/connector-run-cmd.ts | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/packages/cli/src/commands/_lib/connector-run-cmd.ts b/packages/cli/src/commands/_lib/connector-run-cmd.ts index c35b64184..9e8e936a3 100644 --- a/packages/cli/src/commands/_lib/connector-run-cmd.ts +++ b/packages/cli/src/commands/_lib/connector-run-cmd.ts @@ -26,11 +26,8 @@ import { homedir } from "node:os"; import { join } from "node:path"; import { printText } from "../memory/_lib/output.js"; -import { - getUsableToken, - resolveOrg, - resolveServerUrl, -} from "../memory/_lib/openclaw-auth.js"; +import { resolveContext } from "../../internal/context.js"; +import { getUsableToken, resolveOrg } from "../memory/_lib/openclaw-auth.js"; import { compileConnectorFromFile, findBundledConnectorFile, @@ -88,11 +85,13 @@ interface ResolvedFeed { checkpoint: Record | null; } -// The new connector-run REST routes live on the main app (mounted at `/`), -// not under the Agent API (`/lobu`) or the MCP path. Collapse the resolved -// server URL to just its origin so we can append `/api//...`. -function apiBaseFrom(serverUrl: string): string { - const { origin } = new URL(serverUrl); +// The connector-run REST routes live on the main app (mounted at `/`), +// not under the Agent API (`/lobu`) or the MCP path. We resolve the +// app origin from the context's agent API URL — *not* the memory MCP +// URL, which historically defaulted to lobu.ai/mcp and pointed the +// connector-run client at the marketing site (404). +function apiBaseFrom(agentApiUrl: string): string { + const { origin } = new URL(agentApiUrl); return origin; } @@ -160,18 +159,34 @@ export async function connectorRun( ): Promise { ensurePlaywrightAvailable(); - // Resolve API endpoint + auth via the same path the rest of the CLI uses. - // LOBU_API_TOKEN short-circuits the credential store — useful for testing - // against a dev backend you haven't `lobu login`'d into. - const baseMcpUrl = await resolveServerUrl(args.url, args.context); + // Resolve API endpoint + auth from the chosen context. The agent API URL + // (e.g. https://app.lobu.ai/api/v1) shares an origin with the connector-run + // REST routes — unlike the memory MCP URL, which historically defaulted to + // lobu.ai/mcp and pointed this client at the marketing site. + // + // --url is an explicit override (for testing); LOBU_API_TOKEN short-circuits + // the credential store. + const ctx = await resolveContext(args.context); + const explicitUrl = args.url?.trim(); + const apiBase = explicitUrl + ? new URL(explicitUrl).origin + : apiBaseFrom(ctx.apiUrl); + const envToken = process.env.LOBU_API_TOKEN?.trim(); let token: string; let resolvedContextName: string | undefined; if (envToken) { token = envToken; - resolvedContextName = args.context; + resolvedContextName = ctx.name; + } else if (explicitUrl) { + // Refuse to silently forward the context's stored credentials to a URL the + // user typed on the command line — that's how tokens leak to the wrong + // backend. Pair --url with LOBU_API_TOKEN explicitly. + throw new Error( + "--url requires LOBU_API_TOKEN to be set (refuse to forward stored credentials to an explicit override)." + ); } else { - const tokenInfo = await getUsableToken(baseMcpUrl, args.context); + const tokenInfo = await getUsableToken(undefined, ctx.name); if (!tokenInfo) { throw new Error( "Not logged in. Run `lobu login` first (or set LOBU_API_TOKEN; or pass --context )." @@ -186,7 +201,6 @@ export async function connectorRun( "No active org. Run `lobu org use ` or pass --org ." ); } - const apiBase = apiBaseFrom(baseMcpUrl); // Resolve auth profile and (optionally) feed in parallel. let feed: ResolvedFeed | null = null;