From 02efeb2eb662cb696dda06464cd756408bfab701 Mon Sep 17 00:00:00 2001 From: Lifei Zhou Date: Wed, 1 Jul 2026 18:05:17 +1000 Subject: [PATCH] removed dependency on openapi type client and status --- ui/desktop/src/backendStatus.ts | 48 ++++++++++++++++++++++++--------- ui/desktop/src/goosed.ts | 33 ++++++++++++++++++++++- ui/desktop/src/main.ts | 25 +++++------------ 3 files changed, 74 insertions(+), 32 deletions(-) diff --git a/ui/desktop/src/backendStatus.ts b/ui/desktop/src/backendStatus.ts index 864e3d1a39..c3cd4e8d94 100644 --- a/ui/desktop/src/backendStatus.ts +++ b/ui/desktop/src/backendStatus.ts @@ -1,23 +1,39 @@ -import { status } from './api'; -import type { Client } from './api/client'; - export interface CheckServerStatusOptions { onEvent?: (name: string, details?: Record) => void; } +export interface CheckBackendStatusParams { + baseUrl: string; + serverSecret: string; + fetch: typeof globalThis.fetch; + errorLog?: string[]; + options?: CheckServerStatusOptions; +} + export const isFatalError = (line: string): boolean => { const fatalPatterns = [/panicked at/, /RUST_BACKTRACE/, /fatal error/i]; return fatalPatterns.some((pattern) => pattern.test(line)); }; -export const checkServerStatus = async ( - client: Client, - errorLog: string[], - options: CheckServerStatusOptions = {} -): Promise => { +const statusUrlFromBase = (baseUrl: string): string => { + const url = new URL(baseUrl); + url.pathname = `${url.pathname.replace(/\/+$/, '')}/status`; + url.search = ''; + url.hash = ''; + return url.toString(); +}; + +export const checkBackendStatus = async ({ + baseUrl, + serverSecret, + fetch, + errorLog = [], + options = {}, +}: CheckBackendStatusParams): Promise => { const timeout = 30000; const interval = 100; const maxAttempts = Math.ceil(timeout / interval); + const statusUrl = statusUrlFromBase(baseUrl); options.onEvent?.('healthcheck_start', { timeoutMs: timeout, intervalMs: interval }); for (let attempt = 1; attempt <= maxAttempts; attempt++) { @@ -27,12 +43,20 @@ export const checkServerStatus = async ( } try { - await status({ client, throwOnError: true }); - options.onEvent?.('healthcheck_success', { attempt }); - return true; + const response = await fetch(statusUrl, { + headers: { + 'X-Secret-Key': serverSecret, + }, + }); + if (response.ok) { + options.onEvent?.('healthcheck_success', { attempt }); + return true; + } } catch { - await new Promise((resolve) => setTimeout(resolve, interval)); + // Retry until the backend is ready or the timeout expires. } + + await new Promise((resolve) => setTimeout(resolve, interval)); } options.onEvent?.('healthcheck_timeout', { timeoutMs: timeout }); diff --git a/ui/desktop/src/goosed.ts b/ui/desktop/src/goosed.ts index c7e4cc7141..afaf7607c4 100644 --- a/ui/desktop/src/goosed.ts +++ b/ui/desktop/src/goosed.ts @@ -4,6 +4,7 @@ import os from 'node:os'; import path from 'node:path'; import { createServer } from 'net'; import { Buffer } from 'node:buffer'; +import { status } from './api'; import { Client, createClient, createConfig } from './api/client'; import { appendTail, @@ -11,7 +12,8 @@ import { type StartupDiagnostics, } from './startupDiagnostics'; import { isFatalError } from './backendStatus'; -export { checkServerStatus, isFatalError, type CheckServerStatusOptions } from './backendStatus'; +export { isFatalError, type CheckServerStatusOptions } from './backendStatus'; +import type { CheckServerStatusOptions } from './backendStatus'; export interface Logger { info: (...args: unknown[]) => void; @@ -36,6 +38,35 @@ export const findAvailablePort = (): Promise => { }); }; +export const checkServerStatus = async ( + client: Client, + errorLog: string[], + options: CheckServerStatusOptions = {} +): Promise => { + const timeout = 30000; + const interval = 100; + const maxAttempts = Math.ceil(timeout / interval); + options.onEvent?.('healthcheck_start', { timeoutMs: timeout, intervalMs: interval }); + + for (let attempt = 1; attempt <= maxAttempts; attempt++) { + if (errorLog.some(isFatalError)) { + options.onEvent?.('healthcheck_fatal_error', { attempt }); + return false; + } + + try { + await status({ client, throwOnError: true }); + options.onEvent?.('healthcheck_success', { attempt }); + return true; + } catch { + await new Promise((resolve) => setTimeout(resolve, interval)); + } + } + + options.onEvent?.('healthcheck_timeout', { timeoutMs: timeout }); + return false; +}; + export interface FindBinaryOptions { isPackaged?: boolean; resourcesPath?: string; diff --git a/ui/desktop/src/main.ts b/ui/desktop/src/main.ts index b6e6d45adb..aaa4837521 100644 --- a/ui/desktop/src/main.ts +++ b/ui/desktop/src/main.ts @@ -25,10 +25,9 @@ import path from 'node:path'; import os from 'node:os'; import { execFileSync, spawn, execFile } from 'child_process'; import 'dotenv/config'; -import { checkServerStatus } from './backendStatus'; +import { checkBackendStatus } from './backendStatus'; import { startGooseServe } from './gooseServe'; import { GooseServeLeaseRegistry, type GooseServeLease } from './gooseServeLeaseRegistry'; -import { createClient, createConfig } from './api/client'; import { acpWebSocketUrlFromHttpBase, normalizeAcpHttpBaseUrl } from './acp/url'; import { expandTilde } from './utils/pathUtils'; import log from './utils/logger'; @@ -51,7 +50,6 @@ import { } from './utils/autoUpdater'; import { UPDATES_ENABLED } from './updates'; import './utils/recipeHash'; -import { Client } from './api/client'; import type { GooseApp } from './types/apps'; import installExtension, { REACT_DEVELOPER_TOOLS } from 'electron-devtools-installer'; import { BLOCKED_PROTOCOLS, WEB_PROTOCOLS } from './utils/urlSecurity'; @@ -918,18 +916,6 @@ const getExternalBackendForCsp = (settings: Settings) => { }; }; -const createMainProcessBackendClient = (baseUrl: string, serverSecret: string): Client => - createClient( - createConfig({ - baseUrl, - fetch: net.fetch as unknown as typeof globalThis.fetch, - headers: { - 'Content-Type': 'application/json', - 'X-Secret-Key': serverSecret, - }, - }) - ); - let appConfig = { GOOSE_DEFAULT_PROVIDER: defaultProvider, GOOSE_DEFAULT_MODEL: defaultModel, @@ -1045,10 +1031,11 @@ const createChat = async ( ? normalizeFingerprint(externalBackend.certFingerprint) : null; - const externalBackendReady = await checkServerStatus( - createMainProcessBackendClient(externalBaseUrl, serverSecret), - [] - ); + const externalBackendReady = await checkBackendStatus({ + baseUrl: externalBaseUrl, + serverSecret, + fetch: net.fetch as unknown as typeof globalThis.fetch, + }); if (!externalBackendReady) { const canDisableExternalBackend = externalBackend.source === 'settings'; const response = dialog.showMessageBoxSync({