From d1211e4dcaa2712fe19482e073278ba40c579f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E9=98=BF=E6=B1=9F=28Relakkes?= =?UTF-8?q?=29?= Date: Sun, 10 May 2026 23:01:43 +0800 Subject: [PATCH] Keep desktop smoke on the configured backend Browser-mode startup now respects an explicit Vite desktop server URL before falling back to same-origin H5 serving. This keeps local desktop smoke tests pointed at the real API server while preserving same-origin behavior for packaged H5 access. Constraint: Vite dev smoke serves the web app and API on different loopback ports Rejected: Treat same-origin as always preferred | Vite would answer /api requests with HTML and the chat surface never becomes ready Confidence: high Scope-risk: narrow Tested: cd desktop && bun run test -- src/lib/desktopRuntime.test.ts src/api/client.test.ts --run Tested: bun run check:desktop Tested: bun run quality:gate --mode baseline --allow-live --only 'provider-smoke:*' --only 'desktop-smoke:*' --provider-model codingplan:main:codingplan-main Not-tested: Full bun run quality:pr after this commit; it will run again during push --- desktop/src/api/client.ts | 4 ++++ desktop/src/lib/desktopRuntime.test.ts | 24 ++++++++++++++++++++++++ desktop/src/lib/desktopRuntime.ts | 18 ++++++++++++++++-- 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/desktop/src/api/client.ts b/desktop/src/api/client.ts index efa1e090..d7d61f3d 100644 --- a/desktop/src/api/client.ts +++ b/desktop/src/api/client.ts @@ -44,6 +44,10 @@ export function getDefaultBaseUrl() { return DEFAULT_BASE_URL } +export function hasExplicitDefaultBaseUrl() { + return Boolean(ENV_BASE_URL) +} + export class ApiError extends Error { constructor( public status: number, diff --git a/desktop/src/lib/desktopRuntime.test.ts b/desktop/src/lib/desktopRuntime.test.ts index 2374e263..d97f09e6 100644 --- a/desktop/src/lib/desktopRuntime.test.ts +++ b/desktop/src/lib/desktopRuntime.test.ts @@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' const clientMocks = vi.hoisted(() => ({ defaultBaseUrl: 'http://127.0.0.1:3456', + explicitDefaultBaseUrl: false, setBaseUrl: vi.fn(), setAuthToken: vi.fn(), postVerify: vi.fn(), @@ -12,6 +13,7 @@ vi.mock('../api/client', () => ({ post: clientMocks.postVerify, }, getDefaultBaseUrl: () => clientMocks.defaultBaseUrl, + hasExplicitDefaultBaseUrl: () => clientMocks.explicitDefaultBaseUrl, setAuthToken: clientMocks.setAuthToken, setBaseUrl: clientMocks.setBaseUrl, })) @@ -31,6 +33,8 @@ describe('desktopRuntime browser H5 bootstrap', () => { beforeEach(() => { vi.clearAllMocks() + clientMocks.defaultBaseUrl = 'http://127.0.0.1:3456' + clientMocks.explicitDefaultBaseUrl = false vi.useRealTimers() window.localStorage.clear() window.history.pushState({}, '', '/') @@ -111,6 +115,26 @@ describe('desktopRuntime browser H5 bootstrap', () => { }) }) + it('prefers an explicit Vite desktop server URL over the dev server origin', async () => { + clientMocks.defaultBaseUrl = 'http://127.0.0.1:55189' + clientMocks.explicitDefaultBaseUrl = true + window.history.pushState({}, '', '/') + globalThis.fetch = vi.fn().mockResolvedValue( + new Response(null, { status: 200 }), + ) as typeof fetch + + await expect(initializeDesktopServerUrl()).resolves.toBe('http://127.0.0.1:55189') + + expect(clientMocks.setBaseUrl).toHaveBeenLastCalledWith('http://127.0.0.1:55189') + expect(clientMocks.setAuthToken).toHaveBeenLastCalledWith(null) + expect(globalThis.fetch).toHaveBeenCalledWith('http://127.0.0.1:55189/health', { + cache: 'no-store', + }) + expect(globalThis.fetch).toHaveBeenCalledWith('http://127.0.0.1:55189/api/status', { + cache: 'no-store', + }) + }) + it('normalizes unreachable remote browser startup into a recoverable H5 error', async () => { vi.useFakeTimers() globalThis.fetch = vi.fn().mockRejectedValue(new TypeError('Failed to fetch')) as typeof fetch diff --git a/desktop/src/lib/desktopRuntime.ts b/desktop/src/lib/desktopRuntime.ts index 082ebac9..0eeacc6d 100644 --- a/desktop/src/lib/desktopRuntime.ts +++ b/desktop/src/lib/desktopRuntime.ts @@ -1,4 +1,10 @@ -import { api, getDefaultBaseUrl, setAuthToken, setBaseUrl } from '../api/client' +import { + api, + getDefaultBaseUrl, + hasExplicitDefaultBaseUrl, + setAuthToken, + setBaseUrl, +} from '../api/client' export const H5_SERVER_URL_STORAGE_KEY = 'cc-haha-h5-server-url' export const H5_TOKEN_STORAGE_KEY = 'cc-haha-h5-token' @@ -133,7 +139,7 @@ async function initializeBrowserServerUrl(fallbackUrl: string) { const requestedUrl = normalizeServerUrl(queryUrl) ?? stored.serverUrl ?? - getSameOriginServerUrl() ?? + getConfiguredBrowserServerUrl(fallbackUrl) ?? fallbackUrl const token = stored.token const browserH5Runtime = requiresH5AuthForServerUrl(requestedUrl) @@ -249,6 +255,14 @@ function getSameOriginServerUrl() { return normalizeServerUrl(window.location.origin) } +function getConfiguredBrowserServerUrl(fallbackUrl: string) { + if (hasExplicitDefaultBaseUrl()) { + return normalizeServerUrl(fallbackUrl) + } + + return getSameOriginServerUrl() +} + export function isLoopbackHostname(hostname: string) { const normalized = hostname.trim().replace(/^\[/, '').replace(/\]$/, '').toLowerCase() return normalized === '127.0.0.1' || normalized === 'localhost' || normalized === '::1'