diff --git a/packages/shared/IpcChannel.ts b/packages/shared/IpcChannel.ts index 720d1ec4bb..93679f5faa 100644 --- a/packages/shared/IpcChannel.ts +++ b/packages/shared/IpcChannel.ts @@ -317,6 +317,7 @@ export enum IpcChannel { ApiServer_Stop = 'api-server:stop', ApiServer_Restart = 'api-server:restart', ApiServer_GetStatus = 'api-server:get-status', + // NOTE: This api is not be used. ApiServer_GetConfig = 'api-server:get-config', // Anthropic OAuth diff --git a/src/main/services/ApiServerService.ts b/src/main/services/ApiServerService.ts index 9a7bfad7e0..76a5eca617 100644 --- a/src/main/services/ApiServerService.ts +++ b/src/main/services/ApiServerService.ts @@ -1,5 +1,11 @@ import { IpcChannel } from '@shared/IpcChannel' -import { ApiServerConfig } from '@types' +import { + ApiServerConfig, + GetApiServerStatusResult, + RestartApiServerStatusResult, + StartApiServerStatusResult, + StopApiServerStatusResult +} from '@types' import { ipcMain } from 'electron' import { apiServer } from '../apiServer' @@ -52,7 +58,7 @@ export class ApiServerService { registerIpcHandlers(): void { // API Server - ipcMain.handle(IpcChannel.ApiServer_Start, async () => { + ipcMain.handle(IpcChannel.ApiServer_Start, async (): Promise => { try { await this.start() return { success: true } @@ -61,7 +67,7 @@ export class ApiServerService { } }) - ipcMain.handle(IpcChannel.ApiServer_Stop, async () => { + ipcMain.handle(IpcChannel.ApiServer_Stop, async (): Promise => { try { await this.stop() return { success: true } @@ -70,7 +76,7 @@ export class ApiServerService { } }) - ipcMain.handle(IpcChannel.ApiServer_Restart, async () => { + ipcMain.handle(IpcChannel.ApiServer_Restart, async (): Promise => { try { await this.restart() return { success: true } @@ -79,7 +85,7 @@ export class ApiServerService { } }) - ipcMain.handle(IpcChannel.ApiServer_GetStatus, async () => { + ipcMain.handle(IpcChannel.ApiServer_GetStatus, async (): Promise => { try { const config = await this.getCurrentConfig() return { diff --git a/src/preload/index.ts b/src/preload/index.ts index a90174baa5..34656092b2 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -12,6 +12,7 @@ import { FileListResponse, FileMetadata, FileUploadResponse, + GetApiServerStatusResult, KnowledgeBaseParams, KnowledgeItem, KnowledgeSearchResult, @@ -22,8 +23,11 @@ import { OcrProvider, OcrResult, Provider, + RestartApiServerStatusResult, S3Config, Shortcut, + StartApiServerStatusResult, + StopApiServerStatusResult, SupportedOcrFile, ThemeMode, WebDavConfig @@ -496,6 +500,12 @@ const api = { ipcRenderer.removeListener(channel, listener) } } + }, + apiServer: { + getStatus: (): Promise => ipcRenderer.invoke(IpcChannel.ApiServer_GetStatus), + start: (): Promise => ipcRenderer.invoke(IpcChannel.ApiServer_Start), + restart: (): Promise => ipcRenderer.invoke(IpcChannel.ApiServer_Restart), + stop: (): Promise => ipcRenderer.invoke(IpcChannel.ApiServer_Stop) } } diff --git a/src/renderer/src/hooks/agents/useAgent.ts b/src/renderer/src/hooks/agents/useAgent.ts index 62d2393618..a6755a4293 100644 --- a/src/renderer/src/hooks/agents/useAgent.ts +++ b/src/renderer/src/hooks/agents/useAgent.ts @@ -1,18 +1,28 @@ import { useCallback } from 'react' +import { useTranslation } from 'react-i18next' import useSWR from 'swr' +import { useApiServer } from '../useApiServer' import { useAgentClient } from './useAgentClient' export const useAgent = (id: string | null) => { + const { t } = useTranslation() const client = useAgentClient() const key = id ? client.agentPaths.withId(id) : null + const { apiServerConfig, apiServerRunning } = useApiServer() const fetcher = useCallback(async () => { if (!id || id === 'fake') { return null } + if (!apiServerConfig.enabled) { + throw new Error(t('apiServer.messages.notEnabled')) + } + if (!apiServerRunning) { + throw new Error(t('agent.server.error.not_running')) + } const result = await client.getAgent(id) return result - }, [client, id]) + }, [apiServerConfig.enabled, apiServerRunning, client, id, t]) const { data, error, isLoading } = useSWR(key, id ? fetcher : null) return { diff --git a/src/renderer/src/hooks/agents/useAgents.ts b/src/renderer/src/hooks/agents/useAgents.ts index c49cf46d40..6af38228cb 100644 --- a/src/renderer/src/hooks/agents/useAgents.ts +++ b/src/renderer/src/hooks/agents/useAgents.ts @@ -6,6 +6,7 @@ import { useCallback } from 'react' import { useTranslation } from 'react-i18next' import useSWR from 'swr' +import { useApiServer } from '../useApiServer' import { useRuntime } from '../useRuntime' import { useAgentClient } from './useAgentClient' @@ -23,11 +24,18 @@ export const useAgents = () => { const { t } = useTranslation() const client = useAgentClient() const key = client.agentPaths.base + const { apiServerConfig, apiServerRunning } = useApiServer() const fetcher = useCallback(async () => { + if (!apiServerConfig.enabled) { + throw new Error(t('apiServer.messages.notEnabled')) + } + if (!apiServerRunning) { + throw new Error(t('agent.server.error.not_running')) + } const result = await client.listAgents() // NOTE: We only use the array for now. useUpdateAgent depends on this behavior. return result.data - }, [client]) + }, [apiServerConfig.enabled, apiServerRunning, client, t]) const { data, error, isLoading, mutate } = useSWR(key, fetcher) const { chat } = useRuntime() const { activeAgentId } = chat diff --git a/src/renderer/src/hooks/useApiServer.ts b/src/renderer/src/hooks/useApiServer.ts new file mode 100644 index 0000000000..ae418f6cc0 --- /dev/null +++ b/src/renderer/src/hooks/useApiServer.ts @@ -0,0 +1,112 @@ +import { loggerService } from '@logger' +import { useAppDispatch, useAppSelector } from '@renderer/store' +import { setApiServerEnabled as setApiServerEnabledAction } from '@renderer/store/settings' +import { useCallback, useEffect, useState } from 'react' +import { useTranslation } from 'react-i18next' + +const logger = loggerService.withContext('useApiServer') + +export const useApiServer = () => { + const { t } = useTranslation() + // FIXME: We currently store two copies of the config data in both the renderer and the main processes, + // which carries the risk of data inconsistency. This should be modified so that the main process stores + // the data, and the renderer retrieves it. + const apiServerConfig = useAppSelector((state) => state.settings.apiServer) + const dispatch = useAppDispatch() + + // Optimistic initial state. + const [apiServerRunning, setApiServerRunning] = useState(apiServerConfig.enabled) + const [apiServerLoading, setApiServerLoading] = useState(true) + + const setApiServerEnabled = useCallback( + (enabled: boolean) => { + dispatch(setApiServerEnabledAction(enabled)) + }, + [dispatch] + ) + + // API Server functions + const checkApiServerStatus = useCallback(async () => { + setApiServerLoading(true) + try { + const status = await window.api.apiServer.getStatus() + setApiServerRunning(status.running) + } catch (error: any) { + logger.error('Failed to check API server status:', error) + } finally { + setApiServerLoading(false) + } + }, []) + + const startApiServer = useCallback(async () => { + if (apiServerLoading) return + + setApiServerLoading(true) + try { + const result = await window.api.apiServer.start() + if (result.success) { + setApiServerRunning(true) + window.toast.success(t('apiServer.messages.startSuccess')) + } else { + window.toast.error(t('apiServer.messages.startError') + result.error) + } + } catch (error: any) { + window.toast.error(t('apiServer.messages.startError') + (error.message || error)) + } finally { + setApiServerLoading(false) + } + }, [apiServerLoading, t]) + + const stopApiServer = useCallback(async () => { + if (apiServerLoading) return + + setApiServerLoading(true) + try { + const result = await window.api.apiServer.stop() + if (result.success) { + setApiServerRunning(false) + window.toast.success(t('apiServer.messages.stopSuccess')) + } else { + window.toast.error(t('apiServer.messages.stopError') + result.error) + } + } catch (error: any) { + window.toast.error(t('apiServer.messages.stopError') + (error.message || error)) + } finally { + setApiServerLoading(false) + } + }, [apiServerLoading, t]) + + const restartApiServer = useCallback(async () => { + if (apiServerLoading) return + + setApiServerLoading(true) + try { + const result = await window.api.apiServer.restart() + if (result.success) { + await checkApiServerStatus() + window.toast.success(t('apiServer.messages.restartSuccess')) + } else { + window.toast.error(t('apiServer.messages.restartError') + result.error) + } + } catch (error) { + window.toast.error(t('apiServer.messages.restartFailed') + (error as Error).message) + } finally { + setApiServerLoading(false) + } + }, [apiServerLoading, checkApiServerStatus, t]) + + useEffect(() => { + checkApiServerStatus() + }, [checkApiServerStatus]) + + return { + apiServerConfig, + apiServerRunning, + apiServerLoading, + startApiServer, + stopApiServer, + restartApiServer, + checkApiServerStatus, + setApiServerEnabled + } +} diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 0d46390767..288f0964b0 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -30,6 +30,11 @@ "failed": "Failed to list agents." } }, + "server": { + "error": { + "not_running": "The API server is enabled but not running properly." + } + }, "session": { "accessible_paths": { "add": "Add directory", @@ -237,6 +242,7 @@ "messages": { "apiKeyCopied": "API Key copied to clipboard", "apiKeyRegenerated": "API Key regenerated", + "notEnabled": "The API Server is not enabled.", "operationFailed": "API Server operation failed: ", "restartError": "Failed to restart API Server: ", "restartFailed": "API Server restart failed: ", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 2ae391419f..3e6bac4708 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -30,6 +30,11 @@ "failed": "获取智能体列表失败" } }, + "server": { + "error": { + "not_running": "API 服务器已启用但未正常运行。" + } + }, "session": { "accessible_paths": { "add": "添加目录", @@ -237,6 +242,7 @@ "messages": { "apiKeyCopied": "API 密钥已复制到剪贴板", "apiKeyRegenerated": "API 密钥已重新生成", + "notEnabled": "API 服务器未启用。", "operationFailed": "API 服务器操作失败:", "restartError": "重启 API 服务器失败:", "restartFailed": "API 服务器重启失败:", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 3e9f321f17..b87e5e3256 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -30,6 +30,11 @@ "failed": "無法列出代理程式。" } }, + "server": { + "error": { + "not_running": "API 伺服器已啟用,但運行不正常。" + } + }, "session": { "accessible_paths": { "add": "新增目錄", @@ -237,6 +242,7 @@ "messages": { "apiKeyCopied": "API 金鑰已複製到剪貼簿", "apiKeyRegenerated": "API 金鑰已重新生成", + "notEnabled": "API 伺服器未啟用。", "operationFailed": "API 伺服器操作失敗:", "restartError": "重新啟動 API 伺服器失敗:", "restartFailed": "API 伺服器重新啟動失敗:", diff --git a/src/renderer/src/i18n/translate/el-gr.json b/src/renderer/src/i18n/translate/el-gr.json index ed2ae1aee7..a39c429d1c 100644 --- a/src/renderer/src/i18n/translate/el-gr.json +++ b/src/renderer/src/i18n/translate/el-gr.json @@ -30,6 +30,11 @@ "failed": "Αποτυχία καταχώρησης πρακτόρων." } }, + "server": { + "error": { + "not_running": "Ο διακομιστής API είναι ενεργοποιημένος αλλά δεν λειτουργεί σωστά." + } + }, "session": { "accessible_paths": { "add": "Προσθήκη καταλόγου", @@ -237,6 +242,7 @@ "messages": { "apiKeyCopied": "Το κλειδί API αντιγράφηκε στο πρόχειρο", "apiKeyRegenerated": "Το κλειδί API αναδημιουργήθηκε", + "notEnabled": "Ο διακομιστής API δεν είναι ενεργοποιημένος.", "operationFailed": "Η λειτουργία του Διακομιστή API απέτυχε: ", "restartError": "Αποτυχία επανεκκίνησης του Διακομιστή API: ", "restartFailed": "Η επανεκκίνηση του Διακομιστή API απέτυχε: ", diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json index eeb943c39f..219f809187 100644 --- a/src/renderer/src/i18n/translate/es-es.json +++ b/src/renderer/src/i18n/translate/es-es.json @@ -30,6 +30,11 @@ "failed": "Error al listar agentes." } }, + "server": { + "error": { + "not_running": "El servidor de API está habilitado pero no funciona correctamente." + } + }, "session": { "accessible_paths": { "add": "Agregar directorio", @@ -237,6 +242,7 @@ "messages": { "apiKeyCopied": "Clave API copiada al portapapeles", "apiKeyRegenerated": "Clave API regenerada", + "notEnabled": "El servidor de API no está habilitado.", "operationFailed": "Falló la operación del Servidor API: ", "restartError": "Error al reiniciar el Servidor API: ", "restartFailed": "Falló el reinicio del Servidor API: ", diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json index b0e4a1afed..72899c3c13 100644 --- a/src/renderer/src/i18n/translate/fr-fr.json +++ b/src/renderer/src/i18n/translate/fr-fr.json @@ -30,6 +30,11 @@ "failed": "Échec de la liste des agents." } }, + "server": { + "error": { + "not_running": "Le serveur API est activé mais ne fonctionne pas correctement." + } + }, "session": { "accessible_paths": { "add": "Ajouter un répertoire", @@ -237,6 +242,7 @@ "messages": { "apiKeyCopied": "Clé API copiée dans le presse-papiers", "apiKeyRegenerated": "Clé API régénérée", + "notEnabled": "Le serveur API n'est pas activé.", "operationFailed": "Opération du Serveur API échouée : ", "restartError": "Échec du redémarrage du Serveur API : ", "restartFailed": "Redémarrage du Serveur API échoué : ", diff --git a/src/renderer/src/i18n/translate/ja-jp.json b/src/renderer/src/i18n/translate/ja-jp.json index 7d07988276..5c7e77400c 100644 --- a/src/renderer/src/i18n/translate/ja-jp.json +++ b/src/renderer/src/i18n/translate/ja-jp.json @@ -30,6 +30,11 @@ "failed": "エージェントの一覧取得に失敗しました。" } }, + "server": { + "error": { + "not_running": "APIサーバーは有効になっていますが、正常に動作していません。" + } + }, "session": { "accessible_paths": { "add": "ディレクトリを追加", @@ -237,6 +242,7 @@ "messages": { "apiKeyCopied": "API キーがクリップボードにコピーされました", "apiKeyRegenerated": "API キーが再生成されました", + "notEnabled": "APIサーバーが有効になっていません。", "operationFailed": "API サーバーの操作に失敗しました:", "restartError": "API サーバーの再起動に失敗しました:", "restartFailed": "API サーバーの再起動に失敗しました:", diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json index e4707b846d..f10aea90df 100644 --- a/src/renderer/src/i18n/translate/pt-pt.json +++ b/src/renderer/src/i18n/translate/pt-pt.json @@ -30,6 +30,11 @@ "failed": "Falha ao listar agentes." } }, + "server": { + "error": { + "not_running": "O servidor de API está habilitado, mas não está funcionando corretamente." + } + }, "session": { "accessible_paths": { "add": "Adicionar diretório", @@ -237,6 +242,7 @@ "messages": { "apiKeyCopied": "Chave API copiada para a área de transferência", "apiKeyRegenerated": "Chave API regenerada", + "notEnabled": "O Servidor de API não está habilitado.", "operationFailed": "Operação do Servidor API falhou: ", "restartError": "Falha ao reiniciar o Servidor API: ", "restartFailed": "Reinício do Servidor API falhou: ", diff --git a/src/renderer/src/i18n/translate/ru-ru.json b/src/renderer/src/i18n/translate/ru-ru.json index d4c631d81c..bf4533a4ac 100644 --- a/src/renderer/src/i18n/translate/ru-ru.json +++ b/src/renderer/src/i18n/translate/ru-ru.json @@ -30,6 +30,11 @@ "failed": "Не удалось получить список агентов." } }, + "server": { + "error": { + "not_running": "API-сервер включен, но работает неправильно." + } + }, "session": { "accessible_paths": { "add": "Добавить каталог", @@ -237,6 +242,7 @@ "messages": { "apiKeyCopied": "API ключ скопирован в буфер обмена", "apiKeyRegenerated": "API ключ перегенерирован", + "notEnabled": "API-сервер не включен.", "operationFailed": "Операция API сервера не удалась: ", "restartError": "Не удалось перезапустить API сервер: ", "restartFailed": "Перезапуск API сервера не удался: ", diff --git a/src/renderer/src/pages/home/Tabs/AssistantsTab.tsx b/src/renderer/src/pages/home/Tabs/AssistantsTab.tsx index 2eaa2ef1e3..f4588f60a0 100644 --- a/src/renderer/src/pages/home/Tabs/AssistantsTab.tsx +++ b/src/renderer/src/pages/home/Tabs/AssistantsTab.tsx @@ -1,15 +1,16 @@ import { Alert, Spinner } from '@heroui/react' import Scrollbar from '@renderer/components/Scrollbar' import { useAgents } from '@renderer/hooks/agents/useAgents' +import { useApiServer } from '@renderer/hooks/useApiServer' import { useAssistants } from '@renderer/hooks/useAssistant' import { useAssistantPresets } from '@renderer/hooks/useAssistantPresets' import { useRuntime } from '@renderer/hooks/useRuntime' -import { useSettings } from '@renderer/hooks/useSettings' import { useAssistantsTabSortType } from '@renderer/hooks/useStore' import { useTags } from '@renderer/hooks/useTags' import { useAppDispatch } from '@renderer/store' import { addIknowAction } from '@renderer/store/runtime' import { Assistant, AssistantsSortType } from '@renderer/types' +import { getErrorMessage } from '@renderer/utils' import { FC, useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' @@ -35,7 +36,8 @@ const AssistantsTab: FC = (props) => { const { activeAssistant, setActiveAssistant, onCreateAssistant, onCreateDefaultAssistant } = props const containerRef = useRef(null) const { t } = useTranslation() - const { apiServer } = useSettings() + const { apiServerConfig, apiServerRunning } = useApiServer() + const apiServerEnabled = apiServerConfig.enabled const { iknow, chat } = useRuntime() const dispatch = useAppDispatch() @@ -55,7 +57,7 @@ const AssistantsTab: FC = (props) => { const { unifiedItems, handleUnifiedListReorder } = useUnifiedItems({ agents, assistants, - apiServerEnabled: apiServer.enabled, + apiServerEnabled, agentsLoading, agentsError, updateAssistants @@ -72,17 +74,17 @@ const AssistantsTab: FC = (props) => { unifiedItems, assistants, agents, - apiServerEnabled: apiServer.enabled, + apiServerEnabled, agentsLoading, agentsError, updateAssistants }) useEffect(() => { - if (!agentsLoading && agents.length > 0 && !activeAgentId && apiServer.enabled) { + if (!agentsLoading && agents.length > 0 && !activeAgentId && apiServerConfig.enabled) { setActiveAgentId(agents[0].id) } - }, [agentsLoading, agents, activeAgentId, setActiveAgentId, apiServer.enabled]) + }, [agentsLoading, agents, activeAgentId, setActiveAgentId, apiServerConfig.enabled]) const onDeleteAssistant = useCallback( (assistant: Assistant) => { @@ -105,7 +107,7 @@ const AssistantsTab: FC = (props) => { return ( - {!apiServer.enabled && !iknow[ALERT_KEY] && ( + {!apiServerConfig.enabled && !iknow[ALERT_KEY] && ( = (props) => { onClose={() => { dispatch(addIknowAction(ALERT_KEY)) }} + className="mb-2" /> )} {agentsLoading && } - {apiServer.enabled && agentsError && } + {apiServerConfig.enabled && !apiServerRunning && ( + + )} + {apiServerConfig.enabled && apiServerRunning && agentsError && ( + + )} {assistantsTabSortType === 'tags' ? ( { @@ -25,67 +23,25 @@ const ApiServerSettings: FC = () => { // API Server state with proper defaults const apiServerConfig = useSelector((state: RootState) => state.settings.apiServer) - - const [apiServerRunning, setApiServerRunning] = useState(false) - const [apiServerLoading, setApiServerLoading] = useState(false) - - // API Server functions - const checkApiServerStatus = async () => { - try { - const status = await window.electron.ipcRenderer.invoke(IpcChannel.ApiServer_GetStatus) - setApiServerRunning(status.running) - } catch (error: any) { - logger.error('Failed to check API server status:', error) - } - } - - useEffect(() => { - checkApiServerStatus() - }, []) + const { apiServerRunning, apiServerLoading, startApiServer, stopApiServer, restartApiServer, setApiServerEnabled } = + useApiServer() const handleApiServerToggle = async (enabled: boolean) => { - setApiServerLoading(true) try { if (enabled) { - const result = await window.electron.ipcRenderer.invoke(IpcChannel.ApiServer_Start) - if (result.success) { - setApiServerRunning(true) - window.toast.success(t('apiServer.messages.startSuccess')) - } else { - window.toast.error(t('apiServer.messages.startError') + result.error) - } + await startApiServer() } else { - const result = await window.electron.ipcRenderer.invoke(IpcChannel.ApiServer_Stop) - if (result.success) { - setApiServerRunning(false) - window.toast.success(t('apiServer.messages.stopSuccess')) - } else { - window.toast.error(t('apiServer.messages.stopError') + result.error) - } + await stopApiServer() } } catch (error) { window.toast.error(t('apiServer.messages.operationFailed') + formatErrorMessage(error)) } finally { - dispatch(setApiServerEnabled(enabled)) - setApiServerLoading(false) + setApiServerEnabled(enabled) } } const handleApiServerRestart = async () => { - setApiServerLoading(true) - try { - const result = await window.electron.ipcRenderer.invoke(IpcChannel.ApiServer_Restart) - if (result.success) { - await checkApiServerStatus() - window.toast.success(t('apiServer.messages.restartSuccess')) - } else { - window.toast.error(t('apiServer.messages.restartError') + result.error) - } - } catch (error) { - window.toast.error(t('apiServer.messages.restartFailed') + (error as Error).message) - } finally { - setApiServerLoading(false) - } + await restartApiServer() } const copyApiKey = () => { diff --git a/src/renderer/src/types/apiServer.ts b/src/renderer/src/types/apiServer.ts new file mode 100644 index 0000000000..316b8fe632 --- /dev/null +++ b/src/renderer/src/types/apiServer.ts @@ -0,0 +1,38 @@ +export type ApiServerConfig = { + enabled: boolean + host: string + port: number + apiKey: string +} + +export type GetApiServerStatusResult = { + running: boolean + config: ApiServerConfig | null +} + +export type StartApiServerStatusResult = + | { + success: true + } + | { + success: false + error: string + } + +export type RestartApiServerStatusResult = + | { + success: true + } + | { + success: false + error: string + } + +export type StopApiServerStatusResult = + | { + success: true + } + | { + success: false + error: string + } diff --git a/src/renderer/src/types/index.ts b/src/renderer/src/types/index.ts index 2f201b3fc8..96bbdbd4e5 100644 --- a/src/renderer/src/types/index.ts +++ b/src/renderer/src/types/index.ts @@ -17,6 +17,7 @@ import type { BaseTool, MCPTool } from './tool' export * from './agent' export * from './apiModels' +export * from './apiServer' export * from './knowledge' export * from './mcp' export * from './notification' @@ -848,13 +849,6 @@ export type S3Config = { } export type { Message } from './newMessage' - -export interface ApiServerConfig { - enabled: boolean - host: string - port: number - apiKey: string -} export * from './tool' // Memory Service Types