fix: notify renderer when api server ready (#11049)

* fix: notify renderer when api server ready

* chore: minor comment update

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix: minor ui change to reflect server loading state

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
defi-failure 2025-10-31 12:13:59 +08:00 committed by GitHub
parent b586e1796e
commit aa810a7ead
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 40 additions and 7 deletions

View File

@ -322,6 +322,7 @@ export enum IpcChannel {
ApiServer_Stop = 'api-server:stop',
ApiServer_Restart = 'api-server:restart',
ApiServer_GetStatus = 'api-server:get-status',
ApiServer_Ready = 'api-server:ready',
// NOTE: This api is not be used.
ApiServer_GetConfig = 'api-server:get-config',

View File

@ -1,8 +1,10 @@
import { createServer } from 'node:http'
import { loggerService } from '@logger'
import { IpcChannel } from '@shared/IpcChannel'
import { agentService } from '../services/agents'
import { windowService } from '../services/WindowService'
import { app } from './app'
import { config } from './config'
@ -43,6 +45,13 @@ export class ApiServer {
return new Promise((resolve, reject) => {
this.server!.listen(port, host, () => {
logger.info('API server started', { host, port })
// Notify renderer that API server is ready
const mainWindow = windowService.getMainWindow()
if (mainWindow && !mainWindow.isDestroyed()) {
mainWindow.webContents.send(IpcChannel.ApiServer_Ready)
}
resolve()
})

View File

@ -525,7 +525,16 @@ const api = {
getStatus: (): Promise<GetApiServerStatusResult> => ipcRenderer.invoke(IpcChannel.ApiServer_GetStatus),
start: (): Promise<StartApiServerStatusResult> => ipcRenderer.invoke(IpcChannel.ApiServer_Start),
restart: (): Promise<RestartApiServerStatusResult> => ipcRenderer.invoke(IpcChannel.ApiServer_Restart),
stop: (): Promise<StopApiServerStatusResult> => ipcRenderer.invoke(IpcChannel.ApiServer_Stop)
stop: (): Promise<StopApiServerStatusResult> => ipcRenderer.invoke(IpcChannel.ApiServer_Stop),
onReady: (callback: () => void): (() => void) => {
const listener = () => {
callback()
}
ipcRenderer.on(IpcChannel.ApiServer_Ready, listener)
return () => {
ipcRenderer.removeListener(IpcChannel.ApiServer_Ready, listener)
}
}
},
claudeCodePlugin: {
listAvailable: (): Promise<PluginResult<ListAvailablePluginsResult>> =>

View File

@ -25,6 +25,10 @@ export const useAgents = () => {
const client = useAgentClient()
const key = client.agentPaths.base
const { apiServerConfig, apiServerRunning } = useApiServer()
// Disable SWR fetching when server is not running by setting key to null
const swrKey = apiServerRunning ? key : null
const fetcher = useCallback(async () => {
// API server will start on startup if enabled OR there are agents
if (!apiServerConfig.enabled && !apiServerRunning) {
@ -37,7 +41,7 @@ export const useAgents = () => {
// NOTE: We only use the array for now. useUpdateAgent depends on this behavior.
return result.data
}, [apiServerConfig.enabled, apiServerRunning, client, t])
const { data, error, isLoading, mutate } = useSWR(key, fetcher)
const { data, error, isLoading, mutate } = useSWR(swrKey, fetcher)
const { chat } = useRuntime()
const { activeAgentId } = chat
const dispatch = useAppDispatch()

View File

@ -14,8 +14,8 @@ export const useApiServer = () => {
const apiServerConfig = useAppSelector((state) => state.settings.apiServer)
const dispatch = useAppDispatch()
// Optimistic initial state.
const [apiServerRunning, setApiServerRunning] = useState(apiServerConfig.enabled)
// Initial state - no longer optimistic, wait for actual status
const [apiServerRunning, setApiServerRunning] = useState(false)
const [apiServerLoading, setApiServerLoading] = useState(true)
const setApiServerEnabled = useCallback(
@ -99,6 +99,16 @@ export const useApiServer = () => {
checkApiServerStatus()
}, [checkApiServerStatus])
// Listen for API server ready event
useEffect(() => {
const cleanup = window.api.apiServer.onReady(() => {
logger.info('API server ready event received, checking status')
checkApiServerStatus()
})
return cleanup
}, [checkApiServerStatus])
return {
apiServerConfig,
apiServerRunning,

View File

@ -36,7 +36,7 @@ const AssistantsTab: FC<AssistantsTabProps> = (props) => {
const { activeAssistant, setActiveAssistant, onCreateAssistant, onCreateDefaultAssistant } = props
const containerRef = useRef<HTMLDivElement>(null)
const { t } = useTranslation()
const { apiServerConfig, apiServerRunning } = useApiServer()
const { apiServerConfig, apiServerRunning, apiServerLoading } = useApiServer()
const apiServerEnabled = apiServerConfig.enabled
const { iknow, chat } = useRuntime()
const dispatch = useAppDispatch()
@ -113,8 +113,8 @@ const AssistantsTab: FC<AssistantsTabProps> = (props) => {
/>
)}
{agentsLoading && <Spinner />}
{apiServerConfig.enabled && !apiServerRunning && (
{(agentsLoading || apiServerLoading) && <Spinner />}
{apiServerConfig.enabled && !apiServerLoading && !apiServerRunning && (
<Alert color="danger" title={t('agent.server.error.not_running')} isClosable className="mb-2" />
)}
{apiServerRunning && agentsError && (