xiaozhi-client 2.2.0 → 2.3.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/backend/WebServer.js +10 -32
- package/dist/backend/WebServer.js.map +1 -1
- package/dist/backend/WebServerLauncher.js +10 -32
- package/dist/backend/WebServerLauncher.js.map +1 -1
- package/dist/backend/package.json +52 -8
- package/dist/frontend/assets/form-utils-n2_wJnKb.js +41 -0
- package/dist/frontend/assets/form-utils-n2_wJnKb.js.map +1 -0
- package/dist/frontend/assets/index-C3xvW3AQ.js +81 -0
- package/dist/frontend/assets/index-C3xvW3AQ.js.map +1 -0
- package/dist/frontend/assets/index-CLIN00a1.css +1 -0
- package/dist/frontend/assets/radix-ui-B9D1KdKb.js +2 -0
- package/dist/frontend/assets/radix-ui-B9D1KdKb.js.map +1 -0
- package/dist/frontend/assets/react-vendor-CmWLnLSk.js +67 -0
- package/dist/frontend/assets/react-vendor-CmWLnLSk.js.map +1 -0
- package/dist/frontend/assets/rolldown-runtime-Dw2cE7zH.js +1 -0
- package/dist/frontend/assets/utils-UasCLNM3.js +2 -0
- package/dist/frontend/assets/utils-UasCLNM3.js.map +1 -0
- package/dist/frontend/index.html +7 -6
- package/package.json +25 -11
- package/dist/frontend/assets/form-utils-Bskf0D3l.js +0 -41
- package/dist/frontend/assets/form-utils-Bskf0D3l.js.map +0 -1
- package/dist/frontend/assets/index-BAV6nu4X.js +0 -81
- package/dist/frontend/assets/index-BAV6nu4X.js.map +0 -1
- package/dist/frontend/assets/index-HDlbxheg.css +0 -1
- package/dist/frontend/assets/radix-ui-BQCqNqg0.js +0 -2
- package/dist/frontend/assets/radix-ui-BQCqNqg0.js.map +0 -1
- package/dist/frontend/assets/react-vendor-BPQojLhf.js +0 -67
- package/dist/frontend/assets/react-vendor-BPQojLhf.js.map +0 -1
- package/dist/frontend/assets/utils-BWIWSmq9.js +0 -2
- package/dist/frontend/assets/utils-BWIWSmq9.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-C3xvW3AQ.js","names":[],"sources":["../../../apps/frontend/src/services/api.ts","../../../apps/frontend/src/constants/timeouts.ts","../../../apps/frontend/src/services/websocket.ts","../../../apps/frontend/src/stores/status.ts","../../../apps/frontend/src/hooks/useRestartNotifications.ts","../../../apps/frontend/src/components/status-cards/mini-circular-progress.tsx","../../../apps/frontend/src/lib/utils.ts","../../../apps/frontend/src/components/ui/badge.tsx","../../../apps/frontend/src/components/ui/button.tsx","../../../apps/frontend/src/components/ui/alert-dialog.tsx","../../../apps/frontend/src/components/ui/dialog.tsx","../../../apps/frontend/src/components/ui/input.tsx","../../../apps/frontend/src/stores/config.ts","../../../apps/frontend/src/components/mcp-endpoint-setting-button.tsx","../../../apps/frontend/src/components/ui/card.tsx","../../../apps/frontend/src/components/status-cards/endpoint-status-card.tsx","../../../apps/frontend/src/components/ui/label.tsx","../../../apps/frontend/src/components/ui/form.tsx","../../../apps/frontend/src/services/cozeApi.ts","../../../apps/frontend/src/services/index.ts","../../../apps/frontend/src/stores/websocket.ts","../../../apps/frontend/src/hooks/useNetworkService.ts","../../../apps/frontend/src/stores/index.ts","../../../apps/frontend/src/providers/WebSocketProvider.tsx","../../../apps/frontend/src/components/web-url-setting-button.tsx","../../../apps/frontend/src/components/status-cards/client-status-card.tsx","../../../apps/frontend/src/components/ui/select.tsx","../../../apps/frontend/src/components/ui/textarea.tsx","../../../apps/frontend/src/components/form-fields.tsx","../../../apps/frontend/src/config/mcp-form-fields.ts","../../../apps/frontend/src/schemas/mcp-form.ts","../../../apps/frontend/src/components/mcp-server-form.tsx","../../../apps/frontend/src/components/ui/tabs.tsx","../../../apps/frontend/src/utils/mcpFormConverter.ts","../../../apps/frontend/src/utils/mcpValidation.ts","../../../apps/frontend/src/components/add-mcp-server-button.tsx","../../../apps/frontend/src/components/ui/table.tsx","../../../apps/frontend/src/hooks/useServerSearch.ts","../../../apps/frontend/src/hooks/useSortPersistence.ts","../../../apps/frontend/src/hooks/useServerSortPersistence.ts","../../../apps/frontend/src/hooks/useToolPagination.ts","../../../apps/frontend/src/components/mcp-server-setting-button.tsx","../../../apps/frontend/src/components/remove-mcp-server-button.tsx","../../../apps/frontend/src/components/ui/pagination.tsx","../../../apps/frontend/src/components/mcp-tool/tool-pagination.tsx","../../../apps/frontend/src/components/mcp-tool/tool-search-input.tsx","../../../apps/frontend/src/components/mcp-server/server-sort-selector.tsx","../../../apps/frontend/src/components/mcp-server/status-badge.tsx","../../../apps/frontend/src/components/mcp-server/mcp-server-table.tsx","../../../apps/frontend/src/components/mcp-server/mcp-server-table-dialog.tsx","../../../apps/frontend/src/components/ui/scroll-area.tsx","../../../apps/frontend/src/components/ui/separator.tsx","../../../apps/frontend/src/utils/formatUtils.ts","../../../apps/frontend/src/components/tool-call-logs-dialog.tsx","../../../apps/frontend/src/components/status-cards/server-status-card.tsx","../../../apps/frontend/src/components/restart-button.tsx","../../../apps/frontend/src/components/system-setting-dialog.tsx","../../../apps/frontend/src/components/status-cards/system-status-card.tsx","../../../apps/frontend/src/components/ui/alert.tsx","../../../apps/frontend/src/components/prompt-editor-dialog.tsx","../../../apps/frontend/src/components/ui/password-input.tsx","../../../apps/frontend/src/components/voice-interaction-setting-dialog.tsx","../../../apps/frontend/src/components/status-cards/voice-interaction-card.tsx","../../../apps/frontend/src/components/dashboard-status-card.tsx","../../../apps/frontend/src/components/common/collapsible-text.tsx","../../../apps/frontend/src/components/ui/tooltip.tsx","../../../apps/frontend/src/lib/schema-utils.ts","../../../apps/frontend/src/components/tool-debug-dialog.tsx","../../../apps/frontend/src/components/ui/switch.tsx","../../../apps/frontend/src/hooks/useToolSearch.ts","../../../apps/frontend/src/hooks/useToolSortPersistence.ts","../../../apps/frontend/src/components/mcp-tool/tool-sort-selector.tsx","../../../apps/frontend/src/components/mcp-tool/mcp-tool-table.tsx","../../../apps/frontend/src/components/icons/QQ.tsx","../../../apps/frontend/src/components/icons/Github.tsx","../../../apps/frontend/src/hooks/useNPMInstall.ts","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/internal/constants.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/internal/debug.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/internal/re.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/internal/parse-options.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/internal/identifiers.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/classes/semver.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/parse.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/valid.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/clean.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/inc.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/diff.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/major.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/minor.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/patch.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/prerelease.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/compare.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/rcompare.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/compare-loose.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/compare-build.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/sort.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/rsort.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/gt.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/lt.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/eq.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/neq.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/gte.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/lte.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/cmp.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/coerce.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/internal/lrucache.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/classes/range.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/classes/comparator.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/functions/satisfies.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/ranges/to-comparators.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/ranges/max-satisfying.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/ranges/min-satisfying.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/ranges/min-version.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/ranges/valid.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/ranges/outside.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/ranges/gtr.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/ranges/ltr.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/ranges/intersects.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/ranges/simplify.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/ranges/subset.js","../../../node_modules/.pnpm/semver@7.7.3/node_modules/semver/index.js","../../../apps/frontend/src/components/ui/progress.tsx","../../../apps/frontend/src/components/install-log-dialog.tsx","../../../apps/frontend/src/components/version-upgrade-dialog.tsx","../../../apps/frontend/src/components/version-display.tsx","../../../apps/frontend/src/components/site-header.tsx","../../../apps/frontend/src/pages/DashboardPage.tsx","../../../apps/frontend/src/App.tsx","../../../apps/frontend/src/main.tsx"],"sourcesContent":["/**\n * 统一的 HTTP API 客户端\n * 负责所有的配置管理、状态查询和服务控制操作\n */\n\nimport type {\n ApiErrorResponse,\n ApiSuccessResponse,\n AppConfig,\n ClientStatus,\n CustomMCPToolWithStats,\n MCPErrorCode,\n MCPServerAddRequest,\n MCPServerConfig,\n MCPServerListResponse,\n MCPServerStatus,\n VoicesResponse,\n} from \"@xiaozhi-client/shared-types\";\n\n/**\n * CustomMCPTool 接口定义\n * 使用共享类型,与后端保持一致\n * CustomMCPTool 是 CustomMCPToolWithStats 的别名\n */\nexport type CustomMCPTool = CustomMCPToolWithStats;\n\n/**\n * API 响应格式\n */\ninterface ApiResponse<T = any> {\n success: boolean;\n data?: T;\n error?: {\n code: string;\n message: string;\n details?: unknown;\n };\n}\n\n/**\n * 服务状态接口\n */\ninterface ServiceStatus {\n running: boolean;\n mode?: string;\n pid?: number;\n}\n\n/**\n * 服务健康状态接口\n */\ninterface ServiceHealth {\n status: string;\n timestamp: number;\n uptime: number;\n memory: {\n rss: number;\n heapTotal: number;\n heapUsed: number;\n external: number;\n arrayBuffers: number;\n };\n version: string;\n}\n\n/**\n * 版本信息接口\n */\ninterface VersionInfo {\n name: string;\n version: string;\n description: string;\n author: string;\n}\n\n/**\n * 提示词文件信息接口\n */\nexport interface PromptFileInfo {\n /** 文件名 */\n fileName: string;\n /** 相对路径(相对于配置文件所在目录) */\n relativePath: string;\n}\n\n/**\n * 重启状态接口\n */\ninterface RestartStatus {\n status: \"restarting\" | \"completed\" | \"failed\";\n error?: string;\n timestamp: number;\n}\n\n/**\n * 接入点状态响应接口\n */\ninterface EndpointStatusResponse {\n endpoint: string;\n connected: boolean;\n initialized: boolean;\n isReconnecting: boolean;\n reconnectAttempts: number;\n nextReconnectTime?: number;\n reconnectDelay: number;\n}\n\n/**\n * 完整状态接口\n */\ninterface FullStatus {\n client: ClientStatus;\n restart?: RestartStatus;\n timestamp: number;\n}\n\n/**\n * MCP 工具管理操作类型\n */\ntype MCPToolManageAction = \"enable\" | \"disable\" | \"status\" | \"toggle\";\n\n/**\n * MCP 工具管理请求接口\n */\ninterface MCPToolManageRequest {\n action: MCPToolManageAction;\n serverName: string;\n toolName: string;\n description?: string;\n}\n\n/**\n * MCP 工具管理响应接口\n */\ninterface MCPToolManageResponse {\n serverName: string;\n toolName: string;\n enabled: boolean;\n description?: string;\n usageCount?: number;\n lastUsedTime?: string;\n}\n\n/**\n * MCP 工具列表请求接口\n */\ninterface MCPToolListRequest {\n serverName?: string;\n includeUsageStats?: boolean;\n}\n\n/**\n * MCP 工具列表响应接口\n */\ninterface MCPToolListResponse {\n serverName?: string;\n tools: Array<{\n toolName: string;\n enabled: boolean;\n description?: string;\n usageCount?: number;\n lastUsedTime?: string;\n }>;\n total: number;\n enabledCount: number;\n disabledCount: number;\n}\n\n/**\n * HTTP API 客户端类\n */\nexport class ApiClient {\n private baseUrl: string;\n\n constructor(baseUrl?: string) {\n // 从当前页面 URL 推断 API 基础 URL\n if (baseUrl) {\n this.baseUrl = baseUrl;\n } else {\n const protocol = window.location.protocol;\n const hostname = window.location.hostname;\n const port = window.location.port;\n this.baseUrl = `${protocol}//${hostname}${port ? `:${port}` : \"\"}`;\n }\n }\n\n /**\n * 通用请求方法\n */\n private async request<T>(\n endpoint: string,\n options: RequestInit = {}\n ): Promise<T> {\n const url = `${this.baseUrl}${endpoint}`;\n\n const defaultOptions: RequestInit = {\n headers: {\n \"Content-Type\": \"application/json\",\n ...options.headers,\n },\n };\n\n const response = await fetch(url, { ...defaultOptions, ...options });\n\n if (!response.ok) {\n let errorMessage = `HTTP ${response.status}: ${response.statusText}`;\n\n try {\n const errorData: ApiErrorResponse = await response.json();\n errorMessage = errorData.error?.message || errorMessage;\n } catch {\n // 如果无法解析错误响应,使用默认错误消息\n }\n\n throw new Error(errorMessage);\n }\n\n return response.json();\n }\n\n // ==================== 配置管理 API ====================\n\n /**\n * 获取完整配置\n */\n async getConfig(): Promise<AppConfig> {\n const response: ApiResponse<AppConfig> = await this.request(\"/api/config\");\n if (!response.success || !response.data) {\n throw new Error(\"获取配置失败\");\n }\n return response.data;\n }\n\n /**\n * 更新配置\n */\n async updateConfig(config: AppConfig): Promise<void> {\n const response: ApiResponse = await this.request(\"/api/config\", {\n method: \"PUT\",\n body: JSON.stringify(config),\n });\n\n if (!response.success) {\n throw new Error(response.error?.message || \"配置更新失败\");\n }\n }\n\n /**\n * 获取 MCP 端点\n */\n async getMcpEndpoint(): Promise<string> {\n const response: ApiResponse<{ endpoint: string }> = await this.request(\n \"/api/config/mcp-endpoint\"\n );\n if (!response.success || !response.data) {\n throw new Error(\"获取 MCP 端点失败\");\n }\n return response.data.endpoint;\n }\n\n /**\n * 获取 MCP 端点列表\n */\n async getMcpEndpoints(): Promise<string[]> {\n const response: ApiResponse<{ endpoints: string[] }> = await this.request(\n \"/api/config/mcp-endpoints\"\n );\n if (!response.success || !response.data) {\n throw new Error(\"获取 MCP 端点列表失败\");\n }\n return response.data.endpoints;\n }\n\n /**\n * 获取 MCP 服务配置\n */\n async getMcpServers(): Promise<Record<string, any>> {\n const response: ApiResponse<{ servers: Record<string, any> }> =\n await this.request(\"/api/config/mcp-servers\");\n if (!response.success || !response.data) {\n throw new Error(\"获取 MCP 服务配置失败\");\n }\n return response.data.servers;\n }\n\n /**\n * 获取连接配置\n */\n async getConnectionConfig(): Promise<any> {\n const response: ApiResponse<{ connection: any }> = await this.request(\n \"/api/config/connection\"\n );\n if (!response.success || !response.data) {\n throw new Error(\"获取连接配置失败\");\n }\n return response.data.connection;\n }\n\n /**\n * 重新加载配置\n */\n async reloadConfig(): Promise<AppConfig> {\n const response: ApiResponse<AppConfig> = await this.request(\n \"/api/config/reload\",\n { method: \"POST\" }\n );\n if (!response.success || !response.data) {\n throw new Error(\"重新加载配置失败\");\n }\n return response.data;\n }\n\n /**\n * 获取配置文件路径\n */\n async getConfigPath(): Promise<string> {\n const response: ApiResponse<{ path: string }> =\n await this.request(\"/api/config/path\");\n if (!response.success || !response.data) {\n throw new Error(\"获取配置文件路径失败\");\n }\n return response.data.path;\n }\n\n /**\n * 检查配置是否存在\n */\n async checkConfigExists(): Promise<boolean> {\n const response: ApiResponse<{ exists: boolean }> =\n await this.request(\"/api/config/exists\");\n if (!response.success || response.data?.exists === undefined) {\n throw new Error(\"检查配置是否存在失败\");\n }\n return response.data.exists;\n }\n\n /**\n * 获取提示词文件列表\n */\n async getPromptFiles(): Promise<PromptFileInfo[]> {\n const response: ApiResponse<{ prompts: PromptFileInfo[] }> =\n await this.request(\"/api/config/prompts\");\n if (!response.success || !response.data) {\n throw new Error(\"获取提示词文件列表失败\");\n }\n return response.data.prompts;\n }\n\n /**\n * 获取提示词文件内容\n */\n async getPromptFileContent(\n path: string\n ): Promise<{ fileName: string; relativePath: string; content: string }> {\n const response: ApiResponse<{\n fileName: string;\n relativePath: string;\n content: string;\n }> = await this.request(\n `/api/config/prompts/content?path=${encodeURIComponent(path)}`\n );\n if (!response.success || !response.data) {\n throw new Error(response.error?.message || \"获取提示词文件内容失败\");\n }\n return response.data;\n }\n\n /**\n * 更新提示词文件内容\n */\n async updatePromptFileContent(\n path: string,\n content: string\n ): Promise<{ fileName: string; relativePath: string; content: string }> {\n const response: ApiResponse<{\n fileName: string;\n relativePath: string;\n content: string;\n }> = await this.request(\"/api/config/prompts/content\", {\n method: \"PUT\",\n body: JSON.stringify({ path, content }),\n });\n if (!response.success || !response.data) {\n throw new Error(response.error?.message || \"更新提示词文件内容失败\");\n }\n return response.data;\n }\n\n /**\n * 创建新的提示词文件\n */\n async createPromptFile(\n fileName: string,\n content: string\n ): Promise<{ fileName: string; relativePath: string; content: string }> {\n const response: ApiResponse<{\n fileName: string;\n relativePath: string;\n content: string;\n }> = await this.request(\"/api/config/prompts/content\", {\n method: \"POST\",\n body: JSON.stringify({ fileName, content }),\n });\n if (!response.success || !response.data) {\n throw new Error(response.error?.message || \"创建提示词文件失败\");\n }\n return response.data;\n }\n\n /**\n * 删除提示词文件\n */\n async deletePromptFile(path: string): Promise<void> {\n const response: ApiResponse = await this.request(\n `/api/config/prompts/content?path=${encodeURIComponent(path)}`,\n {\n method: \"DELETE\",\n }\n );\n if (!response.success) {\n throw new Error(response.error?.message || \"删除提示词文件失败\");\n }\n }\n\n // ==================== 状态管理 API ====================\n\n /**\n * 获取完整状态\n */\n async getStatus(): Promise<FullStatus> {\n const response: ApiResponse<FullStatus> = await this.request(\"/api/status\");\n if (!response.success || !response.data) {\n throw new Error(\"获取状态失败\");\n }\n return response.data;\n }\n\n /**\n * 获取客户端状态\n */\n async getClientStatus(): Promise<ClientStatus> {\n const response: ApiResponse<ClientStatus> =\n await this.request(\"/api/status/client\");\n if (!response.success || !response.data) {\n throw new Error(\"获取客户端状态失败\");\n }\n return response.data;\n }\n\n /**\n * 获取重启状态\n */\n async getRestartStatus(): Promise<RestartStatus | null> {\n const response: ApiResponse<RestartStatus> = await this.request(\n \"/api/status/restart\"\n );\n if (!response.success) {\n throw new Error(\"获取重启状态失败\");\n }\n return response.data || null;\n }\n\n /**\n * 检查客户端是否连接\n */\n async checkClientConnected(): Promise<boolean> {\n const response: ApiResponse<{ connected: boolean }> = await this.request(\n \"/api/status/connected\"\n );\n if (!response.success || response.data?.connected === undefined) {\n throw new Error(\"检查客户端连接失败\");\n }\n return response.data.connected;\n }\n\n /**\n * 获取最后心跳时间\n */\n async getLastHeartbeat(): Promise<number | null> {\n const response: ApiResponse<{ lastHeartbeat?: number }> =\n await this.request(\"/api/status/heartbeat\");\n if (!response.success) {\n throw new Error(\"获取最后心跳时间失败\");\n }\n return response.data?.lastHeartbeat || null;\n }\n\n /**\n * 获取活跃的 MCP 服务器列表\n */\n async getActiveMCPServers(): Promise<string[]> {\n const response: ApiResponse<{ servers: string[] }> = await this.request(\n \"/api/status/mcp-servers\"\n );\n if (!response.success || !response.data) {\n throw new Error(\"获取活跃 MCP 服务器失败\");\n }\n return response.data.servers;\n }\n\n /**\n * 更新客户端状态\n */\n async updateClientStatus(status: Partial<ClientStatus>): Promise<void> {\n const response: ApiResponse = await this.request(\"/api/status/client\", {\n method: \"PUT\",\n body: JSON.stringify(status),\n });\n\n if (!response.success) {\n throw new Error(response.error?.message || \"更新客户端状态失败\");\n }\n }\n\n /**\n * 设置活跃的 MCP 服务器列表\n */\n async setActiveMCPServers(servers: string[]): Promise<void> {\n const response: ApiResponse = await this.request(\n \"/api/status/mcp-servers\",\n {\n method: \"PUT\",\n body: JSON.stringify({ servers }),\n }\n );\n\n if (!response.success) {\n throw new Error(response.error?.message || \"设置活跃 MCP 服务器失败\");\n }\n }\n\n /**\n * 重置状态\n */\n async resetStatus(): Promise<void> {\n const response: ApiResponse = await this.request(\"/api/status/reset\", {\n method: \"POST\",\n });\n\n if (!response.success) {\n throw new Error(response.error?.message || \"重置状态失败\");\n }\n }\n\n // ==================== 工具管理 API ====================\n\n /**\n * 添加自定义工具\n * 支持新的类型化格式和向后兼容的旧格式\n */\n async addCustomTool(\n workflow: any,\n customName?: string,\n customDescription?: string,\n parameterConfig?: any\n ): Promise<any>;\n\n /**\n * 添加自定义工具(新格式)\n * 支持多种工具类型:MCP 工具、Coze 工作流等\n */\n async addCustomTool(request: {\n type: \"mcp\" | \"coze\" | \"http\" | \"function\";\n data: any;\n }): Promise<any>;\n\n async addCustomTool(\n param1: any,\n customName?: string,\n customDescription?: string,\n parameterConfig?: any\n ): Promise<any> {\n // 判断是否为新格式调用\n if (typeof param1 === \"object\" && \"type\" in param1 && \"data\" in param1) {\n // 新格式:类型化请求\n const response: ApiResponse<{ tool: any }> = await this.request(\n \"/api/tools/custom\",\n {\n method: \"POST\",\n body: JSON.stringify(param1),\n }\n );\n\n if (!response.success || !response.data) {\n throw new Error(response.error?.message || \"添加自定义工具失败\");\n }\n return response.data.tool;\n }\n // 旧格式:向后兼容\n const workflow = param1;\n const response: ApiResponse<{ tool: any }> = await this.request(\n \"/api/tools/custom\",\n {\n method: \"POST\",\n body: JSON.stringify({\n workflow,\n customName,\n customDescription,\n parameterConfig,\n }),\n }\n );\n\n if (!response.success || !response.data) {\n throw new Error(response.error?.message || \"添加自定义工具失败\");\n }\n return response.data.tool;\n }\n\n /**\n * 更新自定义工具配置\n * @param toolName 工具名称\n * @param updateRequest 更新请求\n */\n async updateCustomTool(\n toolName: string,\n updateRequest: {\n type: \"mcp\" | \"coze\" | \"http\" | \"function\";\n data: any;\n }\n ): Promise<any> {\n const response: ApiResponse<{ tool: any }> = await this.request(\n `/api/tools/custom/${encodeURIComponent(toolName)}`,\n {\n method: \"PUT\",\n body: JSON.stringify(updateRequest),\n }\n );\n\n if (!response.success || !response.data) {\n throw new Error(response.error?.message || \"更新自定义工具失败\");\n }\n return response.data.tool;\n }\n\n /**\n * 删除自定义工具\n */\n async removeCustomTool(toolName: string): Promise<void> {\n const response: ApiResponse = await this.request(\n `/api/tools/custom/${encodeURIComponent(toolName)}`,\n {\n method: \"DELETE\",\n }\n );\n\n if (!response.success) {\n throw new Error(response.error?.message || \"删除自定义工具失败\");\n }\n }\n\n /**\n * 获取自定义工具列表\n */\n async getCustomTools(): Promise<any[]> {\n const response: ApiResponse<{ tools: any[] }> =\n await this.request(\"/api/tools/custom\");\n if (!response.success || !response.data) {\n throw new Error(\"获取自定义工具列表失败\");\n }\n return response.data.tools;\n }\n\n /**\n * 获取工具列表\n * 调用 /api/tools/list 端点,返回 { list: CustomMCPTool[], total: number } 格式\n * @param status 筛选状态:'enabled'(已启用)、'disabled'(未启用)、'all'(全部,默认)\n * @param sortConfig 排序配置:可选的排序字段\n */\n async getToolsList(\n status: \"enabled\" | \"disabled\" | \"all\" = \"all\",\n sortConfig?: { field: string }\n ): Promise<CustomMCPToolWithStats[]> {\n // 构建查询参数\n const queryParams = new URLSearchParams();\n if (status !== \"all\") {\n queryParams.append(\"status\", status);\n }\n\n // 添加排序参数\n if (sortConfig) {\n queryParams.append(\"sortBy\", sortConfig.field);\n }\n\n const url = `/api/tools/list${\n queryParams.toString() ? `?${queryParams.toString()}` : \"\"\n }`;\n\n const response: ApiResponse<{ list: CustomMCPTool[]; total: number }> =\n await this.request(url);\n if (!response.success || !response.data) {\n throw new Error(\"获取工具列表失败\");\n }\n return response.data.list;\n }\n\n // ==================== 服务控制 API ====================\n\n /**\n * 重启服务\n */\n async restartService(): Promise<void> {\n const response: ApiResponse = await this.request(\"/api/services/restart\", {\n method: \"POST\",\n });\n\n if (!response.success) {\n throw new Error(response.error?.message || \"重启服务失败\");\n }\n }\n\n /**\n * 停止服务\n */\n async stopService(): Promise<void> {\n const response: ApiResponse = await this.request(\"/api/services/stop\", {\n method: \"POST\",\n });\n\n if (!response.success) {\n throw new Error(response.error?.message || \"停止服务失败\");\n }\n }\n\n /**\n * 启动服务\n */\n async startService(): Promise<void> {\n const response: ApiResponse = await this.request(\"/api/services/start\", {\n method: \"POST\",\n });\n\n if (!response.success) {\n throw new Error(response.error?.message || \"启动服务失败\");\n }\n }\n\n /**\n * 获取服务状态\n */\n async getServiceStatus(): Promise<ServiceStatus> {\n const response: ApiResponse<ServiceStatus> = await this.request(\n \"/api/services/status\"\n );\n if (!response.success || !response.data) {\n throw new Error(\"获取服务状态失败\");\n }\n return response.data;\n }\n\n /**\n * 获取服务健康状态\n */\n async getServiceHealth(): Promise<ServiceHealth> {\n const response: ApiResponse<ServiceHealth> = await this.request(\n \"/api/services/health\"\n );\n if (!response.success || !response.data) {\n throw new Error(\"获取服务健康状态失败\");\n }\n return response.data;\n }\n\n // ==================== 版本信息 API ====================\n\n /**\n * 获取 TTS 音色列表\n */\n async getTTSVoices(): Promise<VoicesResponse> {\n const response: ApiResponse<VoicesResponse> =\n await this.request(\"/api/tts/voices\");\n if (!response.success || !response.data) {\n throw new Error(\"获取音色列表失败\");\n }\n return response.data;\n }\n\n /**\n * 获取版本信息\n */\n async getVersion(): Promise<VersionInfo> {\n const response: ApiResponse<VersionInfo> =\n await this.request(\"/api/version\");\n if (!response.success || !response.data) {\n throw new Error(\"获取版本信息失败\");\n }\n return response.data;\n }\n\n /**\n * 获取版本号(简化接口)\n */\n async getVersionSimple(): Promise<{ version: string }> {\n const response: ApiResponse<{ version: string }> = await this.request(\n \"/api/version/simple\"\n );\n if (!response.success || !response.data) {\n throw new Error(\"获取版本号失败\");\n }\n return response.data;\n }\n\n /**\n * 获取可用版本列表\n * @param type 版本类型:'stable'(正式版)、'rc'(预览版)、'beta'(测试版)、'all'(全部)\n */\n async getAvailableVersions(\n type: \"stable\" | \"rc\" | \"beta\" | \"all\" = \"stable\"\n ): Promise<{\n versions: string[];\n type: string;\n total: number;\n }> {\n // 构建查询参数\n const queryParams = new URLSearchParams();\n if (type !== \"stable\") {\n queryParams.append(\"type\", type);\n }\n\n const url = `/api/version/available${\n queryParams.toString() ? `?${queryParams.toString()}` : \"\"\n }`;\n\n const response: ApiResponse<{\n versions: string[];\n type: string;\n total: number;\n }> = await this.request(url);\n if (!response.success || !response.data) {\n throw new Error(\"获取可用版本列表失败\");\n }\n return response.data;\n }\n\n /**\n * 检查最新版本\n * 返回当前版本、最新版本以及是否有更新\n */\n async getLatestVersion(): Promise<{\n currentVersion: string;\n latestVersion: string | null;\n hasUpdate: boolean;\n error?: string;\n }> {\n const response: ApiResponse<{\n currentVersion: string;\n latestVersion: string | null;\n hasUpdate: boolean;\n error?: string;\n }> = await this.request(\"/api/version/latest\");\n\n if (!response.success || !response.data) {\n throw new Error(\"检查最新版本失败\");\n }\n\n return response.data;\n }\n\n /**\n * 清除版本缓存\n */\n async clearVersionCache(): Promise<void> {\n const response: ApiResponse = await this.request(\n \"/api/version/cache/clear\",\n {\n method: \"POST\",\n }\n );\n if (!response.success) {\n throw new Error(response.error?.message || \"清除版本缓存失败\");\n }\n }\n\n /**\n * 更新版本\n */\n async updateVersion(version: string): Promise<any> {\n const response: ApiResponse = await this.request(\"/api/update\", {\n method: \"POST\",\n body: JSON.stringify({ version }),\n });\n\n if (!response.success) {\n throw new Error(response.error?.message || \"版本更新失败\");\n }\n\n return response.data;\n }\n\n // ==================== 端点管理 API ====================\n\n /**\n * 获取接入点状态\n */\n async getEndpointStatus(endpoint: string): Promise<EndpointStatusResponse> {\n const response: ApiResponse<EndpointStatusResponse> = await this.request(\n \"/api/endpoint/status\",\n {\n method: \"POST\",\n body: JSON.stringify({ endpoint }),\n }\n );\n if (!response.success || !response.data) {\n throw new Error(\"获取接入点状态失败\");\n }\n return response.data;\n }\n\n /**\n * 连接接入点\n */\n async connectEndpoint(endpoint: string): Promise<void> {\n const response: ApiResponse = await this.request(\"/api/endpoint/connect\", {\n method: \"POST\",\n body: JSON.stringify({ endpoint }),\n });\n if (!response.success) {\n throw new Error(response.error?.message || \"连接接入点失败\");\n }\n }\n\n /**\n * 断开接入点\n */\n async disconnectEndpoint(endpoint: string): Promise<void> {\n const response: ApiResponse = await this.request(\n \"/api/endpoint/disconnect\",\n { method: \"POST\", body: JSON.stringify({ endpoint }) }\n );\n if (!response.success) {\n throw new Error(response.error?.message || \"断开接入点失败\");\n }\n }\n\n /**\n * 重连接入点\n */\n async reconnectEndpoint(endpoint: string): Promise<void> {\n const response: ApiResponse = await this.request(\n \"/api/endpoint/reconnect\",\n { method: \"POST\", body: JSON.stringify({ endpoint }) }\n );\n if (!response.success) {\n throw new Error(response.error?.message || \"重连接入点失败\");\n }\n }\n\n /**\n * 添加新接入点\n */\n async addEndpoint(endpoint: string): Promise<EndpointStatusResponse> {\n const response: ApiResponse<EndpointStatusResponse> = await this.request(\n \"/api/endpoint/add\",\n {\n method: \"POST\",\n body: JSON.stringify({ endpoint }),\n }\n );\n if (!response.success || !response.data) {\n throw new Error(response.error?.message || \"添加接入点失败\");\n }\n return response.data;\n }\n\n /**\n * 移除接入点\n */\n async removeEndpoint(endpoint: string): Promise<void> {\n const response: ApiResponse = await this.request(\"/api/endpoint/remove\", {\n method: \"POST\",\n body: JSON.stringify({ endpoint }),\n });\n if (!response.success) {\n throw new Error(response.error?.message || \"移除接入点失败\");\n }\n }\n\n // ==================== MCP 服务器管理 API ====================\n\n /**\n * 添加 MCP 服务器\n * POST /api/mcp-servers\n */\n async addMCPServer(\n name: string,\n config: MCPServerConfig\n ): Promise<MCPServerStatus> {\n const requestData: MCPServerAddRequest = { name, config };\n\n const response: ApiResponse<MCPServerStatus> = await this.request(\n \"/api/mcp-servers\",\n {\n method: \"POST\",\n body: JSON.stringify(requestData),\n }\n );\n\n if (!response.success || !response.data) {\n throw new Error(response.error?.message || \"添加 MCP 服务器失败\");\n }\n\n return response.data;\n }\n\n /**\n * 删除 MCP 服务器\n * DELETE /api/mcp-servers/:serverName\n */\n async removeMCPServer(\n serverName: string\n ): Promise<{ name: string; operation: string; affectedTools: string[] }> {\n const response: ApiResponse<{\n name: string;\n operation: string;\n affectedTools: string[];\n }> = await this.request(\n `/api/mcp-servers/${encodeURIComponent(serverName)}`,\n {\n method: \"DELETE\",\n }\n );\n\n if (!response.success || !response.data) {\n throw new Error(response.error?.message || \"删除 MCP 服务器失败\");\n }\n\n return response.data;\n }\n\n /**\n * 获取 MCP 服务器状态\n * GET /api/mcp-servers/:serverName/status\n */\n async getMCPServerStatus(serverName: string): Promise<MCPServerStatus> {\n const response: ApiResponse<MCPServerStatus> = await this.request(\n `/api/mcp-servers/${encodeURIComponent(serverName)}/status`\n );\n\n if (!response.success || !response.data) {\n throw new Error(response.error?.message || \"获取 MCP 服务器状态失败\");\n }\n\n return response.data;\n }\n\n /**\n * 获取所有 MCP 服务器列表\n * GET /api/mcp-servers\n */\n async listMCPServers(): Promise<MCPServerListResponse> {\n const response: ApiResponse<MCPServerListResponse> =\n await this.request(\"/api/mcp-servers\");\n\n if (!response.success || !response.data) {\n throw new Error(response.error?.message || \"获取 MCP 服务器列表失败\");\n }\n\n return response.data;\n }\n\n /**\n * 检查 MCP 服务器是否存在\n * GET /api/mcp-servers/:serverName/exists\n */\n async checkMCPServerExists(serverName: string): Promise<boolean> {\n try {\n const response: ApiResponse<{ exists: boolean }> = await this.request(\n `/api/mcp-servers/${encodeURIComponent(serverName)}/exists`\n );\n\n return response.success ? response.data?.exists || false : false;\n } catch (error) {\n // 如果返回 404,说明服务器不存在\n if (error instanceof Error && error.message.includes(\"404\")) {\n return false;\n }\n throw error;\n }\n }\n\n /**\n * 更新 MCP 服务器配置\n * PUT /api/mcp-servers/:serverName\n */\n async updateMCPServer(\n serverName: string,\n config: MCPServerConfig\n ): Promise<MCPServerStatus> {\n const response: ApiResponse<MCPServerStatus> = await this.request(\n `/api/mcp-servers/${encodeURIComponent(serverName)}`,\n {\n method: \"PUT\",\n body: JSON.stringify({ config }),\n }\n );\n\n if (!response.success || !response.data) {\n throw new Error(response.error?.message || \"更新 MCP 服务器配置失败\");\n }\n\n return response.data;\n }\n\n /**\n * 调用 MCP 工具\n * POST /api/tools/call\n */\n async callTool(\n serviceName: string,\n toolName: string,\n args: any = {}\n ): Promise<any> {\n const response: ApiResponse = await this.request(\"/api/tools/call\", {\n method: \"POST\",\n body: JSON.stringify({\n serviceName,\n toolName,\n args,\n }),\n });\n\n if (!response.success) {\n throw new Error(response.error?.message || \"调用工具失败\");\n }\n\n return response.data;\n }\n\n /**\n * 重启 MCP 服务器\n * POST /api/mcp-servers/:serverName/restart\n */\n async restartMCPServer(serverName: string): Promise<MCPServerStatus> {\n const response: ApiResponse<MCPServerStatus> = await this.request(\n `/api/mcp-servers/${encodeURIComponent(serverName)}/restart`,\n {\n method: \"POST\",\n }\n );\n\n if (!response.success || !response.data) {\n throw new Error(response.error?.message || \"重启 MCP 服务器失败\");\n }\n\n return response.data;\n }\n\n // ==================== MCP 工具管理 API ====================\n\n /**\n * 管理 MCP 工具(启用/禁用/查询状态/切换)\n * POST /api/tools/mcp/manage\n * @param request 管理请求\n * @returns 工具状态信息\n */\n async manageMCPTool(\n request: MCPToolManageRequest\n ): Promise<MCPToolManageResponse> {\n const response: ApiResponse<MCPToolManageResponse> = await this.request(\n \"/api/tools/mcp/manage\",\n {\n method: \"POST\",\n body: JSON.stringify(request),\n }\n );\n\n if (!response.success || !response.data) {\n throw new Error(response.error?.message || \"MCP 工具管理操作失败\");\n }\n\n return response.data;\n }\n\n /**\n * 获取 MCP 工具列表\n * POST /api/tools/mcp/list\n * @param request 列表请求(可选)\n * @returns 工具列表信息\n */\n async listMCPTools(\n request?: MCPToolListRequest\n ): Promise<MCPToolListResponse> {\n const response: ApiResponse<MCPToolListResponse> = await this.request(\n \"/api/tools/mcp/list\",\n {\n method: \"POST\",\n body: JSON.stringify(request || {}),\n }\n );\n\n if (!response.success || !response.data) {\n throw new Error(response.error?.message || \"获取 MCP 工具列表失败\");\n }\n\n return response.data;\n }\n}\n\n// 创建默认的 API 客户端实例\nexport const apiClient = new ApiClient();\n\n// 导出类型\nexport type {\n ApiResponse,\n ApiErrorResponse,\n ServiceStatus,\n ServiceHealth,\n RestartStatus,\n FullStatus,\n VersionInfo,\n EndpointStatusResponse,\n MCPServerAddRequest,\n MCPServerStatus,\n MCPServerListResponse,\n ApiSuccessResponse,\n MCPErrorCode,\n};\n","/**\n * 超时相关常量\n */\n\n/**\n * WebSocket 重连延迟常量\n */\nexport const WEBSOCKET_RECONNECT_DELAY = 1000;\n","/**\n * 重构后的 WebSocket 管理器\n * 特性:\n * - 严格单例模式\n * - 全局事件总线机制\n * - 完善的错误处理和重连逻辑\n * - 支持多个 store 订阅 WebSocket 事件\n */\n\nimport { WEBSOCKET_RECONNECT_DELAY } from \"@/constants/timeouts\";\nimport type { AppConfig, ClientStatus } from \"@xiaozhi-client/shared-types\";\n\n/**\n * WebSocket 消息类型\n */\ninterface WebSocketMessage {\n type: string;\n data?: unknown;\n timestamp?: number;\n error?: {\n code: string;\n message: string;\n timestamp?: number;\n };\n}\n\n/**\n * 重启状态接口\n */\ninterface RestartStatus {\n status: \"restarting\" | \"completed\" | \"failed\";\n error?: string;\n timestamp: number;\n}\n\n/**\n * 接入点状态变更事件数据\n */\nexport interface EndpointStatusChangedEvent {\n endpoint: string;\n connected: boolean;\n operation: \"connect\" | \"disconnect\" | \"reconnect\";\n success: boolean;\n message?: string;\n timestamp: number;\n}\n\n/**\n * NPM 安装日志事件数据\n */\nexport interface NPMInstallLogEvent {\n version: string;\n installId: string;\n type: \"stdout\" | \"stderr\";\n message: string;\n timestamp: number;\n}\n\n/**\n * NPM 安装开始事件数据\n */\nexport interface NPMInstallStartedEvent {\n version: string;\n installId: string;\n timestamp: number;\n}\n\n/**\n * NPM 安装完成事件数据\n */\nexport interface NPMInstallCompletedEvent {\n version: string;\n installId: string;\n success: boolean;\n duration: number;\n timestamp: number;\n}\n\n/**\n * NPM 安装失败事件数据\n */\nexport interface NPMInstallFailedEvent {\n version: string;\n installId: string;\n error: string;\n duration: number;\n timestamp: number;\n}\n\n/**\n * 事件总线事件类型\n */\ninterface EventBusEvents {\n // 连接状态事件\n \"connection:connecting\": undefined;\n \"connection:connected\": undefined;\n \"connection:disconnected\": undefined;\n \"connection:reconnecting\": { attempt: number; maxAttempts: number };\n \"connection:error\": { error: Error; context?: string };\n\n // 数据更新事件\n \"data:configUpdate\": AppConfig;\n \"data:statusUpdate\": ClientStatus;\n \"data:restartStatus\": RestartStatus;\n\n // 接入点状态事件\n \"data:endpointStatusChanged\": EndpointStatusChangedEvent;\n\n // NPM 安装事件\n \"data:npmInstallStarted\": NPMInstallStartedEvent;\n \"data:npmInstallLog\": NPMInstallLogEvent;\n \"data:npmInstallCompleted\": NPMInstallCompletedEvent;\n \"data:npmInstallFailed\": NPMInstallFailedEvent;\n\n // 系统事件\n \"system:heartbeat\": { timestamp: number };\n \"system:message\": WebSocketMessage;\n \"system:error\": { error: Error; message?: WebSocketMessage };\n}\n\n/**\n * 事件监听器类型\n */\ntype EventListener<T = unknown> = (data: T) => void;\n\n/**\n * WebSocket 连接状态\n */\nenum ConnectionState {\n DISCONNECTED = \"disconnected\",\n CONNECTING = \"connecting\",\n CONNECTED = \"connected\",\n RECONNECTING = \"reconnecting\",\n}\n\n/**\n * WebSocket 管理器配置\n */\ninterface WebSocketManagerConfig {\n url?: string;\n reconnectInterval?: number;\n maxReconnectAttempts?: number;\n heartbeatInterval?: number;\n heartbeatTimeout?: number;\n}\n\n/**\n * 事件总线类 - 支持多个订阅者\n */\nclass EventBus {\n private listeners: Map<keyof EventBusEvents, Set<EventListener<unknown>>> =\n new Map();\n\n /**\n * 订阅事件\n */\n on<K extends keyof EventBusEvents>(\n event: K,\n listener: EventListener<EventBusEvents[K]>\n ): () => void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)!.add(listener as EventListener<unknown>);\n\n // 返回取消订阅函数\n return () => {\n this.off(event, listener);\n };\n }\n\n /**\n * 取消订阅事件\n */\n off<K extends keyof EventBusEvents>(\n event: K,\n listener: EventListener<EventBusEvents[K]>\n ): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n eventListeners.delete(listener as EventListener<unknown>);\n if (eventListeners.size === 0) {\n this.listeners.delete(event);\n }\n }\n }\n\n /**\n * 发布事件\n */\n emit<K extends keyof EventBusEvents>(\n event: K,\n data: EventBusEvents[K]\n ): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n for (const listener of eventListeners) {\n try {\n listener(data);\n } catch (error) {\n console.error(`[EventBus] 事件监听器执行失败 (${event}):`, error);\n }\n }\n }\n }\n\n /**\n * 清除所有监听器\n */\n clear(): void {\n this.listeners.clear();\n }\n\n /**\n * 获取事件监听器数量\n */\n getListenerCount(event?: keyof EventBusEvents): number {\n if (event) {\n return this.listeners.get(event)?.size || 0;\n }\n return Array.from(this.listeners.values()).reduce(\n (total, listeners) => total + listeners.size,\n 0\n );\n }\n}\n\n/**\n * WebSocket 管理器类 - 严格单例模式\n */\nexport class WebSocketManager {\n private static instance: WebSocketManager | null = null;\n private static isCreating = false;\n\n private ws: WebSocket | null = null;\n private url: string;\n private state: ConnectionState = ConnectionState.DISCONNECTED;\n private eventBus: EventBus = new EventBus();\n private reconnectAttempts = 0;\n private maxReconnectAttempts: number;\n private reconnectInterval: number;\n private reconnectTimer?: NodeJS.Timeout;\n private heartbeatTimer?: NodeJS.Timeout;\n private heartbeatInterval: number;\n private heartbeatTimeout: number;\n private lastHeartbeat = 0;\n\n private constructor(config: WebSocketManagerConfig = {}) {\n this.url = config.url || this.getDefaultWebSocketUrl();\n this.maxReconnectAttempts = config.maxReconnectAttempts || 5;\n this.reconnectInterval = config.reconnectInterval || 3000;\n this.heartbeatInterval = config.heartbeatInterval || 30000; // 30秒\n this.heartbeatTimeout = config.heartbeatTimeout || 35000; // 35秒\n }\n\n /**\n * 获取单例实例\n */\n static getInstance(config?: WebSocketManagerConfig): WebSocketManager {\n if (WebSocketManager.instance) {\n return WebSocketManager.instance;\n }\n\n if (WebSocketManager.isCreating) {\n throw new Error(\"[WebSocketManager] 检测到循环创建,请检查代码逻辑\");\n }\n\n WebSocketManager.isCreating = true;\n try {\n WebSocketManager.instance = new WebSocketManager(config);\n console.log(\"[WebSocketManager] 单例实例已创建\");\n return WebSocketManager.instance;\n } finally {\n WebSocketManager.isCreating = false;\n }\n }\n\n /**\n * 重置单例实例(仅用于测试)\n */\n static resetInstance(): void {\n if (WebSocketManager.instance) {\n WebSocketManager.instance.disconnect();\n WebSocketManager.instance.eventBus.clear();\n WebSocketManager.instance = null;\n console.log(\"[WebSocketManager] 单例实例已重置\");\n }\n }\n\n /**\n * 获取默认的 WebSocket URL\n */\n private getDefaultWebSocketUrl(): string {\n // 从 localStorage 获取自定义 URL\n const savedUrl = localStorage.getItem(\"xiaozhi-ws-url\");\n if (savedUrl) {\n return savedUrl;\n }\n\n // 根据当前页面 URL 构建 WebSocket URL\n const protocol = window.location.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n const hostname = window.location.hostname;\n // 固定使用后端端口 9999,与当前页面端口无关\n const port = 9999;\n return `${protocol}//${hostname}:${port}`;\n }\n\n /**\n * 连接 WebSocket\n */\n connect(): void {\n if (\n this.state === ConnectionState.CONNECTED ||\n this.state === ConnectionState.CONNECTING\n ) {\n return;\n }\n\n this.state = ConnectionState.CONNECTING;\n console.log(`[WebSocket] 连接到: ${this.url}`);\n\n // 发布连接中事件\n this.eventBus.emit(\"connection:connecting\", undefined);\n\n try {\n this.ws = new WebSocket(this.url);\n this.setupEventHandlers();\n } catch (error) {\n console.error(\"[WebSocket] 连接失败:\", error);\n this.handleConnectionError(error as Error);\n }\n }\n\n /**\n * 断开 WebSocket 连接\n */\n disconnect(): void {\n console.log(\"[WebSocket] 主动断开连接\");\n\n this.clearTimers();\n this.state = ConnectionState.DISCONNECTED;\n this.reconnectAttempts = 0;\n\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n }\n\n /**\n * 新的事件订阅方法 - 使用事件总线\n */\n subscribe<K extends keyof EventBusEvents>(\n event: K,\n listener: EventListener<EventBusEvents[K]>\n ): () => void {\n return this.eventBus.on(event, listener);\n }\n\n /**\n * 取消事件订阅\n */\n unsubscribe<K extends keyof EventBusEvents>(\n event: K,\n listener: EventListener<EventBusEvents[K]>\n ): void {\n this.eventBus.off(event, listener);\n }\n\n /**\n * 获取事件总线实例(用于高级用法)\n */\n getEventBus(): EventBus {\n return this.eventBus;\n }\n\n /**\n * 获取连接状态\n */\n getState(): ConnectionState {\n return this.state;\n }\n\n /**\n * 检查是否已连接\n */\n isConnected(): boolean {\n return (\n this.state === ConnectionState.CONNECTED &&\n this.ws?.readyState === WebSocket.OPEN\n );\n }\n\n /**\n * 更新 WebSocket URL\n */\n setUrl(url: string): void {\n if (this.url !== url) {\n this.url = url;\n localStorage.setItem(\"xiaozhi-ws-url\", url);\n\n // 如果当前已连接,重新连接到新 URL\n if (this.isConnected()) {\n this.disconnect();\n setTimeout(() => this.connect(), WEBSOCKET_RECONNECT_DELAY);\n }\n }\n }\n\n /**\n * 发送消息\n */\n send(message: unknown): boolean {\n if (!this.isConnected()) {\n console.warn(\"[WebSocket] 连接未建立,无法发送消息\");\n return false;\n }\n\n try {\n const messageStr =\n typeof message === \"string\" ? message : JSON.stringify(message);\n this.ws!.send(messageStr);\n return true;\n } catch (error) {\n console.error(\"[WebSocket] 发送消息失败:\", error);\n this.eventBus.emit(\"connection:error\", {\n error: error as Error,\n context: \"send_message\",\n });\n return false;\n }\n }\n\n /**\n * 获取当前 URL\n */\n getUrl(): string {\n return this.url;\n }\n\n /**\n * 获取连接统计信息\n */\n getConnectionStats() {\n return {\n state: this.state,\n url: this.url,\n reconnectAttempts: this.reconnectAttempts,\n maxReconnectAttempts: this.maxReconnectAttempts,\n lastHeartbeat: this.lastHeartbeat,\n eventListenerCount: this.eventBus.getListenerCount(),\n };\n }\n\n /**\n * 设置 WebSocket 事件处理器\n */\n private setupEventHandlers(): void {\n if (!this.ws) return;\n\n this.ws.onopen = () => {\n console.log(\"[WebSocket] 连接已建立\");\n this.state = ConnectionState.CONNECTED;\n this.reconnectAttempts = 0;\n this.startHeartbeat();\n\n // 发布连接成功事件\n this.eventBus.emit(\"connection:connected\", undefined);\n };\n\n this.ws.onmessage = (event) => {\n try {\n const message: WebSocketMessage = JSON.parse(event.data);\n this.handleMessage(message);\n } catch (error) {\n console.error(\"[WebSocket] 消息解析失败:\", error);\n }\n };\n\n this.ws.onclose = (event) => {\n console.log(`[WebSocket] 连接已关闭 (code: ${event.code})`);\n this.handleConnectionClose();\n };\n\n this.ws.onerror = (error) => {\n console.error(\"[WebSocket] 连接错误:\", error);\n this.handleConnectionError(new Error(\"WebSocket 连接错误\"));\n };\n }\n\n /**\n * 处理 WebSocket 消息\n */\n private handleMessage(message: WebSocketMessage): void {\n console.log(\"[WebSocket] 收到消息:\", message.type);\n\n // 发布原始消息事件\n this.eventBus.emit(\"system:message\", message);\n\n try {\n switch (message.type) {\n case \"configUpdate\":\n case \"config\":\n if (message.data) {\n this.eventBus.emit(\"data:configUpdate\", message.data as AppConfig);\n }\n break;\n\n case \"statusUpdate\":\n case \"status\":\n if (message.data) {\n this.eventBus.emit(\n \"data:statusUpdate\",\n message.data as ClientStatus\n );\n }\n break;\n\n case \"restartStatus\":\n if (message.data) {\n this.eventBus.emit(\n \"data:restartStatus\",\n message.data as RestartStatus\n );\n }\n break;\n\n case \"endpoint_status_changed\":\n if (message.data) {\n this.eventBus.emit(\n \"data:endpointStatusChanged\",\n message.data as EndpointStatusChangedEvent\n );\n }\n break;\n\n case \"npm:install:started\":\n if (message.data) {\n this.eventBus.emit(\n \"data:npmInstallStarted\",\n message.data as NPMInstallStartedEvent\n );\n }\n break;\n\n case \"npm:install:log\":\n if (message.data) {\n this.eventBus.emit(\n \"data:npmInstallLog\",\n message.data as NPMInstallLogEvent\n );\n }\n break;\n\n case \"npm:install:completed\":\n if (message.data) {\n this.eventBus.emit(\n \"data:npmInstallCompleted\",\n message.data as NPMInstallCompletedEvent\n );\n }\n break;\n\n case \"npm:install:failed\":\n if (message.data) {\n this.eventBus.emit(\n \"data:npmInstallFailed\",\n message.data as NPMInstallFailedEvent\n );\n }\n break;\n\n case \"heartbeatResponse\":\n this.lastHeartbeat = Date.now();\n this.eventBus.emit(\"system:heartbeat\", {\n timestamp: this.lastHeartbeat,\n });\n break;\n\n case \"error\": {\n const error = new Error(message.error?.message || \"服务器错误\");\n console.error(\"[WebSocket] 服务器错误:\", message.error);\n this.eventBus.emit(\"system:error\", { error, message });\n this.eventBus.emit(\"connection:error\", {\n error,\n context: \"server_error\",\n });\n break;\n }\n\n default:\n console.log(\"[WebSocket] 未处理的消息类型:\", message.type);\n }\n } catch (error) {\n console.error(\"[WebSocket] 消息处理失败:\", error);\n this.eventBus.emit(\"system:error\", {\n error: error as Error,\n message,\n });\n }\n }\n\n /**\n * 处理连接关闭\n */\n private handleConnectionClose(): void {\n this.state = ConnectionState.DISCONNECTED;\n this.clearTimers();\n\n // 发布连接断开事件\n this.eventBus.emit(\"connection:disconnected\", undefined);\n\n // 如果不是主动断开连接,尝试重连\n if (this.reconnectAttempts < this.maxReconnectAttempts) {\n this.scheduleReconnect();\n } else {\n console.error(\"[WebSocket] 达到最大重连次数,停止重连\");\n this.eventBus.emit(\"connection:error\", {\n error: new Error(\"达到最大重连次数\"),\n context: \"max_reconnect_attempts\",\n });\n }\n }\n\n /**\n * 处理连接错误\n */\n private handleConnectionError(error: Error): void {\n this.state = ConnectionState.DISCONNECTED;\n this.clearTimers();\n\n // 发布连接错误事件\n this.eventBus.emit(\"connection:error\", {\n error,\n context: \"connection_error\",\n });\n\n // 尝试重连\n if (this.reconnectAttempts < this.maxReconnectAttempts) {\n this.scheduleReconnect();\n } else {\n console.error(\"[WebSocket] 达到最大重连次数,停止重连\");\n }\n }\n\n /**\n * 安排重连\n */\n private scheduleReconnect(): void {\n this.reconnectAttempts++;\n this.state = ConnectionState.RECONNECTING;\n\n console.log(\n `[WebSocket] 安排重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts}) 在 ${this.reconnectInterval}ms 后`\n );\n\n // 发布重连事件\n this.eventBus.emit(\"connection:reconnecting\", {\n attempt: this.reconnectAttempts,\n maxAttempts: this.maxReconnectAttempts,\n });\n\n this.reconnectTimer = setTimeout(() => {\n this.connect();\n }, this.reconnectInterval);\n }\n\n /**\n * 开始心跳检测\n */\n private startHeartbeat(): void {\n this.lastHeartbeat = Date.now();\n\n this.heartbeatTimer = setInterval(() => {\n if (this.isConnected()) {\n // 发送心跳消息\n this.sendHeartbeat();\n\n // 检查心跳超时\n const now = Date.now();\n if (now - this.lastHeartbeat > this.heartbeatTimeout) {\n console.warn(\"[WebSocket] 心跳超时,重新连接\");\n this.disconnect();\n this.connect();\n }\n }\n }, this.heartbeatInterval);\n }\n\n /**\n * 发送心跳消息\n */\n private sendHeartbeat(): void {\n if (this.isConnected()) {\n const heartbeatMessage = {\n type: \"clientStatus\",\n data: {\n status: \"connected\" as const,\n timestamp: Date.now(),\n },\n };\n\n this.ws?.send(JSON.stringify(heartbeatMessage));\n }\n }\n\n /**\n * 清理定时器\n */\n private clearTimers(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = undefined;\n }\n\n if (this.heartbeatTimer) {\n clearInterval(this.heartbeatTimer);\n this.heartbeatTimer = undefined;\n }\n }\n}\n\n// 创建默认的 WebSocket 管理器实例(使用单例模式)\nexport const webSocketManager = WebSocketManager.getInstance();\n\n// 导出类型和枚举\nexport { ConnectionState };\nexport type {\n WebSocketMessage,\n RestartStatus,\n WebSocketManagerConfig,\n EventBusEvents,\n EventListener,\n};\n","/**\n * 状态数据统一管理 Store\n *\n * 特性:\n * - 支持定时轮询和 WebSocket 实时更新\n * - 提供异步方法:getStatus()、refreshStatus()、restartService()\n * - 管理重启状态和服务状态\n * - 使用 Zustand 进行状态管理\n * - 提供选择器 hooks 优化组件渲染\n * - 集成 WebSocket 事件监听\n */\n\nimport { apiClient } from \"@/services/api\";\nimport { webSocketManager } from \"@/services/websocket\";\nimport type { ClientStatus } from \"@xiaozhi-client/shared-types\";\nimport { create } from \"zustand\";\nimport { devtools } from \"zustand/middleware\";\nimport { useShallow } from \"zustand/react/shallow\";\n\n/**\n * 重启状态接口\n */\ninterface RestartStatus {\n status: \"restarting\" | \"completed\" | \"failed\";\n error?: string;\n timestamp: number;\n}\n\n/**\n * 服务状态接口\n */\ninterface ServiceStatus {\n running: boolean;\n mode?: string;\n pid?: number;\n}\n\n/**\n * 服务健康状态接口\n */\ninterface ServiceHealth {\n status: string;\n timestamp: number;\n uptime: number;\n memory: {\n rss: number;\n heapTotal: number;\n heapUsed: number;\n external: number;\n arrayBuffers: number;\n };\n version: string;\n}\n\n/**\n * 完整状态接口\n */\ninterface FullStatus {\n client: ClientStatus;\n restart?: RestartStatus;\n timestamp: number;\n}\n\n/**\n * 状态加载状态\n */\ninterface StatusLoadingState {\n isLoading: boolean;\n isRefreshing: boolean;\n isRestarting: boolean;\n lastUpdated: number | null;\n lastError: Error | null;\n}\n\n/**\n * 轮询配置\n */\ninterface PollingConfig {\n enabled: boolean;\n interval: number; // 毫秒\n maxRetries: number;\n currentRetries: number;\n}\n\n/**\n * 重启轮询配置\n */\ninterface RestartPollingConfig {\n enabled: boolean;\n interval: number; // 毫秒,重启检查间隔\n maxAttempts: number; // 最大检查次数\n currentAttempts: number; // 当前检查次数\n timeout: number; // 总超时时间(毫秒)\n startTime: number | null; // 开始时间戳\n}\n\n/**\n * 状态 Store 状态\n */\ninterface StatusState {\n // 状态数据\n clientStatus: ClientStatus | null;\n restartStatus: RestartStatus | null;\n serviceStatus: ServiceStatus | null;\n serviceHealth: ServiceHealth | null;\n fullStatus: FullStatus | null;\n\n // 加载状态\n loading: StatusLoadingState;\n\n // 轮询配置\n polling: PollingConfig;\n\n // 重启轮询配置\n restartPolling: RestartPollingConfig;\n\n // 状态来源追踪\n lastSource: \"http\" | \"websocket\" | \"polling\" | \"initial\" | null;\n}\n\n/**\n * 状态 Store 操作方法\n */\ninterface StatusActions {\n // 基础操作\n setClientStatus: (\n status: ClientStatus,\n source?: \"http\" | \"websocket\" | \"polling\" | \"initial\"\n ) => void;\n setRestartStatus: (\n status: RestartStatus | null,\n source?: \"http\" | \"websocket\" | \"polling\" | \"initial\"\n ) => void;\n setServiceStatus: (status: ServiceStatus) => void;\n setServiceHealth: (health: ServiceHealth) => void;\n setFullStatus: (\n status: FullStatus,\n source?: \"http\" | \"websocket\" | \"polling\" | \"initial\"\n ) => void;\n setLoading: (loading: Partial<StatusLoadingState>) => void;\n setError: (error: Error | null) => void;\n\n // 异步操作\n getStatus: () => Promise<FullStatus>;\n refreshStatus: () => Promise<FullStatus>;\n restartService: () => Promise<void>;\n getServiceStatus: () => Promise<ServiceStatus>;\n getServiceHealth: () => Promise<ServiceHealth>;\n\n // 轮询控制\n startPolling: (interval?: number) => void;\n stopPolling: () => void;\n setPollingConfig: (config: Partial<PollingConfig>) => void;\n\n // 重启轮询控制\n startRestartPolling: () => void;\n stopRestartPolling: () => void;\n setRestartPollingConfig: (config: Partial<RestartPollingConfig>) => void;\n\n // 工具方法\n reset: () => void;\n initialize: () => Promise<void>;\n}\n\n/**\n * 完整的状态 Store 接口\n */\nexport interface StatusStore extends StatusState, StatusActions {}\n\n/**\n * 初始状态\n */\nconst initialState: StatusState = {\n clientStatus: null,\n restartStatus: null,\n serviceStatus: null,\n serviceHealth: null,\n fullStatus: null,\n loading: {\n isLoading: false,\n isRefreshing: false,\n isRestarting: false,\n lastUpdated: null,\n lastError: null,\n },\n polling: {\n enabled: false,\n interval: 30000, // 30秒\n maxRetries: 3,\n currentRetries: 0,\n },\n restartPolling: {\n enabled: false,\n interval: 1000, // 1秒检查间隔\n maxAttempts: 60, // 最多检查60次(60秒)\n currentAttempts: 0,\n timeout: 60000, // 60秒总超时\n startTime: null,\n },\n lastSource: null,\n};\n\n/**\n * 轮询定时器引用\n */\nlet pollingTimer: NodeJS.Timeout | null = null;\n\n/**\n * 重启轮询定时器引用\n */\nlet restartPollingTimer: NodeJS.Timeout | null = null;\n\n/**\n * 创建状态 Store\n */\nexport const useStatusStore = create<StatusStore>()(\n devtools(\n (set, get) => ({\n ...initialState,\n\n // ==================== 基础操作 ====================\n\n setClientStatus: (status: ClientStatus, source = \"http\") => {\n console.log(`[StatusStore] 设置客户端状态,来源: ${source}`);\n set(\n (state) => ({\n clientStatus: status,\n lastSource: source,\n loading: {\n ...state.loading,\n lastUpdated: Date.now(),\n lastError: null,\n },\n }),\n false,\n \"setClientStatus\"\n );\n },\n\n setRestartStatus: (status: RestartStatus | null, source = \"http\") => {\n console.log(`[StatusStore] 设置重启状态,来源: ${source}`);\n set(\n (state) => ({\n restartStatus: status,\n lastSource: source,\n loading: {\n ...state.loading,\n lastUpdated: Date.now(),\n lastError: null,\n },\n }),\n false,\n \"setRestartStatus\"\n );\n },\n\n setServiceStatus: (status: ServiceStatus) => {\n console.log(\"[StatusStore] 设置服务状态\");\n set({ serviceStatus: status }, false, \"setServiceStatus\");\n },\n\n setServiceHealth: (health: ServiceHealth) => {\n console.log(\"[StatusStore] 设置服务健康状态\");\n set({ serviceHealth: health }, false, \"setServiceHealth\");\n },\n\n setFullStatus: (status: FullStatus, source = \"http\") => {\n console.log(`[StatusStore] 设置完整状态,来源: ${source}`);\n set(\n (state) => ({\n fullStatus: status,\n clientStatus: status.client,\n restartStatus: status.restart || null,\n lastSource: source,\n loading: {\n ...state.loading,\n lastUpdated: Date.now(),\n lastError: null,\n },\n }),\n false,\n \"setFullStatus\"\n );\n },\n\n setLoading: (loading: Partial<StatusLoadingState>) => {\n set(\n (state) => ({\n loading: { ...state.loading, ...loading },\n }),\n false,\n \"setLoading\"\n );\n },\n\n setError: (error: Error | null) => {\n set(\n (state) => ({\n loading: { ...state.loading, lastError: error },\n }),\n false,\n \"setError\"\n );\n },\n\n // ==================== 异步操作 ====================\n\n getStatus: async (): Promise<FullStatus> => {\n const { fullStatus, loading } = get();\n\n // 如果已有状态且不超过30秒,直接返回\n if (\n fullStatus &&\n loading.lastUpdated &&\n Date.now() - loading.lastUpdated < 30 * 1000\n ) {\n return fullStatus;\n }\n\n // 否则从服务器获取最新状态\n return get().refreshStatus();\n },\n\n refreshStatus: async (): Promise<FullStatus> => {\n const { setLoading, setFullStatus, setError, polling } = get();\n\n try {\n setLoading({ isRefreshing: true, lastError: null });\n console.log(\"[StatusStore] 开始刷新状态\");\n\n // 从服务器获取最新状态\n const status = await apiClient.getStatus();\n\n // 更新本地状态\n setFullStatus(status, \"http\");\n\n // 重置轮询重试计数\n if (polling.enabled) {\n get().setPollingConfig({ currentRetries: 0 });\n }\n\n console.log(\"[StatusStore] 状态刷新成功\");\n return status;\n } catch (error) {\n const err =\n error instanceof Error ? error : new Error(\"状态刷新失败\");\n console.error(\"[StatusStore] 状态刷新失败:\", err);\n setError(err);\n\n // 增加轮询重试计数\n if (polling.enabled) {\n const newRetries = polling.currentRetries + 1;\n get().setPollingConfig({ currentRetries: newRetries });\n\n // 如果达到最大重试次数,停止轮询\n if (newRetries >= polling.maxRetries) {\n console.warn(\"[StatusStore] 达到最大重试次数,停止轮询\");\n get().stopPolling();\n }\n }\n\n throw err;\n } finally {\n setLoading({ isRefreshing: false });\n }\n },\n\n restartService: async (): Promise<void> => {\n const { setLoading, setRestartStatus, setError, startRestartPolling } =\n get();\n\n try {\n setLoading({ isRestarting: true, lastError: null });\n console.log(\"[StatusStore] 开始重启服务\");\n\n // 设置重启状态\n setRestartStatus(\n {\n status: \"restarting\",\n timestamp: Date.now(),\n },\n \"http\"\n );\n\n // 调用重启 API\n await apiClient.restartService();\n\n console.log(\"[StatusStore] 服务重启请求已发送,开始重连检查\");\n\n // 启动重启后的重连检查轮询\n startRestartPolling();\n } catch (error) {\n const err =\n error instanceof Error ? error : new Error(\"服务重启失败\");\n console.error(\"[StatusStore] 服务重启失败:\", err);\n\n // 设置重启失败状态\n setRestartStatus(\n {\n status: \"failed\",\n error: err.message,\n timestamp: Date.now(),\n },\n \"http\"\n );\n\n setError(err);\n setLoading({ isRestarting: false });\n throw err;\n }\n },\n\n getServiceStatus: async (): Promise<ServiceStatus> => {\n try {\n console.log(\"[StatusStore] 获取服务状态\");\n const status = await apiClient.getServiceStatus();\n get().setServiceStatus(status);\n return status;\n } catch (error) {\n const err =\n error instanceof Error ? error : new Error(\"获取服务状态失败\");\n console.error(\"[StatusStore] 获取服务状态失败:\", err);\n get().setError(err);\n throw err;\n }\n },\n\n getServiceHealth: async (): Promise<ServiceHealth> => {\n try {\n console.log(\"[StatusStore] 获取服务健康状态\");\n const health = await apiClient.getServiceHealth();\n get().setServiceHealth(health);\n return health;\n } catch (error) {\n const err =\n error instanceof Error ? error : new Error(\"获取服务健康状态失败\");\n console.error(\"[StatusStore] 获取服务健康状态失败:\", err);\n get().setError(err);\n throw err;\n }\n },\n\n // ==================== 轮询控制 ====================\n\n startPolling: (interval = 30000) => {\n const { polling, refreshStatus } = get();\n\n if (polling.enabled) {\n console.log(\"[StatusStore] 轮询已启用,跳过启动\");\n return;\n }\n\n console.log(`[StatusStore] 启动状态轮询,间隔: ${interval}ms`);\n\n set(\n (state) => ({\n polling: {\n ...state.polling,\n enabled: true,\n interval,\n currentRetries: 0,\n },\n }),\n false,\n \"startPolling\"\n );\n\n // 立即执行一次刷新\n refreshStatus().catch((error) => {\n console.error(\"[StatusStore] 轮询初始刷新失败:\", error);\n });\n\n // 设置定时器\n pollingTimer = setInterval(() => {\n const currentState = get();\n if (!currentState.polling.enabled) {\n return;\n }\n\n refreshStatus().catch((error) => {\n console.error(\"[StatusStore] 轮询刷新失败:\", error);\n });\n }, interval);\n },\n\n stopPolling: () => {\n console.log(\"[StatusStore] 停止状态轮询\");\n\n set(\n (state) => ({\n polling: {\n ...state.polling,\n enabled: false,\n currentRetries: 0,\n },\n }),\n false,\n \"stopPolling\"\n );\n\n if (pollingTimer) {\n clearInterval(pollingTimer);\n pollingTimer = null;\n }\n },\n\n setPollingConfig: (config: Partial<PollingConfig>) => {\n set(\n (state) => ({\n polling: { ...state.polling, ...config },\n }),\n false,\n \"setPollingConfig\"\n );\n },\n\n // ==================== 重启轮询控制 ====================\n\n startRestartPolling: () => {\n const { restartPolling, refreshStatus, setRestartStatus, setLoading } =\n get();\n\n if (restartPolling.enabled) {\n console.log(\"[StatusStore] 重启轮询已启用,跳过启动\");\n return;\n }\n\n console.log(\"[StatusStore] 启动重启后重连检查轮询\");\n\n const startTime = Date.now();\n set(\n (state) => ({\n restartPolling: {\n ...state.restartPolling,\n enabled: true,\n currentAttempts: 0,\n startTime,\n },\n }),\n false,\n \"startRestartPolling\"\n );\n\n // 设置定时器进行重连检查\n restartPollingTimer = setInterval(async () => {\n const currentState = get();\n const { restartPolling: currentRestartPolling } = currentState;\n\n if (!currentRestartPolling.enabled) {\n return;\n }\n\n const elapsed = Date.now() - (currentRestartPolling.startTime || 0);\n const attempts = currentRestartPolling.currentAttempts + 1;\n\n console.log(\n `[StatusStore] 重启重连检查 (第 ${attempts} 次,已用时 ${Math.round(elapsed / 1000)}s)`\n );\n\n try {\n // 尝试获取状态以检查服务是否重连成功\n const status = await refreshStatus();\n\n // 检查是否重连成功\n const isReconnected = status.client?.status === \"connected\";\n\n if (isReconnected) {\n console.log(\"[StatusStore] 服务重连成功,停止重启轮询\");\n\n // 设置重启完成状态\n setRestartStatus(\n {\n status: \"completed\",\n timestamp: Date.now(),\n },\n \"polling\"\n );\n\n // 停止重启轮询和loading状态\n currentState.stopRestartPolling();\n setLoading({ isRestarting: false });\n return;\n }\n\n // 更新尝试次数\n currentState.setRestartPollingConfig({ currentAttempts: attempts });\n\n // 检查是否超时或达到最大尝试次数\n if (\n elapsed >= currentRestartPolling.timeout ||\n attempts >= currentRestartPolling.maxAttempts\n ) {\n console.warn(\"[StatusStore] 重启重连检查超时或达到最大尝试次数\");\n\n // 设置重启失败状态\n setRestartStatus(\n {\n status: \"failed\",\n error: \"重连超时,服务可能未成功重启\",\n timestamp: Date.now(),\n },\n \"polling\"\n );\n\n // 停止重启轮询和loading状态\n currentState.stopRestartPolling();\n setLoading({ isRestarting: false });\n }\n } catch (error) {\n console.log(\n `[StatusStore] 重启重连检查失败 (第 ${attempts} 次):`,\n error\n );\n\n // 更新尝试次数\n currentState.setRestartPollingConfig({ currentAttempts: attempts });\n\n // 检查是否超时或达到最大尝试次数\n if (\n elapsed >= currentRestartPolling.timeout ||\n attempts >= currentRestartPolling.maxAttempts\n ) {\n console.error(\"[StatusStore] 重启重连检查超时或达到最大尝试次数\");\n\n // 设置重启失败状态\n setRestartStatus(\n {\n status: \"failed\",\n error: \"重连超时,服务可能未成功重启\",\n timestamp: Date.now(),\n },\n \"polling\"\n );\n\n // 停止重启轮询和loading状态\n currentState.stopRestartPolling();\n setLoading({ isRestarting: false });\n }\n }\n }, restartPolling.interval);\n },\n\n stopRestartPolling: () => {\n console.log(\"[StatusStore] 停止重启轮询\");\n\n set(\n (state) => ({\n restartPolling: {\n ...state.restartPolling,\n enabled: false,\n currentAttempts: 0,\n startTime: null,\n },\n }),\n false,\n \"stopRestartPolling\"\n );\n\n if (restartPollingTimer) {\n clearInterval(restartPollingTimer);\n restartPollingTimer = null;\n }\n },\n\n setRestartPollingConfig: (config: Partial<RestartPollingConfig>) => {\n set(\n (state) => ({\n restartPolling: { ...state.restartPolling, ...config },\n }),\n false,\n \"setRestartPollingConfig\"\n );\n },\n\n // ==================== 工具方法 ====================\n\n reset: () => {\n console.log(\"[StatusStore] 重置状态\");\n\n // 停止所有轮询\n get().stopPolling();\n get().stopRestartPolling();\n\n // 重置状态\n set(initialState, false, \"reset\");\n },\n\n initialize: async (): Promise<void> => {\n const { setLoading, refreshStatus } = get();\n\n try {\n setLoading({ isLoading: true });\n console.log(\"[StatusStore] 初始化状态 Store\");\n\n // 设置 WebSocket 事件监听\n webSocketManager.subscribe(\"data:statusUpdate\", (status) => {\n console.log(\"[StatusStore] 收到 WebSocket 状态更新\");\n get().setClientStatus(status, \"websocket\");\n });\n\n webSocketManager.subscribe(\"data:restartStatus\", (status) => {\n console.log(\"[StatusStore] 收到 WebSocket 重启状态更新\");\n get().setRestartStatus(status, \"websocket\");\n });\n\n // 获取初始状态\n await refreshStatus();\n\n // 启动轮询(可选)\n // get().startPolling();\n\n console.log(\"[StatusStore] 状态 Store 初始化完成\");\n } catch (error) {\n console.error(\"[StatusStore] 状态 Store 初始化失败:\", error);\n throw error;\n } finally {\n setLoading({ isLoading: false });\n }\n },\n }),\n {\n name: \"status-store\",\n }\n )\n);\n\n// ==================== 选择器 Hooks ====================\n\n/**\n * 获取客户端状态\n */\nexport const useClientStatus = () =>\n useStatusStore((state) => state.clientStatus);\n\n/**\n * 获取重启状态\n */\nexport const useRestartStatus = () =>\n useStatusStore((state) => state.restartStatus);\n\n/**\n * 获取服务状态\n */\nexport const useServiceStatus = () =>\n useStatusStore((state) => state.serviceStatus);\n\n/**\n * 获取服务健康状态\n */\nexport const useServiceHealth = () =>\n useStatusStore((state) => state.serviceHealth);\n\n/**\n * 获取完整状态\n */\nexport const useFullStatus = () => useStatusStore((state) => state.fullStatus);\n\n/**\n * 获取状态加载状态\n */\nexport const useStatusLoading = () => useStatusStore((state) => state.loading);\n\n/**\n * 获取状态是否正在加载\n */\nexport const useStatusIsLoading = () =>\n useStatusStore(\n (state) => state.loading.isLoading || state.loading.isRefreshing\n );\n\n/**\n * 获取状态是否正在重启\n */\nexport const useStatusIsRestarting = () =>\n useStatusStore((state) => state.loading.isRestarting);\n\n/**\n * 获取状态错误\n */\nexport const useStatusError = () =>\n useStatusStore((state) => state.loading.lastError);\n\n/**\n * 获取轮询配置\n */\nexport const usePollingConfig = () => useStatusStore((state) => state.polling);\n\n/**\n * 获取轮询是否启用\n */\nexport const usePollingEnabled = () =>\n useStatusStore((state) => state.polling.enabled);\n\n/**\n * 获取重启轮询状态\n */\nexport const useRestartPollingStatus = () =>\n useStatusStore((state) => state.restartPolling);\n\n/**\n * 获取状态来源\n */\nexport const useStatusSource = () =>\n useStatusStore((state) => state.lastSource);\n\n/**\n * 获取连接状态(从客户端状态中提取)\n */\nexport const useConnectionStatus = () =>\n useStatusStore((state) => state.clientStatus?.status === \"connected\");\n\n/**\n * 获取 MCP 端点(从客户端状态中提取)\n */\nexport const useStatusMcpEndpoint = () =>\n useStatusStore((state) => state.clientStatus?.mcpEndpoint);\n\n/**\n * 获取活跃的 MCP 服务器(从客户端状态中提取)\n */\nexport const useActiveMcpServers = () =>\n useStatusStore((state) => state.clientStatus?.activeMCPServers || []);\n\n/**\n * 获取最后心跳时间(从客户端状态中提取)\n */\nexport const useLastHeartbeat = () =>\n useStatusStore((state) => state.clientStatus?.lastHeartbeat);\n\n// ==================== 复合选择器 ====================\n\n/**\n * 获取状态数据和加载状态\n */\nexport const useStatusWithLoading = () =>\n useStatusStore(\n useShallow((state) => ({\n clientStatus: state.clientStatus,\n restartStatus: state.restartStatus,\n fullStatus: state.fullStatus,\n isLoading: state.loading.isLoading || state.loading.isRefreshing,\n isRestarting: state.loading.isRestarting,\n error: state.loading.lastError,\n }))\n );\n\n/**\n * 获取服务相关状态\n */\nexport const useServiceInfo = () =>\n useStatusStore(\n useShallow((state) => ({\n status: state.serviceStatus,\n health: state.serviceHealth,\n isRestarting: state.loading.isRestarting,\n restartStatus: state.restartStatus,\n }))\n );\n\n/**\n * 获取连接相关信息\n */\nexport const useConnectionInfo = () =>\n useStatusStore(\n useShallow((state) => ({\n connected: state.clientStatus?.status === \"connected\",\n endpoint: state.clientStatus?.mcpEndpoint,\n activeServers: state.clientStatus?.activeMCPServers || [],\n lastHeartbeat: state.clientStatus?.lastHeartbeat,\n }))\n );\n\n// ==================== 操作方法 Hooks ====================\n\n/**\n * 获取状态操作方法\n */\nexport const useStatusActions = () =>\n useStatusStore(\n useShallow((state) => ({\n getStatus: state.getStatus,\n refreshStatus: state.refreshStatus,\n restartService: state.restartService,\n getServiceStatus: state.getServiceStatus,\n getServiceHealth: state.getServiceHealth,\n startPolling: state.startPolling,\n stopPolling: state.stopPolling,\n setPollingConfig: state.setPollingConfig,\n startRestartPolling: state.startRestartPolling,\n stopRestartPolling: state.stopRestartPolling,\n setRestartPollingConfig: state.setRestartPollingConfig,\n reset: state.reset,\n initialize: state.initialize,\n }))\n );\n\n/**\n * 获取轮询控制方法\n */\nexport const usePollingActions = () =>\n useStatusStore(\n useShallow((state) => ({\n startPolling: state.startPolling,\n stopPolling: state.stopPolling,\n setPollingConfig: state.setPollingConfig,\n enabled: state.polling.enabled,\n interval: state.polling.interval,\n }))\n );\n","/**\n * 重启通知管理 Hook\n *\n * 职责:\n * - 监听重启状态变化\n * - 在适当时机显示 toast 通知\n * - 避免重复通知\n * - 提供一致的用户体验\n */\n\nimport { useRestartPollingStatus, useRestartStatus } from \"@/stores/status\";\nimport { useEffect, useRef } from \"react\";\nimport { toast } from \"sonner\";\n\n/**\n * 重启通知管理 Hook\n *\n * 使用方式:\n * 1. 在应用的根组件或布局组件中调用\n * 2. 自动监听重启状态变化并显示相应通知\n * 3. 确保全局只有一个实例在运行\n */\nexport function useRestartNotifications() {\n const restartStatus = useRestartStatus();\n const restartPollingStatus = useRestartPollingStatus();\n\n // 使用 ref 来跟踪已显示的通知,避免重复\n const lastNotifiedStatus = useRef<string | null>(null);\n const lastNotifiedTimestamp = useRef<number | null>(null);\n\n useEffect(() => {\n if (!restartStatus) {\n return;\n }\n\n const { status, timestamp, error } = restartStatus;\n\n // 避免重复通知:检查状态和时间戳是否已经通知过\n if (\n lastNotifiedStatus.current === status &&\n lastNotifiedTimestamp.current === timestamp\n ) {\n return;\n }\n\n // 更新已通知的状态\n lastNotifiedStatus.current = status;\n lastNotifiedTimestamp.current = timestamp;\n\n switch (status) {\n case \"restarting\":\n // 重启开始时的通知(可选,根据需要启用)\n toast.info(\"正在重启服务...\", {\n id: \"restart-status-progress\", // 使用固定ID避免重复\n description: \"请耐心等待\",\n duration: 0,\n });\n break;\n\n case \"completed\": {\n // 重启成功通知\n const successMessage = restartPollingStatus.enabled\n ? `服务重启成功!重连检查完成 (${restartPollingStatus.currentAttempts}次检查)`\n : \"服务重启成功!\";\n\n toast.dismiss(\"restart-status-progress\");\n toast.success(successMessage, {\n id: \"restart-status-success\", // 替换之前的通知\n description: \"服务已恢复正常运行\",\n });\n break;\n }\n\n case \"failed\": {\n // 重启失败通知\n const failureMessage = error || \"服务重启失败\";\n const description = restartPollingStatus.enabled\n ? `重连检查超时 (${restartPollingStatus.currentAttempts}/${restartPollingStatus.maxAttempts}次)`\n : \"请检查服务状态或稍后重试\";\n\n toast.dismiss(\"restart-status-progress\");\n toast.error(failureMessage, {\n id: \"restart-status-failed\", // 替换之前的通知\n description,\n });\n break;\n }\n }\n }, [restartStatus, restartPollingStatus]);\n\n // 清理函数:组件卸载时清理状态\n useEffect(() => {\n return () => {\n lastNotifiedStatus.current = null;\n lastNotifiedTimestamp.current = null;\n };\n }, []);\n}\n\n/**\n * 重启通知提供者组件\n *\n * 使用方式:在应用根组件中使用\n * <RestartNotificationProvider />\n */\nexport function RestartNotificationProvider() {\n useRestartNotifications();\n return null; // 这是一个纯逻辑组件,不渲染任何内容\n}\n","/**\n * 迷你圆形进度条组件\n *\n * 用于在状态卡片中显示进度或状态的圆形进度条。\n * 支持自定义颜色、尺寸、最大值等属性。\n */\n\n/**\n * MiniCircularProgress 组件的属性接口\n */\nexport interface MiniCircularProgressProps {\n /** 是否显示数值 */\n showValue?: boolean;\n /** 当前进度值 */\n value?: number;\n /** 最大值(用于计算进度百分比) */\n maxValue?: number;\n /** 组件尺寸(像素) */\n size?: number;\n /** 激活状态的颜色(十六进制或CSS颜色值) */\n activeColor?: string;\n /** 未激活状态的颜色(十六进制或CSS颜色值) */\n inactiveColor?: string;\n /** 数值后缀符号(如 \"%\") */\n symbol?: string;\n /** 可访问性标签 */\n ariaLabel?: string;\n}\n\n/**\n * 迷你圆形进度条组件\n *\n * @param props - 组件属性\n * @returns JSX 元素\n *\n * @example\n * ```tsx\n * <MiniCircularProgress\n * value={75}\n * maxValue={100}\n * activeColor=\"#16a34a\"\n * inactiveColor=\"#e5e7eb\"\n * size={60}\n * />\n * ```\n */\nexport function MiniCircularProgress({\n showValue = true,\n value = 0,\n maxValue = 100,\n size = 60,\n activeColor = \"#3b82f6\",\n inactiveColor = \"#e5e7eb\",\n symbol = \"%\",\n ariaLabel,\n}: MiniCircularProgressProps) {\n const radius = (size - 6) / 2;\n const circumference = radius * 2 * Math.PI;\n const strokeDasharray = circumference;\n // 防止除零错误,当maxValue为0时,将strokeDashoffset设为circumference(显示为空)\n const strokeDashoffset =\n maxValue === 0\n ? circumference\n : circumference - (value / maxValue) * circumference;\n\n return (\n <div\n className=\"relative inline-flex items-center justify-center\"\n role=\"progressbar\"\n tabIndex={0}\n aria-valuenow={value}\n aria-valuemin={0}\n aria-valuemax={maxValue}\n aria-label={ariaLabel || `进度:${value}/${maxValue}`}\n >\n <svg width={size} height={size} className=\"transform -rotate-90\">\n <circle\n cx={size / 2}\n cy={size / 2}\n r={radius}\n stroke={inactiveColor}\n strokeWidth={6}\n fill=\"none\"\n />\n <circle\n cx={size / 2}\n cy={size / 2}\n r={radius}\n stroke={activeColor}\n strokeWidth={6}\n fill=\"none\"\n strokeDasharray={strokeDasharray}\n strokeDashoffset={strokeDashoffset}\n strokeLinecap=\"round\"\n className=\"transition-all duration-300 ease-in-out\"\n />\n </svg>\n {showValue && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <span className=\"text-xs font-medium\">\n {value}\n {symbol}\n </span>\n </div>\n )}\n </div>\n );\n}\n","/**\n * UI 工具函数模块\n *\n * 提供前端 UI 相关的通用工具函数\n *\n * @module utils\n *\n * @example\n * ```typescript\n * import { cn } from '@/lib/utils';\n *\n * // 合并 Tailwind CSS 类名,自动处理冲突\n * const classes = cn('px-4 py-2', isActive && 'bg-blue-500', 'rounded-lg');\n * ```\n */\nimport type { ClassValue } from \"clsx\";\nimport { clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import { type VariantProps, cva } from \"class-variance-authority\";\nimport type * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst badgeVariants = cva(\n \"inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2\",\n {\n variants: {\n variant: {\n default:\n \"border-transparent bg-primary text-primary-foreground hover:bg-primary/80\",\n secondary:\n \"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n destructive:\n \"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80\",\n outline: \"text-foreground\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n }\n);\n\nexport interface BadgeProps\n extends React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof badgeVariants> {}\n\nfunction Badge({ className, variant, ...props }: BadgeProps) {\n return (\n <div className={cn(badgeVariants({ variant }), className)} {...props} />\n );\n}\n\nexport { Badge, badgeVariants };\n","import { Slot } from \"@radix-ui/react-slot\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst buttonVariants = cva(\n \"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n destructive:\n \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n outline:\n \"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\n secondary:\n \"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\n ghost: \"hover:bg-accent hover:text-accent-foreground\",\n link: \"text-primary underline-offset-4 hover:underline\",\n },\n size: {\n default: \"h-10 px-4 py-2\",\n sm: \"h-9 rounded-md px-3\",\n lg: \"h-11 rounded-md px-8\",\n icon: \"h-10 w-10\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n);\n\nexport interface ButtonProps\n extends React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean;\n}\n\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n ({ className, variant, size, asChild = false, ...props }, ref) => {\n const Comp = asChild ? Slot : \"button\";\n return (\n <Comp\n className={cn(buttonVariants({ variant, size, className }))}\n ref={ref}\n {...props}\n />\n );\n }\n);\nButton.displayName = \"Button\";\n\nexport { Button, buttonVariants };\n","import * as AlertDialogPrimitive from \"@radix-ui/react-alert-dialog\";\nimport * as React from \"react\";\n\nimport { buttonVariants } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/utils\";\n\nconst AlertDialog = AlertDialogPrimitive.Root;\n\nconst AlertDialogTrigger = AlertDialogPrimitive.Trigger;\n\nconst AlertDialogPortal = AlertDialogPrimitive.Portal;\n\nconst AlertDialogOverlay = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Overlay\n className={cn(\n \"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className\n )}\n {...props}\n ref={ref}\n />\n));\nAlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;\n\nconst AlertDialogContent = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>\n>(({ className, ...props }, ref) => (\n <AlertDialogPortal>\n <AlertDialogOverlay />\n <AlertDialogPrimitive.Content\n ref={ref}\n className={cn(\n \"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg\",\n className\n )}\n {...props}\n />\n </AlertDialogPortal>\n));\nAlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;\n\nconst AlertDialogHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col space-y-2 text-center sm:text-left\",\n className\n )}\n {...props}\n />\n);\nAlertDialogHeader.displayName = \"AlertDialogHeader\";\n\nconst AlertDialogFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n className\n )}\n {...props}\n />\n);\nAlertDialogFooter.displayName = \"AlertDialogFooter\";\n\nconst AlertDialogTitle = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Title\n ref={ref}\n className={cn(\"text-lg font-semibold\", className)}\n {...props}\n />\n));\nAlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;\n\nconst AlertDialogDescription = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Description\n ref={ref}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n));\nAlertDialogDescription.displayName =\n AlertDialogPrimitive.Description.displayName;\n\nconst AlertDialogAction = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Action>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Action\n ref={ref}\n className={cn(buttonVariants(), className)}\n {...props}\n />\n));\nAlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;\n\nconst AlertDialogCancel = React.forwardRef<\n React.ElementRef<typeof AlertDialogPrimitive.Cancel>,\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>\n>(({ className, ...props }, ref) => (\n <AlertDialogPrimitive.Cancel\n ref={ref}\n className={cn(\n buttonVariants({ variant: \"outline\" }),\n \"mt-2 sm:mt-0\",\n className\n )}\n {...props}\n />\n));\nAlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;\n\nexport {\n AlertDialog,\n AlertDialogPortal,\n AlertDialogOverlay,\n AlertDialogTrigger,\n AlertDialogContent,\n AlertDialogHeader,\n AlertDialogFooter,\n AlertDialogTitle,\n AlertDialogDescription,\n AlertDialogAction,\n AlertDialogCancel,\n};\n","import * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { X } from \"lucide-react\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst Dialog = DialogPrimitive.Root;\n\nconst DialogTrigger = DialogPrimitive.Trigger;\n\nconst DialogPortal = DialogPrimitive.Portal;\n\nconst DialogClose = DialogPrimitive.Close;\n\nconst DialogOverlay = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Overlay>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Overlay\n ref={ref}\n className={cn(\n \"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n className\n )}\n {...props}\n />\n));\nDialogOverlay.displayName = DialogPrimitive.Overlay.displayName;\n\nconst DialogContent = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>\n>(({ className, children, ...props }, ref) => (\n <DialogPortal>\n <DialogOverlay />\n <DialogPrimitive.Content\n ref={ref}\n className={cn(\n \"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg\",\n className\n )}\n {...props}\n >\n {children}\n <DialogPrimitive.Close className=\"absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground\">\n <X className=\"h-4 w-4\" />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n </DialogPrimitive.Content>\n </DialogPortal>\n));\nDialogContent.displayName = DialogPrimitive.Content.displayName;\n\nconst DialogHeader = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col space-y-1.5 text-center sm:text-left\",\n className\n )}\n {...props}\n />\n);\nDialogHeader.displayName = \"DialogHeader\";\n\nconst DialogFooter = ({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) => (\n <div\n className={cn(\n \"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\",\n className\n )}\n {...props}\n />\n);\nDialogFooter.displayName = \"DialogFooter\";\n\nconst DialogTitle = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Title>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Title\n ref={ref}\n className={cn(\n \"text-lg font-semibold leading-none tracking-tight\",\n className\n )}\n {...props}\n />\n));\nDialogTitle.displayName = DialogPrimitive.Title.displayName;\n\nconst DialogDescription = React.forwardRef<\n React.ElementRef<typeof DialogPrimitive.Description>,\n React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>\n>(({ className, ...props }, ref) => (\n <DialogPrimitive.Description\n ref={ref}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n));\nDialogDescription.displayName = DialogPrimitive.Description.displayName;\n\nexport {\n Dialog,\n DialogPortal,\n DialogOverlay,\n DialogClose,\n DialogTrigger,\n DialogContent,\n DialogHeader,\n DialogFooter,\n DialogTitle,\n DialogDescription,\n};\n","import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst Input = React.forwardRef<HTMLInputElement, React.ComponentProps<\"input\">>(\n ({ className, type, ...props }, ref) => {\n return (\n <input\n type={type}\n className={cn(\n \"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n className\n )}\n ref={ref}\n data-1p-ignore\n {...props}\n />\n );\n }\n);\nInput.displayName = \"Input\";\n\nexport { Input };\n","/**\n * 配置数据统一管理 Store\n *\n * 特性:\n * - 支持 HTTP API 和 WebSocket 双重数据源\n * - 提供异步方法:getConfig()、updateConfig()、refreshConfig()\n * - 使用 Zustand 进行状态管理\n * - 提供选择器 hooks 优化组件渲染\n * - 集成 WebSocket 事件监听\n */\n\nimport { apiClient } from \"@/services/api\";\nimport { webSocketManager } from \"@/services/websocket\";\nimport type {\n AppConfig,\n ConnectionConfig,\n MCPServerConfig,\n MCPServerStatus,\n ModelScopeConfig,\n WebUIConfig,\n} from \"@xiaozhi-client/shared-types\";\nimport { create } from \"zustand\";\nimport { devtools } from \"zustand/middleware\";\nimport { useShallow } from \"zustand/react/shallow\";\n\n/**\n * 配置加载状态\n */\ninterface ConfigLoadingState {\n isLoading: boolean;\n isUpdating: boolean;\n isRefreshing: boolean;\n lastUpdated: number | null;\n lastError: Error | null;\n}\n\n/**\n * 配置 Store 状态\n */\ninterface ConfigState {\n // 配置数据\n config: AppConfig | null;\n\n // 加载状态\n loading: ConfigLoadingState;\n\n // 配置来源追踪\n lastSource: \"http\" | \"websocket\" | \"initial\" | null;\n\n // MCP 服务器状态缓存\n mcpServerStatuses: MCPServerStatus[];\n mcpServerStatusLoading: boolean;\n mcpServerStatusLastUpdate: number | null;\n}\n\n/**\n * 配置 Store 操作方法\n */\ninterface ConfigActions {\n // 基础操作\n setConfig: (\n config: AppConfig,\n source?: \"http\" | \"websocket\" | \"initial\"\n ) => void;\n setLoading: (loading: Partial<ConfigLoadingState>) => void;\n setError: (error: Error | null) => void;\n\n // 异步操作\n getConfig: () => Promise<AppConfig>;\n updateConfig: (config: AppConfig) => Promise<void>;\n refreshConfig: () => Promise<AppConfig>;\n reloadConfig: () => Promise<AppConfig>;\n\n // 部分更新操作\n updateMcpEndpoint: (endpoint: string | string[]) => Promise<void>;\n updateMcpServers: (servers: Record<string, MCPServerConfig>) => Promise<void>;\n updateConnectionConfig: (connection: ConnectionConfig) => Promise<void>;\n updateModelScopeConfig: (modelscope: ModelScopeConfig) => Promise<void>;\n updateWebUIConfig: (webUI: WebUIConfig) => Promise<void>;\n\n // 工具方法\n reset: () => void;\n initialize: () => Promise<void>;\n\n // MCP 服务器状态管理\n setMcpServerStatuses: (statuses: MCPServerStatus[]) => void;\n setMcpServerStatusLoading: (loading: boolean) => void;\n refreshMcpServerStatuses: () => Promise<MCPServerStatus[]>;\n getMcpServerStatus: (name: string) => MCPServerStatus | undefined;\n}\n\n/**\n * 完整的配置 Store 接口\n */\nexport interface ConfigStore extends ConfigState, ConfigActions {}\n\n/**\n * 初始状态\n */\nconst initialState: ConfigState = {\n config: null,\n loading: {\n isLoading: false,\n isUpdating: false,\n isRefreshing: false,\n lastUpdated: null,\n lastError: null,\n },\n lastSource: null,\n mcpServerStatuses: [],\n mcpServerStatusLoading: false,\n mcpServerStatusLastUpdate: null,\n};\n\n/**\n * 创建配置 Store\n */\nexport const useConfigStore = create<ConfigStore>()(\n devtools(\n (set, get) => ({\n ...initialState,\n\n // ==================== 基础操作 ====================\n\n setConfig: (config: AppConfig, source = \"http\") => {\n console.log(`[ConfigStore] 设置配置数据,来源: ${source}`);\n set(\n (state) => ({\n config,\n lastSource: source,\n loading: {\n ...state.loading,\n lastUpdated: Date.now(),\n lastError: null,\n },\n }),\n false,\n \"setConfig\"\n );\n },\n\n setLoading: (loading: Partial<ConfigLoadingState>) => {\n set(\n (state) => ({\n loading: { ...state.loading, ...loading },\n }),\n false,\n \"setLoading\"\n );\n },\n\n setError: (error: Error | null) => {\n set(\n (state) => ({\n loading: { ...state.loading, lastError: error },\n }),\n false,\n \"setError\"\n );\n },\n\n // ==================== 异步操作 ====================\n\n getConfig: async (): Promise<AppConfig> => {\n const { config, loading } = get();\n\n // 如果已有配置且不超过5分钟,直接返回\n if (\n config &&\n loading.lastUpdated &&\n Date.now() - loading.lastUpdated < 5 * 60 * 1000\n ) {\n return config;\n }\n\n // 否则从服务器获取最新配置\n return get().refreshConfig();\n },\n\n updateConfig: async (newConfig: AppConfig): Promise<void> => {\n const { setLoading, setConfig, setError } = get();\n\n try {\n setLoading({ isUpdating: true, lastError: null });\n console.log(\"[ConfigStore] 开始更新配置\");\n\n // 通过 HTTP API 更新配置\n await apiClient.updateConfig(newConfig);\n\n // 更新本地状态\n setConfig(newConfig, \"http\");\n\n console.log(\"[ConfigStore] 配置更新成功\");\n } catch (error) {\n const err =\n error instanceof Error ? error : new Error(\"配置更新失败\");\n console.error(\"[ConfigStore] 配置更新失败:\", err);\n setError(err);\n throw err;\n } finally {\n setLoading({ isUpdating: false });\n }\n },\n\n refreshConfig: async (): Promise<AppConfig> => {\n const { setLoading, setConfig, setError } = get();\n\n try {\n setLoading({ isRefreshing: true, lastError: null });\n console.log(\"[ConfigStore] 开始刷新配置\");\n\n // 从服务器获取最新配置\n const config = await apiClient.getConfig();\n\n // 更新本地状态\n setConfig(config, \"http\");\n\n console.log(\"[ConfigStore] 配置刷新成功\");\n return config;\n } catch (error) {\n const err =\n error instanceof Error ? error : new Error(\"配置刷新失败\");\n console.error(\"[ConfigStore] 配置刷新失败:\", err);\n setError(err);\n throw err;\n } finally {\n setLoading({ isRefreshing: false });\n }\n },\n\n reloadConfig: async (): Promise<AppConfig> => {\n const { setLoading, setConfig, setError } = get();\n\n try {\n setLoading({ isRefreshing: true, lastError: null });\n console.log(\"[ConfigStore] 开始重新加载配置\");\n\n // 重新加载配置文件\n const config = await apiClient.reloadConfig();\n\n // 更新本地状态\n setConfig(config, \"http\");\n\n console.log(\"[ConfigStore] 配置重新加载成功\");\n return config;\n } catch (error) {\n const err =\n error instanceof Error ? error : new Error(\"配置重新加载失败\");\n console.error(\"[ConfigStore] 配置重新加载失败:\", err);\n setError(err);\n throw err;\n } finally {\n setLoading({ isRefreshing: false });\n }\n },\n\n // ==================== 部分更新操作 ====================\n\n updateMcpEndpoint: async (endpoint: string | string[]): Promise<void> => {\n const { config, updateConfig } = get();\n if (!config) {\n throw new Error(\"配置未加载,无法更新 MCP 端点\");\n }\n\n const newConfig = { ...config, mcpEndpoint: endpoint };\n await updateConfig(newConfig);\n },\n\n updateMcpServers: async (\n servers: Record<string, MCPServerConfig>\n ): Promise<void> => {\n const { config, updateConfig } = get();\n if (!config) {\n throw new Error(\"配置未加载,无法更新 MCP 服务\");\n }\n\n const newConfig = { ...config, mcpServers: servers };\n await updateConfig(newConfig);\n },\n\n updateConnectionConfig: async (\n connection: ConnectionConfig\n ): Promise<void> => {\n const { config, updateConfig } = get();\n if (!config) {\n throw new Error(\"配置未加载,无法更新连接配置\");\n }\n\n const newConfig = { ...config, connection };\n await updateConfig(newConfig);\n },\n\n updateModelScopeConfig: async (\n modelscope: ModelScopeConfig\n ): Promise<void> => {\n const { config, updateConfig } = get();\n if (!config) {\n throw new Error(\"配置未加载,无法更新 ModelScope 配置\");\n }\n\n const newConfig = { ...config, modelscope };\n await updateConfig(newConfig);\n },\n\n updateWebUIConfig: async (webUI: WebUIConfig): Promise<void> => {\n const { config, updateConfig } = get();\n if (!config) {\n throw new Error(\"配置未加载,无法更新 Web UI 配置\");\n }\n\n const newConfig = { ...config, webUI };\n await updateConfig(newConfig);\n },\n\n // ==================== 工具方法 ====================\n\n reset: () => {\n console.log(\"[ConfigStore] 重置状态\");\n set(initialState, false, \"reset\");\n },\n\n initialize: async (): Promise<void> => {\n const { setLoading, refreshConfig } = get();\n\n try {\n setLoading({ isLoading: true });\n console.log(\"[ConfigStore] 初始化配置 Store\");\n\n // 设置 WebSocket 事件监听\n webSocketManager.subscribe(\"data:configUpdate\", (config) => {\n console.log(\"[ConfigStore] 收到 WebSocket 配置更新\");\n get().setConfig(config, \"websocket\");\n });\n\n // 获取初始配置\n await refreshConfig();\n\n console.log(\"[ConfigStore] 配置 Store 初始化完成\");\n } catch (error) {\n console.error(\"[ConfigStore] 配置 Store 初始化失败:\", error);\n throw error;\n } finally {\n setLoading({ isLoading: false });\n }\n },\n\n // ==================== MCP 服务器状态管理 ====================\n\n setMcpServerStatuses: (statuses: MCPServerStatus[]) => {\n set(\n () => ({\n mcpServerStatuses: statuses,\n mcpServerStatusLastUpdate: Date.now(),\n }),\n false,\n \"setMcpServerStatuses\"\n );\n },\n\n setMcpServerStatusLoading: (loading: boolean) => {\n set(\n () => ({\n mcpServerStatusLoading: loading,\n }),\n false,\n \"setMcpServerStatusLoading\"\n );\n },\n\n refreshMcpServerStatuses: async (): Promise<MCPServerStatus[]> => {\n const state = get();\n const { setMcpServerStatuses, setMcpServerStatusLoading, setConfig } =\n state;\n\n try {\n setMcpServerStatusLoading(true);\n console.log(\"[ConfigStore] 开始刷新 MCP 服务器状态\");\n\n const response = await apiClient.listMCPServers();\n setMcpServerStatuses(response.servers);\n\n // 智能合并:从状态数据派生 mcpServers 配置,同步更新 config.mcpServers\n // 这样可以避免重复调用 /api/config 获取相同的配置数据\n if (state.config) {\n const mcpServers: Record<string, MCPServerConfig> = {};\n for (const server of response.servers) {\n mcpServers[server.name] = server.config;\n }\n // 同步更新 config.mcpServers,保持数据一致性\n setConfig({ ...state.config, mcpServers }, \"http\");\n }\n\n console.log(\n `[ConfigStore] MCP 服务器状态刷新成功,共 ${response.servers.length} 个服务器`\n );\n return response.servers;\n } catch (error) {\n const err =\n error instanceof Error\n ? error\n : new Error(\"MCP 服务器状态刷新失败\");\n console.error(\"[ConfigStore] MCP 服务器状态刷新失败:\", err);\n // 刷新失败时清空状态数据,避免显示过时信息\n setMcpServerStatuses([]);\n throw err;\n } finally {\n setMcpServerStatusLoading(false);\n }\n },\n\n getMcpServerStatus: (name: string): MCPServerStatus | undefined => {\n return get().mcpServerStatuses.find((s) => s.name === name);\n },\n }),\n {\n name: \"config-store\",\n }\n )\n);\n\n// ==================== 选择器 Hooks ====================\n\n/**\n * 获取完整配置\n */\nexport const useConfig = () => useConfigStore((state) => state.config);\n\n/**\n * 获取配置加载状态\n */\nexport const useConfigLoading = () => useConfigStore((state) => state.loading);\n\n/**\n * 获取配置是否正在加载\n */\nexport const useConfigIsLoading = () =>\n useConfigStore(\n (state) => state.loading.isLoading || state.loading.isRefreshing\n );\n\n/**\n * 获取配置是否正在更新\n */\nexport const useConfigIsUpdating = () =>\n useConfigStore((state) => state.loading.isUpdating);\n\n/**\n * 获取配置错误\n */\nexport const useConfigError = () =>\n useConfigStore((state) => state.loading.lastError);\n\n/**\n * 获取 MCP 端点\n */\nexport const useMcpEndpoint = () =>\n useConfigStore((state) => state.config?.mcpEndpoint);\n\n/**\n * 获取 MCP 服务工具配置\n */\nexport const useMcpServerConfig = () =>\n useConfigStore((state) => state.config?.mcpServerConfig);\n\n/**\n * 获取连接配置\n */\nexport const useConnectionConfig = () =>\n useConfigStore((state) => state.config?.connection);\n\n/**\n * 获取 ModelScope 配置\n */\nexport const useModelScopeConfig = () =>\n useConfigStore((state) => state.config?.modelscope);\n\n/**\n * 获取 Web UI 配置\n */\nexport const useWebUIConfig = () =>\n useConfigStore((state) => state.config?.webUI);\n\n/**\n * 获取配置来源\n */\nexport const useConfigSource = () =>\n useConfigStore((state) => state.lastSource);\n\n// ==================== 复合选择器 ====================\n\n/**\n * 获取配置数据和加载状态\n */\nexport const useConfigWithLoading = () =>\n useConfigStore(\n useShallow((state) => ({\n config: state.config,\n isLoading: state.loading.isLoading || state.loading.isRefreshing,\n isUpdating: state.loading.isUpdating,\n error: state.loading.lastError,\n }))\n );\n\n/**\n * 获取 MCP 相关配置\n */\nexport const useMcpConfig = () =>\n useConfigStore(\n useShallow((state) => ({\n endpoint: state.config?.mcpEndpoint,\n servers: state.config?.mcpServers,\n serverConfig: state.config?.mcpServerConfig,\n }))\n );\n\n/**\n * 获取系统配置\n */\nexport const useSystemConfig = () =>\n useConfigStore(\n useShallow((state) => ({\n connection: state.config?.connection,\n modelscope: state.config?.modelscope,\n webUI: state.config?.webUI,\n }))\n );\n\n/**\n * 获取 ASR(语音识别)配置\n */\nexport const useASRConfig = () => useConfigStore((state) => state.config?.asr);\n\n/**\n * 获取 TTS(语音合成)配置\n */\nexport const useTTSConfig = () => useConfigStore((state) => state.config?.tts);\n\n/**\n * 获取 LLM(大语言模型)配置\n */\nexport const useLLMConfig = () => useConfigStore((state) => state.config?.llm);\n\n/**\n * 获取语音交互配置(ASR/LLM/TTS)\n */\nexport const useVoiceInteractionConfig = () =>\n useConfigStore(\n useShallow((state) => ({\n asr: state.config?.asr,\n llm: state.config?.llm,\n tts: state.config?.tts,\n }))\n );\n\n// ==================== 操作方法 Hooks ====================\n\n/**\n * 获取配置操作方法\n */\nexport const useConfigActions = () =>\n useConfigStore(\n useShallow((state) => ({\n getConfig: state.getConfig,\n updateConfig: state.updateConfig,\n refreshConfig: state.refreshConfig,\n reloadConfig: state.reloadConfig,\n updateMcpEndpoint: state.updateMcpEndpoint,\n updateMcpServers: state.updateMcpServers,\n updateConnectionConfig: state.updateConnectionConfig,\n updateModelScopeConfig: state.updateModelScopeConfig,\n updateWebUIConfig: state.updateWebUIConfig,\n reset: state.reset,\n initialize: state.initialize,\n }))\n );\n\n/**\n * 获取配置更新方法\n */\nexport const useConfigUpdaters = () =>\n useConfigStore(\n useShallow((state) => ({\n updateConfig: state.updateConfig,\n updateMcpEndpoint: state.updateMcpEndpoint,\n updateMcpServers: state.updateMcpServers,\n updateConnectionConfig: state.updateConnectionConfig,\n updateModelScopeConfig: state.updateModelScopeConfig,\n updateWebUIConfig: state.updateWebUIConfig,\n }))\n );\n\n// ==================== MCP 服务器状态 Hooks ====================\n\n/**\n * 获取 MCP 服务器配置(向后兼容,仅配置)\n */\nexport const useMcpServers = () =>\n useConfigStore((state) => state.config?.mcpServers);\n\n/**\n * 获取 MCP 服务器状态(新增,包含连接状态)\n * @returns { servers, loading, refresh, lastUpdate }\n */\nexport const useMcpServersWithStatus = () =>\n useConfigStore(\n useShallow((state) => ({\n servers: state.mcpServerStatuses,\n loading: state.mcpServerStatusLoading,\n refresh: state.refreshMcpServerStatuses,\n lastUpdate: state.mcpServerStatusLastUpdate,\n }))\n );\n","import { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\n\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n} from \"@/components/ui/alert-dialog\";\nimport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from \"@/components/ui/dialog\";\nimport { Input } from \"@/components/ui/input\";\nimport { type EndpointStatusResponse, apiClient } from \"@/services/api\";\nimport { webSocketManager } from \"@/services/websocket\";\nimport { useConfig, useConfigActions, useMcpEndpoint } from \"@/stores/config\";\nimport {\n BadgeInfoIcon,\n CopyIcon,\n Loader2Icon,\n PlusIcon,\n SettingsIcon,\n TrashIcon,\n WifiIcon,\n WifiOffIcon,\n} from \"lucide-react\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport { toast } from \"sonner\";\n\n// 接入点状态接口\ninterface EndpointState {\n connected: boolean;\n isOperating: boolean;\n lastOperation: {\n type: \"connect\" | \"disconnect\" | \"reconnect\" | null;\n success: boolean;\n message: string;\n timestamp: number;\n };\n}\n\nconst sliceEndpoint = (endpoint: string) => {\n return `${endpoint.slice(0, 30)}...${endpoint.slice(-10)}`;\n};\n\n// 验证接入点格式\nconst validateEndpoint = (endpoint: string): string | null => {\n if (!endpoint.trim()) {\n return \"请输入接入点地址\";\n }\n\n // 检查是否是有效的 WebSocket URL\n if (!endpoint.startsWith(\"ws://\") && !endpoint.startsWith(\"wss://\")) {\n return \"接入点格式无效,请输入正确的WebSocket URL (ws:// 或 wss://)\";\n }\n\n // 检查是否是有效的 URL\n try {\n new URL(endpoint);\n } catch {\n return \"接入点格式无效,请输入正确的URL格式\";\n }\n\n return null; // 验证通过\n};\n\nexport function McpEndpointSettingButton() {\n const [open, setOpen] = useState(false);\n const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);\n const [endpointToDelete, setEndpointToDelete] = useState<string>(\"\");\n const [isDeleting, setIsDeleting] = useState(false);\n const [addDialogOpen, setAddDialogOpen] = useState(false);\n const [newEndpoint, setNewEndpoint] = useState(\"\");\n const [isAdding, setIsAdding] = useState(false);\n const [validationError, setValidationError] = useState(\"\");\n\n // 接入点状态管理\n const [endpointStates, setEndpointStates] = useState<\n Record<string, EndpointState>\n >({});\n\n const config = useConfig();\n const mcpEndpoint = useMcpEndpoint();\n const { refreshConfig } = useConfigActions();\n\n // 获取接入点状态\n const fetchEndpointStatus = useCallback(\n async (endpoint: string): Promise<EndpointStatusResponse> => {\n try {\n return await apiClient.getEndpointStatus(endpoint);\n } catch (error) {\n console.error(`获取接入点状态失败: ${endpoint}`, error);\n // 返回默认状态\n return {\n endpoint,\n connected: false,\n initialized: false,\n isReconnecting: false,\n reconnectAttempts: 0,\n reconnectDelay: 0,\n };\n }\n },\n []\n );\n\n // 更新接入点状态\n const updateEndpointState = useCallback(\n (endpoint: string, updates: Partial<EndpointState>) => {\n setEndpointStates((prev) => ({\n ...prev,\n [endpoint]: {\n ...prev[endpoint],\n ...updates,\n },\n }));\n },\n []\n );\n\n // 初始化接入点状态\n const initializeEndpointStates = useCallback(\n async (endpoints: string[]) => {\n const states: Record<string, EndpointState> = {};\n\n for (const endpoint of endpoints) {\n try {\n const status = await fetchEndpointStatus(endpoint);\n states[endpoint] = {\n connected: status.connected,\n isOperating: false,\n lastOperation: {\n type: null,\n success: false,\n message: \"\",\n timestamp: 0,\n },\n };\n } catch (error) {\n states[endpoint] = {\n connected: false,\n isOperating: false,\n lastOperation: {\n type: null,\n success: false,\n message: \"\",\n timestamp: 0,\n },\n };\n }\n }\n\n setEndpointStates(states);\n },\n [fetchEndpointStatus]\n );\n\n // 连接接入点\n const handleConnect = async (endpoint: string) => {\n updateEndpointState(endpoint, { isOperating: true });\n\n try {\n await apiClient.connectEndpoint(endpoint);\n updateEndpointState(endpoint, {\n connected: true,\n isOperating: false,\n lastOperation: {\n type: \"connect\",\n success: true,\n message: \"连接成功\",\n timestamp: Date.now(),\n },\n });\n toast.success(\"接入点连接成功\");\n } catch (error) {\n updateEndpointState(endpoint, {\n isOperating: false,\n lastOperation: {\n type: \"connect\",\n success: false,\n message: error instanceof Error ? error.message : \"连接失败\",\n timestamp: Date.now(),\n },\n });\n toast.error(error instanceof Error ? error.message : \"接入点连接失败\");\n }\n };\n\n // 断开接入点\n const handleDisconnect = async (endpoint: string) => {\n updateEndpointState(endpoint, { isOperating: true });\n\n try {\n await apiClient.disconnectEndpoint(endpoint);\n updateEndpointState(endpoint, {\n connected: false,\n isOperating: false,\n lastOperation: {\n type: \"disconnect\",\n success: true,\n message: \"断开成功\",\n timestamp: Date.now(),\n },\n });\n toast.success(\"接入点断开成功\");\n } catch (error) {\n updateEndpointState(endpoint, {\n isOperating: false,\n lastOperation: {\n type: \"disconnect\",\n success: false,\n message: error instanceof Error ? error.message : \"断开失败\",\n timestamp: Date.now(),\n },\n });\n toast.error(error instanceof Error ? error.message : \"接入点断开失败\");\n }\n };\n\n // 复制接入点地址到剪贴板\n const handleCopy = async (endpoint: string) => {\n try {\n if (navigator.clipboard?.writeText) {\n await navigator.clipboard.writeText(endpoint);\n toast.success(\"接入点地址已复制到剪贴板\");\n } else {\n // 降级方案:使用传统的复制方法\n const textArea = document.createElement(\"textarea\");\n textArea.value = endpoint;\n textArea.style.position = \"fixed\";\n textArea.style.opacity = \"0\";\n document.body.appendChild(textArea);\n textArea.select();\n const successful = document.execCommand(\"copy\");\n document.body.removeChild(textArea);\n if (successful) {\n toast.success(\"接入点地址已复制到剪贴板\");\n } else {\n throw new Error(\"复制命令执行失败\");\n }\n }\n } catch (error) {\n console.error(\"复制失败:\", error);\n toast.error(\"复制失败,请手动复制\");\n }\n };\n\n // 删除接入点\n const handleDeleteEndpoint = async () => {\n setIsDeleting(true);\n try {\n // 调用后端 API 删除接入点\n await apiClient.removeEndpoint(endpointToDelete);\n\n // 刷新配置数据以更新 mcpEndpoints 列表\n await refreshConfig();\n\n // 从本地状态中移除该接入点\n setEndpointStates((prev) => {\n const newStates = { ...prev };\n delete newStates[endpointToDelete];\n return newStates;\n });\n\n toast.success(\"接入点已删除\");\n setDeleteConfirmOpen(false);\n setEndpointToDelete(\"\");\n } catch (error) {\n console.error(\"删除接入点失败:\", error);\n toast.error(error instanceof Error ? error.message : \"删除接入点失败\");\n } finally {\n setIsDeleting(false);\n }\n };\n\n // 添加新接入点\n const handleAddEndpoint = async () => {\n const error = validateEndpoint(newEndpoint);\n if (error) {\n setValidationError(error);\n return;\n }\n\n // 检查是否与现有接入点重复\n const currentEndpoints = Array.isArray(mcpEndpoint)\n ? mcpEndpoint\n : [mcpEndpoint];\n if (currentEndpoints.includes(newEndpoint)) {\n setValidationError(\"该接入点已存在\");\n return;\n }\n\n if (!config) {\n toast.error(\"配置数据未加载,请稍后重试\");\n return;\n }\n\n setIsAdding(true);\n try {\n // 调用后端 API 添加接入点\n const endpointStatus = await apiClient.addEndpoint(newEndpoint);\n\n // 刷新配置数据以更新 mcpEndpoints 列表\n await refreshConfig();\n\n // 初始化新接入点的状态\n setEndpointStates((prev) => ({\n ...prev,\n [newEndpoint]: {\n connected: endpointStatus.connected,\n isOperating: false,\n lastOperation: {\n type: null,\n success: false,\n message: \"\",\n timestamp: 0,\n },\n },\n }));\n\n toast.success(\"接入点添加成功\");\n setAddDialogOpen(false);\n setNewEndpoint(\"\");\n setValidationError(\"\");\n } catch (error) {\n console.error(\"添加接入点失败:\", error);\n toast.error(error instanceof Error ? error.message : \"添加接入点失败\");\n } finally {\n setIsAdding(false);\n }\n };\n\n // 打开添加接入点对话框\n const openAddDialog = () => {\n setNewEndpoint(\"\");\n setValidationError(\"\");\n setAddDialogOpen(true);\n };\n\n // 处理输入变化\n const handleInputChange = (value: string) => {\n setNewEndpoint(value);\n if (validationError) {\n setValidationError(\"\");\n }\n };\n\n // 打开删除确认对话框\n const openDeleteConfirm = (endpoint: string) => {\n setEndpointToDelete(endpoint);\n setDeleteConfirmOpen(true);\n };\n\n const mcpEndpoints = useMemo(() => {\n let list: string[] = [];\n if (Array.isArray(mcpEndpoint)) list = mcpEndpoint;\n if (typeof mcpEndpoint === \"string\" && mcpEndpoint.length) {\n list.push(mcpEndpoint);\n }\n return list;\n }, [mcpEndpoint]);\n\n // 当对话框打开时,初始化接入点状态\n useEffect(() => {\n if (open && mcpEndpoints.length > 0) {\n initializeEndpointStates(mcpEndpoints);\n }\n }, [open, mcpEndpoints, initializeEndpointStates]);\n\n // 实时状态同步 - 处理端点状态变更事件\n useEffect(() => {\n if (!open || mcpEndpoints.length === 0) return;\n\n // 为每个端点订阅状态变更事件\n const unsubscribers = mcpEndpoints.map((endpoint) => {\n const unsubscribe = webSocketManager.subscribe(\n \"data:endpointStatusChanged\",\n (event: any) => {\n // 只处理当前端点的事件\n if (event.endpoint === endpoint) {\n console.log(\n `[McpEndpointSettingButton] 接收到端点 ${endpoint} 状态变更:`,\n event\n );\n\n // 更新端点状态\n updateEndpointState(endpoint, {\n connected: event.connected,\n isOperating: false, // 接收到事件说明操作已完成\n lastOperation: {\n type: event.operation,\n success: event.success,\n message:\n event.message || (event.connected ? \"连接成功\" : \"断开成功\"),\n timestamp: event.timestamp,\n },\n });\n\n // 显示通知\n if (event.success) {\n toast.success(\n `端点 ${event.operation === \"connect\" ? \"连接\" : event.operation === \"disconnect\" ? \"断开\" : \"重连\"}成功`\n );\n } else {\n toast.error(\n `端点 ${event.operation === \"connect\" ? \"连接\" : event.operation === \"disconnect\" ? \"断开\" : \"重连\"}失败: ${event.message || \"未知错误\"}`\n );\n }\n }\n }\n );\n\n return unsubscribe;\n });\n\n // 清理函数\n return () => {\n for (const unsubscribe of unsubscribers) {\n unsubscribe();\n }\n };\n }, [open, mcpEndpoints, updateEndpointState]);\n\n return (\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogTrigger asChild>\n <Button variant=\"secondary\" size=\"icon\" className=\"size-8\">\n <SettingsIcon className=\"size-4\" />\n </Button>\n </DialogTrigger>\n <DialogContent className=\"min-w-[600px] max-w-[800px] max-h-[80vh] overflow-y-auto\">\n <DialogHeader className=\"mb-4\">\n <DialogTitle>配置小智服务端接入点</DialogTitle>\n <DialogDescription>\n 点击保存后,需要重启服务才会生效。\n </DialogDescription>\n </DialogHeader>\n <div className=\"flex flex-col gap-2\">\n {mcpEndpoints.map((item) => {\n const endpointState = endpointStates[item];\n const isConnected = endpointState?.connected || false;\n const isOperating = endpointState?.isOperating || false;\n\n return (\n <div\n key={item}\n className=\"flex flex-col sm:flex-row items-start sm:items-center justify-between p-4 bg-slate-50 rounded-md font-mono gap-3 transition-all duration-200 hover:bg-slate-100\"\n >\n <div className=\"flex flex-col sm:flex-row items-start sm:items-center gap-2 sm:gap-3 flex-1 min-w-0\">\n <span className=\"flex-1 text-ellipsis overflow-hidden whitespace-nowrap text-sm sm:text-base\">\n {sliceEndpoint(item)}\n </span>\n {/* 连接状态显示 */}\n <Badge\n className={`flex items-center gap-1 transition-all duration-200 text-xs sm:text-sm ${\n isConnected\n ? \"bg-green-100 text-green-800 border-green-200 hover:text-green-800 hover:border-green-200 hover:bg-green-100\"\n : \"bg-gray-100 text-gray-600 border-gray-200 hover:bg-gray-100 hover:text-gray-600 hover:border-gray-200\"\n }`}\n >\n {isOperating ? (\n <Loader2Icon className=\"size-3 animate-spin\" />\n ) : isConnected ? (\n <WifiIcon className=\"size-3\" />\n ) : (\n <WifiOffIcon className=\"size-3\" />\n )}\n {isOperating ? \"操作中\" : isConnected ? \"已连接\" : \"未连接\"}\n </Badge>\n </div>\n <div className=\"flex items-center gap-1 sm:gap-2 flex-wrap justify-end\">\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => handleCopy(item)}\n title=\"复制完整地址\"\n className=\"transition-all duration-200 hover:scale-105\"\n >\n <CopyIcon className=\"size-4\" />\n </Button>\n {/* 连接/断开按钮 */}\n {isConnected ? (\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => handleDisconnect(item)}\n title=\"断开连接\"\n disabled={isOperating}\n className=\"text-red-600 hover:text-red-700 hover:bg-red-50 transition-all duration-200 hover:scale-105 disabled:scale-100 disabled:opacity-50\"\n >\n {isOperating ? (\n <Loader2Icon className=\"size-4 animate-spin\" />\n ) : (\n <WifiOffIcon className=\"size-4\" />\n )}\n </Button>\n ) : (\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => handleConnect(item)}\n title=\"连接\"\n disabled={isOperating}\n className=\"text-green-600 hover:text-green-700 hover:bg-green-50 transition-all duration-200 hover:scale-105 disabled:scale-100 disabled:opacity-50\"\n >\n {isOperating ? (\n <Loader2Icon className=\"size-4 animate-spin\" />\n ) : (\n <WifiIcon className=\"size-4\" />\n )}\n </Button>\n )}\n <Button\n variant=\"outline\"\n size=\"icon\"\n onClick={() => openDeleteConfirm(item)}\n title=\"删除此接入点\"\n className=\"transition-all duration-200 hover:scale-105 hover:text-red-600\"\n >\n <TrashIcon className=\"size-4 text-red-500\" />\n </Button>\n </div>\n </div>\n );\n })}\n {mcpEndpoints.length === 0 && (\n <div className=\"flex flex-col items-center flex-1 text-sm text-muted-foreground text-center justify-center gap-2\">\n <BadgeInfoIcon />\n <span>暂无接入点,请添加</span>\n </div>\n )}\n <div className=\"flex flex-col sm:flex-row items-center gap-2 mt-4\">\n <Button\n className=\"flex-1 flex items-center gap-2\"\n onClick={openAddDialog}\n >\n <PlusIcon className=\"size-4\" />\n <span className=\"text-sm sm:text-base\">添加小智服务端接入点</span>\n </Button>\n <Button\n variant=\"outline\"\n className=\"flex-1 flex items-center gap-2\"\n onClick={() =>\n window.open(\"https://xiaozhi.me/console/agents\", \"_blank\")\n }\n >\n <span className=\"text-sm sm:text-base\">打开小智服务端</span>\n </Button>\n </div>\n </div>\n </DialogContent>\n\n {/* 删除确认对话框 */}\n <AlertDialog open={deleteConfirmOpen} onOpenChange={setDeleteConfirmOpen}>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>确认删除接入点</AlertDialogTitle>\n <AlertDialogDescription>\n 确定要删除接入点 \"{sliceEndpoint(endpointToDelete)}\"\n 吗?此操作无法撤销。\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel disabled={isDeleting}>取消</AlertDialogCancel>\n <AlertDialogAction\n onClick={handleDeleteEndpoint}\n disabled={isDeleting}\n className=\"bg-destructive text-destructive-foreground hover:bg-destructive/90\"\n >\n {isDeleting ? \"删除中...\" : \"确定删除\"}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n\n {/* 添加接入点对话框 */}\n <Dialog open={addDialogOpen} onOpenChange={setAddDialogOpen}>\n <DialogContent className=\"sm:max-w-[500px]\">\n <DialogHeader>\n <DialogTitle>添加新的接入点</DialogTitle>\n <DialogDescription>请输入小智服务端接入点地址</DialogDescription>\n </DialogHeader>\n <div className=\"grid gap-4 py-4\">\n <div className=\"space-y-2\">\n <Input\n placeholder=\"请输入接入点地址,例如:wss://api.xiaozhi.me/mcp/?token=... 或 ws(s)://<hostname>:<port>\"\n value={newEndpoint}\n onChange={(e) => handleInputChange(e.target.value)}\n disabled={isAdding}\n className=\"font-mono text-sm\"\n />\n {validationError && (\n <p className=\"text-sm text-red-500\">{validationError}</p>\n )}\n </div>\n </div>\n <DialogFooter>\n <DialogClose asChild>\n <Button variant=\"outline\" disabled={isAdding}>\n 取消\n </Button>\n </DialogClose>\n <Button\n onClick={handleAddEndpoint}\n disabled={isAdding || !newEndpoint.trim()}\n >\n {isAdding ? \"添加中...\" : \"确定\"}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n </Dialog>\n );\n}\n","import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst Card = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n \"rounded-lg border bg-card text-card-foreground shadow-sm\",\n className\n )}\n {...props}\n />\n));\nCard.displayName = \"Card\";\n\nconst CardHeader = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\"flex flex-col space-y-1.5 p-6\", className)}\n {...props}\n />\n));\nCardHeader.displayName = \"CardHeader\";\n\nconst CardTitle = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n \"text-2xl font-semibold leading-none tracking-tight\",\n className\n )}\n {...props}\n />\n));\nCardTitle.displayName = \"CardTitle\";\n\nconst CardDescription = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n));\nCardDescription.displayName = \"CardDescription\";\n\nconst CardContent = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div ref={ref} className={cn(\"p-6 pt-0\", className)} {...props} />\n));\nCardContent.displayName = \"CardContent\";\n\nconst CardFooter = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\"flex items-center p-6 pt-0\", className)}\n {...props}\n />\n));\nCardFooter.displayName = \"CardFooter\";\n\nexport {\n Card,\n CardHeader,\n CardFooter,\n CardTitle,\n CardDescription,\n CardContent,\n};\n","/**\n * 小智接入点状态卡片组件\n *\n * 显示当前配置的小智接入点数量,并提供设置按钮。\n */\n\nimport { McpEndpointSettingButton } from \"@/components/mcp-endpoint-setting-button\";\nimport {\n Card,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from \"@/components/ui/card\";\nimport { useMcpEndpoint } from \"@/stores/config\";\nimport { MiniCircularProgress } from \"./mini-circular-progress\";\n\n/**\n * 小智接入点状态卡片组件\n *\n * 显示当前配置的小智接入点数量,右上角显示状态指示器。\n */\nexport function EndpointStatusCard() {\n const mcpEndpoint = useMcpEndpoint();\n const endpointCount = Array.isArray(mcpEndpoint)\n ? mcpEndpoint.length\n : mcpEndpoint\n ? 1\n : 0;\n\n return (\n <Card className=\"@container/card\">\n <CardHeader className=\"relative\">\n <CardDescription>小智接入点</CardDescription>\n <CardTitle className=\"@[250px]/card:text-3xl text-2xl font-semibold tabular-nums\">\n {endpointCount}\n </CardTitle>\n <div className=\"absolute right-4 top-4 flex flex-col items-center\">\n <MiniCircularProgress\n showValue={false}\n value={endpointCount}\n maxValue={Math.max(endpointCount, 1)}\n activeColor=\"#16a34a\"\n inactiveColor=\"#f87171\"\n size={30}\n symbol=\"\"\n />\n </div>\n </CardHeader>\n <CardFooter className=\"flex-col items-end gap-1 text-sm\">\n <McpEndpointSettingButton />\n </CardFooter>\n </Card>\n );\n}\n","import * as LabelPrimitive from \"@radix-ui/react-label\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst labelVariants = cva(\n \"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\"\n);\n\nconst Label = React.forwardRef<\n React.ElementRef<typeof LabelPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &\n VariantProps<typeof labelVariants>\n>(({ className, ...props }, ref) => (\n <LabelPrimitive.Root\n ref={ref}\n className={cn(labelVariants(), className)}\n {...props}\n />\n));\nLabel.displayName = LabelPrimitive.Root.displayName;\n\nexport { Label };\n","import type * as LabelPrimitive from \"@radix-ui/react-label\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport * as React from \"react\";\nimport {\n Controller,\n type ControllerProps,\n type FieldPath,\n type FieldValues,\n FormProvider,\n useFormContext,\n} from \"react-hook-form\";\n\nimport { Label } from \"@/components/ui/label\";\nimport { cn } from \"@/lib/utils\";\n\nconst Form = FormProvider;\n\ntype FormFieldContextValue<\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n> = {\n name: TName;\n};\n\nconst FormFieldContext = React.createContext<FormFieldContextValue>(\n {} as FormFieldContextValue\n);\n\nconst FormField = <\n TFieldValues extends FieldValues = FieldValues,\n TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,\n>({\n ...props\n}: ControllerProps<TFieldValues, TName>) => {\n return (\n <FormFieldContext.Provider value={{ name: props.name }}>\n <Controller {...props} />\n </FormFieldContext.Provider>\n );\n};\n\nconst useFormField = () => {\n const fieldContext = React.useContext(FormFieldContext);\n const itemContext = React.useContext(FormItemContext);\n const { getFieldState, formState } = useFormContext();\n\n const fieldState = getFieldState(fieldContext.name, formState);\n\n if (!fieldContext) {\n throw new Error(\"useFormField should be used within <FormField>\");\n }\n\n const { id } = itemContext;\n\n return {\n id,\n name: fieldContext.name,\n formItemId: `${id}-form-item`,\n formDescriptionId: `${id}-form-item-description`,\n formMessageId: `${id}-form-item-message`,\n ...fieldState,\n };\n};\n\ntype FormItemContextValue = {\n id: string;\n};\n\nconst FormItemContext = React.createContext<FormItemContextValue>(\n {} as FormItemContextValue\n);\n\nconst FormItem = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement>\n>(({ className, ...props }, ref) => {\n const id = React.useId();\n\n return (\n <FormItemContext.Provider value={{ id }}>\n <div ref={ref} className={cn(\"space-y-2\", className)} {...props} />\n </FormItemContext.Provider>\n );\n});\nFormItem.displayName = \"FormItem\";\n\nconst FormLabel = React.forwardRef<\n React.ElementRef<typeof LabelPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>\n>(({ className, ...props }, ref) => {\n const { error, formItemId } = useFormField();\n\n return (\n <Label\n ref={ref}\n className={cn(error && \"text-destructive\", className)}\n htmlFor={formItemId}\n {...props}\n />\n );\n});\nFormLabel.displayName = \"FormLabel\";\n\nconst FormControl = React.forwardRef<\n React.ElementRef<typeof Slot>,\n React.ComponentPropsWithoutRef<typeof Slot>\n>(({ ...props }, ref) => {\n const { error, formItemId, formDescriptionId, formMessageId } =\n useFormField();\n\n return (\n <Slot\n ref={ref}\n id={formItemId}\n aria-describedby={\n !error\n ? `${formDescriptionId}`\n : `${formDescriptionId} ${formMessageId}`\n }\n aria-invalid={!!error}\n {...props}\n />\n );\n});\nFormControl.displayName = \"FormControl\";\n\nconst FormDescription = React.forwardRef<\n HTMLParagraphElement,\n React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, ...props }, ref) => {\n const { formDescriptionId } = useFormField();\n\n return (\n <p\n ref={ref}\n id={formDescriptionId}\n className={cn(\"text-sm text-muted-foreground\", className)}\n {...props}\n />\n );\n});\nFormDescription.displayName = \"FormDescription\";\n\nconst FormMessage = React.forwardRef<\n HTMLParagraphElement,\n React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, children, ...props }, ref) => {\n const { error, formMessageId } = useFormField();\n const body = error ? String(error?.message ?? \"\") : children;\n\n if (!body) {\n return null;\n }\n\n return (\n <p\n ref={ref}\n id={formMessageId}\n className={cn(\"text-sm font-medium text-destructive\", className)}\n {...props}\n >\n {body}\n </p>\n );\n});\nFormMessage.displayName = \"FormMessage\";\n\nexport {\n useFormField,\n Form,\n FormItem,\n FormLabel,\n FormControl,\n FormDescription,\n FormMessage,\n FormField,\n};\n","/**\n * 扣子 API 前端包装器\n * 负责与后端扣子 API 的通信\n */\n\nimport type {\n CozeWorkflowsParams,\n CozeWorkflowsResult,\n CozeWorkspace,\n} from \"@xiaozhi-client/shared-types\";\n\n/**\n * API 响应格式\n */\ninterface ApiResponse<T = unknown> {\n success: boolean;\n data?: T;\n message?: string;\n}\n\ninterface ApiErrorResponse {\n error: {\n code: string;\n message: string;\n details?: Record<string, unknown>;\n };\n}\n\n/**\n * 缓存统计信息\n */\ninterface CacheStats {\n size: number;\n keys: string[];\n}\n\n/**\n * 扣子 API 客户端类\n */\nexport class CozeApiClient {\n private baseUrl: string;\n\n constructor(baseUrl?: string) {\n // 从当前页面 URL 推断 API 基础 URL\n if (baseUrl) {\n this.baseUrl = baseUrl;\n } else {\n const protocol = window.location.protocol;\n const hostname = window.location.hostname;\n const port = window.location.port;\n this.baseUrl = `${protocol}//${hostname}${port ? `:${port}` : \"\"}`;\n }\n }\n\n /**\n * 通用请求方法\n */\n private async request<T>(\n endpoint: string,\n options: RequestInit = {}\n ): Promise<T> {\n const url = `${this.baseUrl}${endpoint}`;\n\n const defaultOptions: RequestInit = {\n headers: {\n \"Content-Type\": \"application/json\",\n ...options.headers,\n },\n };\n\n const response = await fetch(url, { ...defaultOptions, ...options });\n\n if (!response.ok) {\n let errorMessage = `HTTP ${response.status}: ${response.statusText}`;\n\n try {\n const errorData: ApiErrorResponse = await response.json();\n errorMessage = errorData.error?.message || errorMessage;\n } catch {\n // 如果无法解析错误响应,使用默认错误消息\n }\n\n throw new Error(errorMessage);\n }\n\n return response.json();\n }\n\n /**\n * 获取工作空间列表\n */\n async fetchWorkspaces(): Promise<{ workspaces: CozeWorkspace[] }> {\n try {\n const response: ApiResponse<{ workspaces: CozeWorkspace[] }> =\n await this.request(\"/api/coze/workspaces\");\n\n if (!response.success || !response.data) {\n throw new Error(response.message || \"获取工作空间列表失败\");\n }\n\n return response.data;\n } catch (error) {\n console.error(\"获取工作空间列表失败:\", error);\n throw error;\n }\n }\n\n /**\n * 获取工作流列表\n */\n async fetchWorkflows(\n params: CozeWorkflowsParams\n ): Promise<CozeWorkflowsResult> {\n try {\n const searchParams = new URLSearchParams();\n searchParams.append(\"workspace_id\", params.workspace_id);\n\n if (params.page_num !== undefined) {\n searchParams.append(\"page_num\", params.page_num.toString());\n }\n\n if (params.page_size !== undefined) {\n searchParams.append(\"page_size\", params.page_size.toString());\n }\n\n const response: ApiResponse<CozeWorkflowsResult> = await this.request(\n `/api/coze/workflows?${searchParams.toString()}`\n );\n\n if (!response.success || !response.data) {\n throw new Error(response.message || \"获取工作流列表失败\");\n }\n\n return response.data;\n } catch (error) {\n console.error(\"获取工作流列表失败:\", error);\n throw error;\n }\n }\n\n /**\n * 清除缓存\n */\n async clearCache(): Promise<void> {\n try {\n const response: ApiResponse = await this.request(\n \"/api/coze/cache/clear\",\n {\n method: \"POST\",\n }\n );\n\n if (!response.success) {\n throw new Error(response.message || \"清除缓存失败\");\n }\n } catch (error) {\n console.error(\"清除缓存失败:\", error);\n throw error;\n }\n }\n\n /**\n * 获取缓存统计信息\n */\n async getCacheStats(): Promise<CacheStats> {\n try {\n const response: ApiResponse<CacheStats> = await this.request(\n \"/api/coze/cache/stats\"\n );\n\n if (!response.success || !response.data) {\n throw new Error(response.message || \"获取缓存统计失败\");\n }\n\n return response.data;\n } catch (error) {\n console.error(\"获取缓存统计失败:\", error);\n throw error;\n }\n }\n}\n\n// 创建默认的扣子 API 客户端实例\nexport const cozeApiClient = new CozeApiClient();\n\n// 导出类型\nexport type { ApiResponse, ApiErrorResponse, CacheStats };\n","/**\n * 统一的网络服务管理器\n * 整合 HTTP API 客户端和 WebSocket 管理器\n */\n\nimport type { AppConfig, ClientStatus } from \"@xiaozhi-client/shared-types\";\nimport { type ApiClient, apiClient } from \"./api\";\nimport {\n type ConnectionState,\n type WebSocketManager,\n type WebSocketMessage,\n webSocketManager,\n} from \"./websocket\";\n\n/**\n * 网络服务管理器类\n */\nexport class NetworkService {\n private apiClient: ApiClient;\n private webSocketManager: WebSocketManager;\n private initialized = false;\n\n constructor() {\n this.apiClient = apiClient;\n this.webSocketManager = webSocketManager;\n }\n\n /**\n * 初始化网络服务\n */\n async initialize(): Promise<void> {\n if (this.initialized) {\n return;\n }\n\n console.log(\"[NetworkService] 初始化网络服务\");\n\n // 启动 WebSocket 连接\n this.webSocketManager.connect();\n\n this.initialized = true;\n console.log(\"[NetworkService] 网络服务初始化完成\");\n }\n\n /**\n * 销毁网络服务\n */\n destroy(): void {\n console.log(\"[NetworkService] 销毁网络服务\");\n this.webSocketManager.disconnect();\n this.initialized = false;\n }\n\n // ==================== HTTP API 方法 ====================\n\n /**\n * 获取配置 (HTTP)\n */\n async getConfig(): Promise<AppConfig> {\n return this.apiClient.getConfig();\n }\n\n /**\n * 更新配置 (HTTP)\n */\n async updateConfig(config: AppConfig): Promise<void> {\n return this.apiClient.updateConfig(config);\n }\n\n /**\n * 获取状态 (HTTP)\n */\n async getStatus(): Promise<any> {\n return this.apiClient.getStatus();\n }\n\n /**\n * 获取客户端状态 (HTTP)\n */\n async getClientStatus(): Promise<ClientStatus> {\n return this.apiClient.getClientStatus();\n }\n\n /**\n * 重启服务 (HTTP)\n */\n async restartService(): Promise<void> {\n return this.apiClient.restartService();\n }\n\n /**\n * 停止服务 (HTTP)\n */\n async stopService(): Promise<void> {\n return this.apiClient.stopService();\n }\n\n /**\n * 启动服务 (HTTP)\n */\n async startService(): Promise<void> {\n return this.apiClient.startService();\n }\n\n /**\n * 获取服务状态 (HTTP)\n */\n async getServiceStatus(): Promise<any> {\n return this.apiClient.getServiceStatus();\n }\n\n /**\n * 获取服务健康状态 (HTTP)\n */\n async getServiceHealth(): Promise<any> {\n return this.apiClient.getServiceHealth();\n }\n\n /**\n * 获取 MCP 端点 (HTTP)\n */\n async getMcpEndpoint(): Promise<string> {\n return this.apiClient.getMcpEndpoint();\n }\n\n /**\n * 获取 MCP 端点列表 (HTTP)\n */\n async getMcpEndpoints(): Promise<string[]> {\n return this.apiClient.getMcpEndpoints();\n }\n\n /**\n * 获取 MCP 服务配置 (HTTP)\n */\n async getMcpServers(): Promise<Record<string, any>> {\n return this.apiClient.getMcpServers();\n }\n\n /**\n * 获取连接配置 (HTTP)\n */\n async getConnectionConfig(): Promise<any> {\n return this.apiClient.getConnectionConfig();\n }\n\n /**\n * 重新加载配置 (HTTP)\n */\n async reloadConfig(): Promise<AppConfig> {\n return this.apiClient.reloadConfig();\n }\n\n /**\n * 获取配置文件路径 (HTTP)\n */\n async getConfigPath(): Promise<string> {\n return this.apiClient.getConfigPath();\n }\n\n /**\n * 检查配置是否存在 (HTTP)\n */\n async checkConfigExists(): Promise<boolean> {\n return this.apiClient.checkConfigExists();\n }\n\n /**\n * 获取重启状态 (HTTP)\n */\n async getRestartStatus(): Promise<any> {\n return this.apiClient.getRestartStatus();\n }\n\n /**\n * 检查客户端是否连接 (HTTP)\n */\n async checkClientConnected(): Promise<boolean> {\n return this.apiClient.checkClientConnected();\n }\n\n /**\n * 获取最后心跳时间 (HTTP)\n */\n async getLastHeartbeat(): Promise<number | null> {\n return this.apiClient.getLastHeartbeat();\n }\n\n /**\n * 获取活跃的 MCP 服务器列表 (HTTP)\n */\n async getActiveMCPServers(): Promise<string[]> {\n return this.apiClient.getActiveMCPServers();\n }\n\n /**\n * 更新客户端状态 (HTTP)\n */\n async updateClientStatus(status: Partial<ClientStatus>): Promise<void> {\n return this.apiClient.updateClientStatus(status);\n }\n\n /**\n * 设置活跃的 MCP 服务器列表 (HTTP)\n */\n async setActiveMCPServers(servers: string[]): Promise<void> {\n return this.apiClient.setActiveMCPServers(servers);\n }\n\n /**\n * 重置状态 (HTTP)\n */\n async resetStatus(): Promise<void> {\n return this.apiClient.resetStatus();\n }\n\n // ==================== WebSocket 方法 ====================\n\n /**\n * 获取 WebSocket 连接状态\n */\n getWebSocketState(): ConnectionState {\n return this.webSocketManager.getState();\n }\n\n /**\n * 检查 WebSocket 是否已连接\n */\n isWebSocketConnected(): boolean {\n return this.webSocketManager.isConnected();\n }\n\n /**\n * 设置 WebSocket URL\n */\n setWebSocketUrl(url: string): void {\n this.webSocketManager.setUrl(url);\n }\n\n /**\n * 监听 WebSocket 事件\n * @returns 取消订阅的函数\n */\n onWebSocketEvent<K extends keyof import(\"./websocket\").EventBusEvents>(\n event: K,\n listener: import(\"./websocket\").EventListener<\n import(\"./websocket\").EventBusEvents[K]\n >\n ): () => void {\n return this.webSocketManager.subscribe(event, listener);\n }\n\n /**\n * 重新连接 WebSocket\n */\n reconnectWebSocket(): void {\n this.webSocketManager.disconnect();\n setTimeout(() => {\n this.webSocketManager.connect();\n }, 1000);\n }\n\n /**\n * 通过 WebSocket 发送消息\n */\n send(message: WebSocketMessage): boolean {\n return this.webSocketManager.send(message);\n }\n\n // ==================== 便捷方法 ====================\n\n /**\n * 获取完整的应用状态 (HTTP + WebSocket)\n */\n async getFullAppState(): Promise<{\n config: AppConfig;\n status: any;\n webSocketConnected: boolean;\n }> {\n const [config, status] = await Promise.all([\n this.getConfig(),\n this.getStatus(),\n ]);\n\n return {\n config,\n status,\n webSocketConnected: this.isWebSocketConnected(),\n };\n }\n\n /**\n * 更新配置并等待 WebSocket 通知 (混合模式)\n */\n async updateConfigWithNotification(\n config: AppConfig,\n timeout = 5000\n ): Promise<void> {\n // 设置 WebSocket 监听器等待配置更新通知\n return new Promise((resolve, reject) => {\n const unsubscribe = this.webSocketManager.subscribe(\n \"data:configUpdate\",\n () => {\n clearTimeout(timeoutId);\n unsubscribe();\n resolve();\n }\n );\n\n const timeoutId = setTimeout(() => {\n unsubscribe();\n reject(new Error(\"等待配置更新通知超时\"));\n }, timeout);\n\n // 通过 HTTP API 更新配置\n this.updateConfig(config).catch((error) => {\n clearTimeout(timeoutId);\n unsubscribe?.();\n reject(error);\n });\n });\n }\n\n /**\n * 重启服务并等待状态通知 (混合模式)\n */\n async restartServiceWithNotification(timeout = 30000): Promise<void> {\n return new Promise((resolve, reject) => {\n const unsubscribe = this.webSocketManager.subscribe(\n \"data:restartStatus\",\n (status) => {\n if (status.status === \"completed\") {\n clearTimeout(timeoutId);\n unsubscribe();\n resolve();\n } else if (status.status === \"failed\") {\n clearTimeout(timeoutId);\n unsubscribe();\n reject(new Error(status.error || \"服务重启失败\"));\n }\n }\n );\n\n const timeoutId = setTimeout(() => {\n unsubscribe();\n reject(new Error(\"等待重启状态通知超时\"));\n }, timeout);\n\n // 通过 HTTP API 重启服务\n this.restartService().catch((error) => {\n clearTimeout(timeoutId);\n unsubscribe?.();\n reject(error);\n });\n });\n }\n}\n\n// 创建默认的网络服务实例\nexport const networkService = new NetworkService();\n\n// 导出其他服务\nexport { apiClient, webSocketManager };\nexport { ConnectionState } from \"./websocket\";\nexport { cozeApiClient, CozeApiClient } from \"./cozeApi\";\n\n// 导出类型\nexport type { ApiClient, WebSocketManager };\n","/**\n * WebSocket 连接状态管理 Store (重构版)\n *\n * 职责:\n * - 纯 WebSocket 连接状态管理\n * - 集成 WebSocketManager 单例\n * - 提供连接控制方法\n * - 保持向后兼容性\n *\n * 注意:\n * - 配置数据管理已迁移到 stores/config.ts\n * - 状态数据管理已迁移到 stores/status.ts\n */\n\nimport { WEBSOCKET_RECONNECT_DELAY } from \"@/constants/timeouts\";\nimport { ConnectionState, webSocketManager } from \"@/services/websocket\";\nimport { create } from \"zustand\";\nimport { devtools } from \"zustand/middleware\";\nimport { useShallow } from \"zustand/react/shallow\";\n\n/**\n * 端口变更状态接口(保留用于端口切换功能)\n */\ninterface PortChangeStatus {\n status:\n | \"idle\"\n | \"checking\"\n | \"polling\"\n | \"connecting\"\n | \"completed\"\n | \"failed\";\n targetPort?: number;\n currentAttempt?: number;\n maxAttempts?: number;\n error?: string;\n timestamp: number;\n}\n\n/**\n * WebSocket 连接统计信息\n */\ninterface ConnectionStats {\n reconnectAttempts: number;\n maxReconnectAttempts: number;\n lastHeartbeat: number;\n eventListenerCount: number;\n}\n\n/**\n * WebSocket Store 状态(简化版)\n */\ninterface WebSocketState {\n // WebSocket 连接状态\n connectionState: ConnectionState;\n wsUrl: string;\n\n // 连接统计信息\n connectionStats: ConnectionStats;\n\n // 端口变更状态(保留用于端口切换功能)\n portChangeStatus?: PortChangeStatus;\n\n // 连接错误信息\n lastError: Error | null;\n\n // 连接时间戳\n connectedAt: number | null;\n disconnectedAt: number | null;\n}\n\n/**\n * WebSocket Store 操作方法(简化版)\n */\ninterface WebSocketActions {\n // 基础连接状态管理\n setConnectionState: (state: ConnectionState) => void;\n setWsUrl: (url: string) => void;\n setConnectionStats: (stats: ConnectionStats) => void;\n setLastError: (error: Error | null) => void;\n\n // 端口变更状态管理(保留用于端口切换功能)\n setPortChangeStatus: (portChangeStatus: PortChangeStatus | undefined) => void;\n\n // WebSocket 连接控制\n connect: () => Promise<void>;\n disconnect: () => void;\n reconnect: () => Promise<void>;\n send: (message: any) => boolean;\n\n // URL 管理\n updateUrl: (url: string) => void;\n\n // 工具方法\n reset: () => void;\n initialize: () => void;\n getConnectionInfo: () => {\n state: ConnectionState;\n url: string;\n stats: ConnectionStats;\n isConnected: boolean;\n };\n\n // 向后兼容的方法(废弃)\n /** @deprecated 使用 setConnectionState 替代 */\n setConnected: (connected: boolean) => void;\n}\n\nexport interface WebSocketStore extends WebSocketState, WebSocketActions {}\n\n/**\n * 初始状态(简化版)\n */\nconst initialState: WebSocketState = {\n // WebSocket 连接状态\n connectionState: ConnectionState.DISCONNECTED,\n wsUrl: \"\",\n\n // 连接统计信息\n connectionStats: {\n reconnectAttempts: 0,\n maxReconnectAttempts: 5,\n lastHeartbeat: 0,\n eventListenerCount: 0,\n },\n\n // 端口变更状态\n portChangeStatus: undefined,\n\n // 连接错误信息\n lastError: null,\n\n // 连接时间戳\n connectedAt: null,\n disconnectedAt: null,\n};\n\n/**\n * 创建 WebSocket Store(重构版)\n */\nexport const useWebSocketStore = create<WebSocketStore>()(\n devtools(\n (set, get) => ({\n ...initialState,\n\n // ==================== 基础连接状态管理 ====================\n\n setConnectionState: (connectionState: ConnectionState) => {\n console.log(\"[WebSocketStore] 更新连接状态:\", connectionState);\n\n const now = Date.now();\n const updates: Partial<WebSocketState> = { connectionState };\n\n // 更新连接时间戳\n if (connectionState === ConnectionState.CONNECTED) {\n updates.connectedAt = now;\n updates.lastError = null;\n } else if (connectionState === ConnectionState.DISCONNECTED) {\n updates.disconnectedAt = now;\n }\n\n set(updates, false, \"setConnectionState\");\n\n // 同步更新 WebSocketManager 的统计信息\n const stats = webSocketManager.getConnectionStats();\n get().setConnectionStats(stats);\n },\n\n setWsUrl: (wsUrl: string) => {\n console.log(\"[WebSocketStore] 更新 WebSocket URL:\", wsUrl);\n set({ wsUrl }, false, \"setWsUrl\");\n },\n\n setConnectionStats: (connectionStats: ConnectionStats) => {\n set({ connectionStats }, false, \"setConnectionStats\");\n },\n\n setLastError: (lastError: Error | null) => {\n console.log(\"[WebSocketStore] 更新连接错误:\", lastError?.message);\n set({ lastError }, false, \"setLastError\");\n },\n\n // ==================== 端口变更状态管理 ====================\n\n setPortChangeStatus: (portChangeStatus: PortChangeStatus | undefined) => {\n console.log(\n \"[WebSocketStore] 更新端口变更状态:\",\n portChangeStatus?.status\n );\n set({ portChangeStatus }, false, \"setPortChangeStatus\");\n },\n\n // ==================== WebSocket 连接控制 ====================\n\n connect: async (): Promise<void> => {\n try {\n console.log(\"[WebSocketStore] 开始连接 WebSocket\");\n webSocketManager.connect();\n } catch (error) {\n const err = error instanceof Error ? error : new Error(\"连接失败\");\n console.error(\"[WebSocketStore] 连接失败:\", err);\n get().setLastError(err);\n throw err;\n }\n },\n\n disconnect: () => {\n console.log(\"[WebSocketStore] 断开 WebSocket 连接\");\n webSocketManager.disconnect();\n },\n\n reconnect: async (): Promise<void> => {\n try {\n console.log(\"[WebSocketStore] 重新连接 WebSocket\");\n webSocketManager.disconnect();\n await new Promise((resolve) =>\n setTimeout(resolve, WEBSOCKET_RECONNECT_DELAY)\n );\n webSocketManager.connect();\n } catch (error) {\n const err = error instanceof Error ? error : new Error(\"重连失败\");\n console.error(\"[WebSocketStore] 重连失败:\", err);\n get().setLastError(err);\n throw err;\n }\n },\n\n send: (message: any): boolean => {\n try {\n return webSocketManager.send(message);\n } catch (error) {\n const err =\n error instanceof Error ? error : new Error(\"发送消息失败\");\n console.error(\"[WebSocketStore] 发送消息失败:\", err);\n get().setLastError(err);\n return false;\n }\n },\n\n // ==================== URL 管理 ====================\n\n updateUrl: (url: string) => {\n console.log(\"[WebSocketStore] 更新 WebSocket URL:\", url);\n webSocketManager.setUrl(url);\n get().setWsUrl(url);\n },\n\n // ==================== 工具方法 ====================\n\n getConnectionInfo: () => {\n const state = get();\n return {\n state: state.connectionState,\n url: state.wsUrl,\n stats: state.connectionStats,\n isConnected: state.connectionState === ConnectionState.CONNECTED,\n };\n },\n\n reset: () => {\n console.log(\"[WebSocketStore] 重置状态\");\n set(initialState, false, \"reset\");\n },\n\n // ==================== 初始化方法 ====================\n\n initialize: () => {\n console.log(\"[WebSocketStore] 初始化 WebSocket Store\");\n\n // 设置 WebSocket 事件监听\n webSocketManager.subscribe(\"connection:connecting\", () => {\n get().setConnectionState(ConnectionState.CONNECTING);\n });\n\n webSocketManager.subscribe(\"connection:connected\", () => {\n get().setConnectionState(ConnectionState.CONNECTED);\n });\n\n webSocketManager.subscribe(\"connection:disconnected\", () => {\n get().setConnectionState(ConnectionState.DISCONNECTED);\n });\n\n webSocketManager.subscribe(\"connection:reconnecting\", () => {\n get().setConnectionState(ConnectionState.RECONNECTING);\n const stats = webSocketManager.getConnectionStats();\n get().setConnectionStats(stats);\n });\n\n webSocketManager.subscribe(\"connection:error\", ({ error }) => {\n get().setLastError(error);\n });\n\n webSocketManager.subscribe(\"system:heartbeat\", () => {\n const stats = webSocketManager.getConnectionStats();\n get().setConnectionStats(stats);\n });\n\n // 初始化连接状态\n const initialStats = webSocketManager.getConnectionStats();\n get().setConnectionStats(initialStats);\n get().setWsUrl(webSocketManager.getUrl());\n\n console.log(\"[WebSocketStore] WebSocket Store 初始化完成\");\n },\n\n // ==================== 向后兼容方法 ====================\n\n /** @deprecated 使用 setConnectionState 替代 */\n setConnected: (connected: boolean) => {\n console.warn(\n \"[WebSocketStore] setConnected 方法已废弃,请使用 setConnectionState\"\n );\n const connectionState: ConnectionState = connected\n ? ConnectionState.CONNECTED\n : ConnectionState.DISCONNECTED;\n get().setConnectionState(connectionState);\n },\n }),\n {\n name: \"websocket-store\",\n }\n )\n);\n\n// ==================== 选择器 Hooks ====================\n\n/**\n * 获取连接状态\n */\nexport const useWebSocketConnectionState = () =>\n useWebSocketStore((state) => state.connectionState);\n\n/**\n * 获取连接状态(布尔值)\n */\nexport const useWebSocketConnected = () =>\n useWebSocketStore(\n (state) => state.connectionState === ConnectionState.CONNECTED\n );\n\n/**\n * 获取 WebSocket URL\n */\nexport const useWebSocketUrl = () => useWebSocketStore((state) => state.wsUrl);\n\n/**\n * 获取连接统计信息\n */\nexport const useWebSocketConnectionStats = () =>\n useWebSocketStore((state) => state.connectionStats);\n\n/**\n * 获取端口变更状态\n */\nexport const useWebSocketPortChangeStatus = () =>\n useWebSocketStore((state) => state.portChangeStatus);\n\n/**\n * 获取最后的连接错误\n */\nexport const useWebSocketLastError = () =>\n useWebSocketStore((state) => state.lastError);\n\n/**\n * 获取连接时间戳\n */\nexport const useWebSocketConnectionTimes = () =>\n useWebSocketStore(\n useShallow((state) => ({\n connectedAt: state.connectedAt,\n disconnectedAt: state.disconnectedAt,\n }))\n );\n\n// ==================== 向后兼容的选择器(废弃) ====================\n//\n// 注意:这些选择器已迁移到 ./websocket-compat.ts 以避免循环依赖\n// 请从 @/stores/websocket-compat 或 @/stores 导入这些废弃的选择器\n//\n// 迁移指南:\n// - useWebSocketConfig → useConfig from \"./config\"\n// - useWebSocketStatus → useClientStatus from \"./status\"\n// - useWebSocketMcpEndpoint → useMcpEndpoint from \"./config\"\n// - useWebSocketMcpServers → useMcpServers from \"./config\"\n// - useWebSocketMcpServerConfig → useMcpServerConfig from \"./config\"\n// - useWebSocketRestartStatus → useRestartStatus from \"./status\"\n\n// ==================== 复合选择器 ====================\n\n/**\n * 获取连接相关信息\n */\nexport const useWebSocketConnectionInfo = () =>\n useWebSocketStore(\n useShallow((state) => ({\n connected: state.connectionState === ConnectionState.CONNECTED,\n connectionState: state.connectionState,\n wsUrl: state.wsUrl,\n stats: state.connectionStats,\n lastError: state.lastError,\n connectedAt: state.connectedAt,\n disconnectedAt: state.disconnectedAt,\n }))\n );\n\n/**\n * 获取端口变更相关信息\n */\nexport const useWebSocketPortInfo = () =>\n useWebSocketStore(\n useShallow((state) => ({\n portChangeStatus: state.portChangeStatus,\n wsUrl: state.wsUrl,\n }))\n );\n\n// ==================== 操作方法 Hooks ====================\n\n/**\n * 获取 WebSocket 操作方法\n */\nexport const useWebSocketActions = () =>\n useWebSocketStore(\n useShallow((state) => ({\n // 连接状态管理\n setConnectionState: state.setConnectionState,\n setWsUrl: state.setWsUrl,\n setConnectionStats: state.setConnectionStats,\n setLastError: state.setLastError,\n\n // 端口变更状态管理\n setPortChangeStatus: state.setPortChangeStatus,\n\n // WebSocket 连接控制\n connect: state.connect,\n disconnect: state.disconnect,\n reconnect: state.reconnect,\n send: state.send,\n\n // URL 管理\n updateUrl: state.updateUrl,\n\n // 工具方法\n reset: state.reset,\n initialize: state.initialize,\n getConnectionInfo: state.getConnectionInfo,\n\n // 向后兼容(废弃)\n setConnected: state.setConnected,\n }))\n );\n\n/**\n * 获取连接控制方法\n */\nexport const useWebSocketControls = () =>\n useWebSocketStore(\n useShallow((state) => ({\n connect: state.connect,\n disconnect: state.disconnect,\n reconnect: state.reconnect,\n send: state.send,\n updateUrl: state.updateUrl,\n isConnected: state.connectionState === ConnectionState.CONNECTED,\n }))\n );\n\n// ==================== 向后兼容的复合选择器(废弃) ====================\n\n/**\n * @deprecated 数据已分离到不同的 stores,请使用对应的选择器\n */\nexport const useWebSocketData = () => {\n console.warn(\"useWebSocketData 已废弃,请使用对应的专门 stores\");\n return {\n config: null,\n status: null,\n };\n};\n","/**\n * 新的网络服务 Hook\n * 使用统一的网络服务管理器,实现 HTTP 和 WebSocket 的协调使用\n */\n\nimport { ConnectionState, networkService } from \"@/services/index\";\nimport type { RestartStatus } from \"@/services/websocket\";\nimport { useConfigStore } from \"@/stores/config\";\nimport { useStatusStore } from \"@/stores/status\";\nimport { useWebSocketActions } from \"@/stores/websocket\";\nimport type { AppConfig, ClientStatus } from \"@xiaozhi-client/shared-types\";\nimport { useCallback, useEffect, useRef } from \"react\";\n\n/**\n * 网络服务 Hook\n */\nexport function useNetworkService() {\n const webSocketActions = useWebSocketActions();\n const initializationRef = useRef(false);\n\n // 初始化网络服务\n useEffect(() => {\n if (initializationRef.current) {\n return;\n }\n\n console.log(\"[NetworkService] 初始化网络服务\");\n initializationRef.current = true;\n\n // 初始化网络服务\n networkService.initialize().catch((error) => {\n console.error(\"[NetworkService] 初始化失败:\", error);\n });\n\n // 保存所有事件监听器的取消订阅函数\n const unsubscribers = [\n // 连接状态事件\n networkService.onWebSocketEvent(\"connection:connected\", () => {\n console.log(\"[NetworkService] WebSocket 已连接\");\n webSocketActions.setConnectionState(ConnectionState.CONNECTED);\n\n // 连接成功后立即获取初始数据\n loadInitialData();\n }),\n\n networkService.onWebSocketEvent(\"connection:disconnected\", () => {\n console.log(\"[NetworkService] WebSocket 已断开\");\n webSocketActions.setConnectionState(ConnectionState.DISCONNECTED);\n }),\n\n // 数据更新事件\n networkService.onWebSocketEvent(\n \"data:configUpdate\",\n (config: AppConfig) => {\n console.log(\"[NetworkService] 收到配置更新通知\");\n useConfigStore.getState().setConfig(config, \"websocket\");\n }\n ),\n\n networkService.onWebSocketEvent(\n \"data:statusUpdate\",\n (status: ClientStatus) => {\n console.log(\"[NetworkService] 收到状态更新通知\");\n useStatusStore.getState().setClientStatus(status, \"websocket\");\n }\n ),\n\n networkService.onWebSocketEvent(\n \"data:restartStatus\",\n (restartStatus: RestartStatus) => {\n console.log(\"[NetworkService] 收到重启状态通知:\", restartStatus);\n useStatusStore\n .getState()\n .setRestartStatus(restartStatus, \"websocket\");\n }\n ),\n\n // 系统事件\n networkService.onWebSocketEvent(\n \"system:error\",\n ({ error }: { error: Error }) => {\n console.error(\"[NetworkService] WebSocket 错误:\", error);\n\n // 将错误信息存储到状态管理中,供 UI 展示\n const errorMessage = `WebSocket 连接错误: ${error.message}`;\n useStatusStore.getState().setError(new Error(errorMessage));\n\n // 根据错误类型进行分类处理\n if (\n error.message.includes(\"ECONNREFUSED\") ||\n error.message.includes(\"连接被拒绝\")\n ) {\n console.warn(\"[NetworkService] 连接被拒绝,可能是服务未启动\");\n // 可以在这里添加用户友好的提示逻辑\n } else if (\n error.message.includes(\"timeout\") ||\n error.message.includes(\"超时\")\n ) {\n console.warn(\"[NetworkService] 连接超时,请检查网络连接\");\n } else {\n console.warn(\"[NetworkService] 未知的 WebSocket 错误:\", error);\n }\n }\n ),\n ];\n\n // 清理函数\n return () => {\n console.log(\"[NetworkService] 清理网络服务\");\n // 清理所有事件监听器,防止内存泄漏\n for (const unsubscribe of unsubscribers) {\n try {\n unsubscribe?.();\n } catch (error) {\n console.error(\"[NetworkService] 清理事件监听器失败:\", error);\n }\n }\n networkService.destroy();\n initializationRef.current = false;\n };\n }, [webSocketActions]);\n\n /**\n * 加载初始数据\n */\n const loadInitialData = useCallback(async () => {\n try {\n console.log(\"[NetworkService] 加载初始数据\");\n\n // 并行获取配置和状态\n const [config, status] = await Promise.all([\n networkService.getConfig(),\n networkService.getClientStatus(),\n ]);\n\n console.log(\"[NetworkService] 初始数据加载成功\");\n useConfigStore.getState().setConfig(config, \"http\");\n useStatusStore.getState().setClientStatus(status, \"http\");\n } catch (error) {\n console.error(\"[NetworkService] 加载初始数据失败:\", error);\n }\n }, []);\n\n /**\n * 获取配置\n */\n const getConfig = useCallback(async (): Promise<AppConfig> => {\n try {\n const config = await networkService.getConfig();\n useConfigStore.getState().setConfig(config, \"http\");\n return config;\n } catch (error) {\n console.error(\"[NetworkService] 获取配置失败:\", error);\n throw error;\n }\n }, []);\n\n /**\n * 更新配置\n */\n const updateConfig = useCallback(async (config: AppConfig): Promise<void> => {\n try {\n console.log(\"[NetworkService] 更新配置\");\n await networkService.updateConfig(config);\n\n // 立即更新本地状态,WebSocket 通知会进一步确认\n useConfigStore.getState().setConfig(config, \"http\");\n console.log(\"[NetworkService] 配置更新成功\");\n } catch (error) {\n console.error(\"[NetworkService] 配置更新失败:\", error);\n throw error;\n }\n }, []);\n\n /**\n * 获取状态\n */\n const getStatus = useCallback(async () => {\n try {\n const status = await networkService.getStatus();\n useStatusStore.getState().setClientStatus(status.client, \"http\");\n return status;\n } catch (error) {\n console.error(\"[NetworkService] 获取状态失败:\", error);\n throw error;\n }\n }, []);\n\n /**\n * 刷新状态\n */\n const refreshStatus = useCallback(async (): Promise<void> => {\n try {\n await getStatus();\n } catch (error) {\n console.error(\"[NetworkService] 刷新状态失败:\", error);\n }\n }, [getStatus]);\n\n /**\n * 重启服务\n */\n const restartService = useCallback(async (): Promise<void> => {\n try {\n console.log(\"[NetworkService] 重启服务\");\n await networkService.restartService();\n console.log(\"[NetworkService] 重启请求已发送\");\n } catch (error) {\n console.error(\"[NetworkService] 重启服务失败:\", error);\n throw error;\n }\n }, []);\n\n /**\n * 重启服务并等待完成通知\n */\n const restartServiceWithNotification = useCallback(\n async (timeout = 30000): Promise<void> => {\n try {\n console.log(\"[NetworkService] 重启服务并等待通知\");\n await networkService.restartServiceWithNotification(timeout);\n console.log(\"[NetworkService] 服务重启完成\");\n } catch (error) {\n console.error(\"[NetworkService] 重启服务失败:\", error);\n throw error;\n }\n },\n []\n );\n\n /**\n * 更新配置并等待通知\n */\n const updateConfigWithNotification = useCallback(\n async (config: AppConfig, timeout = 5000): Promise<void> => {\n try {\n console.log(\"[NetworkService] 更新配置并等待通知\");\n await networkService.updateConfigWithNotification(config, timeout);\n console.log(\"[NetworkService] 配置更新完成\");\n } catch (error) {\n console.error(\"[NetworkService] 配置更新失败:\", error);\n throw error;\n }\n },\n []\n );\n\n /**\n * 设置自定义 WebSocket URL\n */\n const setCustomWsUrl = useCallback(\n (url: string): void => {\n console.log(\"[NetworkService] 设置自定义 WebSocket URL:\", url);\n networkService.setWebSocketUrl(url);\n webSocketActions.setWsUrl(url);\n },\n [webSocketActions]\n );\n\n /**\n * 端口切换功能 (保留向后兼容)\n */\n const changePort = useCallback(\n async (newPort: number): Promise<void> => {\n try {\n console.log(`[NetworkService] 切换到端口 ${newPort}`);\n\n // 更新端口变更状态\n webSocketActions.setPortChangeStatus({\n status: \"checking\",\n targetPort: newPort,\n timestamp: Date.now(),\n });\n\n // 构建新的 WebSocket URL\n const protocol = window.location.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n const hostname = window.location.hostname;\n const newUrl = `${protocol}//${hostname}:${newPort}`;\n\n // 重启服务\n await restartService();\n\n // 等待一段时间让服务重启\n await new Promise((resolve) => setTimeout(resolve, 5000));\n\n // 更新 WebSocket URL\n setCustomWsUrl(newUrl);\n\n // 重新加载页面\n window.location.reload();\n } catch (error) {\n console.error(\"[NetworkService] 端口切换失败:\", error);\n webSocketActions.setPortChangeStatus({\n status: \"failed\",\n targetPort: newPort,\n error: error instanceof Error ? error.message : \"端口切换失败\",\n timestamp: Date.now(),\n });\n throw error;\n }\n },\n [webSocketActions, restartService, setCustomWsUrl]\n );\n\n /**\n * 获取当前 WebSocket URL\n */\n const getWebSocketUrl = useCallback((): string => {\n // 从 localStorage 获取自定义 URL\n const savedUrl = localStorage.getItem(\"xiaozhi-ws-url\");\n if (savedUrl) {\n return savedUrl;\n }\n\n // 根据当前页面 URL 构建 WebSocket URL\n const protocol = window.location.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n const hostname = window.location.hostname;\n const port = window.location.port;\n return `${protocol}//${hostname}${port ? `:${port}` : \"\"}`;\n }, []);\n\n return {\n // 数据操作方法 (HTTP)\n getConfig,\n updateConfig,\n getStatus,\n refreshStatus,\n restartService,\n\n // 混合模式方法 (HTTP + WebSocket)\n updateConfigWithNotification,\n restartServiceWithNotification,\n\n // WebSocket 管理\n setCustomWsUrl,\n getWebSocketUrl,\n\n // 端口切换 (向后兼容)\n changePort,\n\n // 工具方法\n loadInitialData,\n\n // 网络服务状态\n isWebSocketConnected: () => networkService.isWebSocketConnected(),\n getWebSocketState: () => networkService.getWebSocketState(),\n };\n}\n","/**\n * Stores 统一初始化和管理\n *\n * 这个文件负责:\n * - 初始化所有 stores\n * - 设置 stores 之间的协调\n * - 提供统一的初始化入口\n */\n\nimport { useConfigStore } from \"./config\";\nimport { useStatusStore } from \"./status\";\nimport { useWebSocketStore } from \"./websocket\";\n\n/**\n * 初始化所有 stores\n *\n * 这个函数应该在应用启动时调用一次\n */\nexport async function initializeStores(): Promise<void> {\n console.log(\"[Stores] 开始初始化所有 stores\");\n\n try {\n // 1. 首先初始化 WebSocket store(建立连接管理)\n console.log(\"[Stores] 初始化 WebSocket store\");\n useWebSocketStore.getState().initialize();\n\n // 2. 初始化配置 store(设置配置数据管理和 WebSocket 监听)\n console.log(\"[Stores] 初始化配置 store\");\n await useConfigStore.getState().initialize();\n\n // 3. 初始化 MCP 服务器状态\n console.log(\"[Stores] 初始化 MCP 服务器状态\");\n await useConfigStore.getState().refreshMcpServerStatuses();\n\n // 4. 初始化状态 store(设置状态数据管理和 WebSocket 监听)\n console.log(\"[Stores] 初始化状态 store\");\n await useStatusStore.getState().initialize();\n\n console.log(\"[Stores] 所有 stores 初始化完成\");\n } catch (error) {\n console.error(\"[Stores] Stores 初始化失败:\", error);\n throw error;\n }\n}\n\n/**\n * 重置所有 stores\n *\n * 用于测试或需要完全重置应用状态时\n */\nexport function resetAllStores(): void {\n console.log(\"[Stores] 重置所有 stores\");\n\n useWebSocketStore.getState().reset();\n useConfigStore.getState().reset();\n useStatusStore.getState().reset();\n\n console.log(\"[Stores] 所有 stores 已重置\");\n}\n\n/**\n * 获取所有 stores 的状态摘要\n *\n * 用于调试和监控\n */\nexport function getStoresStatus() {\n const websocketState = useWebSocketStore.getState();\n const configState = useConfigStore.getState();\n const statusState = useStatusStore.getState();\n\n return {\n websocket: {\n connectionState: websocketState.connectionState,\n url: websocketState.wsUrl,\n connected: websocketState.connectionState === \"connected\",\n lastError: websocketState.lastError?.message,\n },\n config: {\n hasConfig: !!configState.config,\n isLoading: configState.loading.isLoading,\n isUpdating: configState.loading.isUpdating,\n lastUpdated: configState.loading.lastUpdated,\n lastError: configState.loading.lastError?.message,\n source: configState.lastSource,\n },\n status: {\n hasStatus: !!statusState.clientStatus,\n isLoading: statusState.loading.isLoading,\n isRestarting: statusState.loading.isRestarting,\n pollingEnabled: statusState.polling.enabled,\n lastUpdated: statusState.loading.lastUpdated,\n lastError: statusState.loading.lastError?.message,\n source: statusState.lastSource,\n },\n };\n}\n\n// 导出所有 stores 以便统一访问\nexport { useWebSocketStore } from \"./websocket\";\nexport { useConfigStore } from \"./config\";\nexport { useStatusStore } from \"./status\";\n\n// 导出废弃的兼容性选择器(从 websocket-compat.ts 直接导入以避免循环依赖)\nexport {\n useWebSocketConfig,\n useWebSocketStatus,\n useWebSocketMcpEndpoint,\n useWebSocketMcpServers,\n useWebSocketMcpServerConfig,\n useWebSocketRestartStatus,\n} from \"./websocket-compat\";\n\n// 导出所有选择器 hooks(避免命名冲突)\nexport {\n // WebSocket store exports\n useWebSocketConnectionState,\n useWebSocketConnected,\n useWebSocketUrl,\n useWebSocketConnectionStats,\n useWebSocketPortChangeStatus,\n useWebSocketLastError,\n useWebSocketConnectionTimes,\n useWebSocketConnectionInfo,\n useWebSocketPortInfo,\n useWebSocketActions,\n useWebSocketControls,\n useWebSocketData,\n} from \"./websocket\";\n\nexport {\n // Config store exports\n useConfig,\n useConfigLoading,\n useConfigIsLoading,\n useConfigIsUpdating,\n useConfigError,\n useMcpEndpoint,\n useMcpServers,\n useMcpServerConfig,\n useConnectionConfig,\n useModelScopeConfig,\n useWebUIConfig,\n useConfigSource,\n useConfigWithLoading,\n useMcpConfig,\n useSystemConfig,\n useConfigActions,\n useConfigUpdaters,\n} from \"./config\";\n\nexport {\n // Status store exports\n useClientStatus,\n useRestartStatus,\n useServiceStatus,\n useServiceHealth,\n useFullStatus,\n useStatusLoading,\n useStatusIsLoading,\n useStatusIsRestarting,\n useStatusError,\n usePollingConfig,\n usePollingEnabled,\n useStatusSource,\n useConnectionStatus,\n useStatusMcpEndpoint,\n useActiveMcpServers,\n useLastHeartbeat,\n useStatusWithLoading,\n useServiceInfo,\n useConnectionInfo,\n useStatusActions,\n usePollingActions,\n} from \"./status\";\n","/**\n * 网络服务 Provider 组件\n *\n * 为应用提供网络相关的功能,包括:\n * - HTTP API 调用(getConfig、updateConfig、getStatus 等)\n * - WebSocket 连接管理\n * - 配置更新通知(结合 HTTP 和 WebSocket)\n * - 服务重启通知\n * - 端口切换\n *\n * 该组件在内部初始化所有 stores,尝试在应用启动时完成数据初始化;若初始化失败会记录错误并允许应用继续运行。\n *\n * @example\n * ```tsx\n * import { WebSocketProvider, useNetworkServiceActions } from '@/providers/WebSocketProvider';\n *\n * function App() {\n * return (\n * <WebSocketProvider>\n * <YourAppComponents />\n * </WebSocketProvider>\n * );\n * }\n *\n * // 在组件中使用\n * function MyComponent() {\n * const { getConfig, updateConfig } = useNetworkServiceActions();\n * // ...\n * }\n * ```\n */\nimport { useNetworkService } from \"@/hooks/useNetworkService\";\nimport { initializeStores } from \"@/stores/index\";\nimport type { AppConfig } from \"@xiaozhi-client/shared-types\";\nimport {\n type ReactNode,\n createContext,\n useContext,\n useEffect,\n useState,\n} from \"react\";\n\ninterface NetworkServiceContextType {\n // HTTP API 方法\n getConfig: () => Promise<AppConfig>;\n updateConfig: (config: AppConfig) => Promise<void>;\n getStatus: () => Promise<any>;\n refreshStatus: () => Promise<void>;\n restartService: () => Promise<void>;\n\n // 混合模式方法 (HTTP + WebSocket)\n updateConfigWithNotification: (\n config: AppConfig,\n timeout?: number\n ) => Promise<void>;\n restartServiceWithNotification: (timeout?: number) => Promise<void>;\n\n // WebSocket 管理\n setCustomWsUrl: (url: string) => void;\n getWebSocketUrl: () => string;\n\n // 端口切换 (向后兼容)\n changePort: (newPort: number) => Promise<void>;\n\n // 工具方法\n loadInitialData: () => Promise<void>;\n isWebSocketConnected: () => boolean;\n getWebSocketState: () => any;\n}\n\nconst NetworkServiceContext = createContext<NetworkServiceContextType | null>(\n null\n);\n\ninterface NetworkServiceProviderProps {\n children: ReactNode;\n}\n\nexport function NetworkServiceProvider({\n children,\n}: NetworkServiceProviderProps) {\n const networkService = useNetworkService();\n const [storesInitialized, setStoresInitialized] = useState(false);\n\n // 初始化 stores\n useEffect(() => {\n let mounted = true;\n\n const initStores = async () => {\n try {\n console.log(\"[WebSocketProvider] 开始初始化 stores\");\n await initializeStores();\n\n if (mounted) {\n setStoresInitialized(true);\n console.log(\"[WebSocketProvider] Stores 初始化完成\");\n }\n } catch (error) {\n console.error(\"[WebSocketProvider] Stores 初始化失败:\", error);\n // 即使初始化失败,也允许应用继续运行\n if (mounted) {\n setStoresInitialized(true);\n }\n }\n };\n\n initStores();\n\n return () => {\n mounted = false;\n };\n }, []);\n\n // 在 stores 初始化完成前显示加载状态(可选)\n if (!storesInitialized) {\n return (\n <div className=\"flex items-center justify-center min-h-screen\">\n <div className=\"text-center\">\n <div className=\"animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600 mx-auto mb-2\" />\n <p className=\"text-sm text-gray-600\">正在初始化应用...</p>\n </div>\n </div>\n );\n }\n\n return (\n <NetworkServiceContext.Provider value={networkService}>\n {children}\n </NetworkServiceContext.Provider>\n );\n}\n\nexport function useNetworkServiceActions() {\n const context = useContext(NetworkServiceContext);\n if (!context) {\n throw new Error(\n \"useNetworkServiceActions must be used within a NetworkServiceProvider\"\n );\n }\n return context;\n}\n\n// 向后兼容的别名\nexport const WebSocketProvider = NetworkServiceProvider;\nexport const useWebSocketActions = useNetworkServiceActions;\n","import { Button } from \"@/components/ui/button\";\nimport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from \"@/components/ui/dialog\";\nimport {\n Form,\n FormControl,\n FormField,\n FormItem,\n FormMessage,\n} from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\nimport { useNetworkServiceActions } from \"@/providers/WebSocketProvider\";\nimport { useConfig } from \"@/stores/config\";\nimport {\n useWebSocketConnected,\n useWebSocketPortChangeStatus,\n} from \"@/stores/websocket\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { SettingsIcon } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport { toast } from \"sonner\";\nimport z from \"zod\";\n\nconst formSchema = z.object({\n port: z\n .string()\n .min(1, { message: \"端口号不能为空\" })\n .refine((val) => !Number.isNaN(Number(val)), {\n message: \"请输入有效的数字\",\n })\n .refine((val) => Number(val) >= 1 && Number(val) <= 65535, {\n message: \"端口号必须在 1-65535 之间\",\n })\n .refine((val) => Number.isInteger(Number(val)), {\n message: \"端口号必须是整数\",\n }),\n});\n\nexport function WebUrlSettingButton() {\n const [open, setOpen] = useState(false);\n const [isLoading, setIsLoading] = useState(false);\n const config = useConfig();\n const connected = useWebSocketConnected();\n const portChangeStatus = useWebSocketPortChangeStatus();\n const { changePort } = useNetworkServiceActions();\n\n const form = useForm<z.infer<typeof formSchema>>({\n resolver: zodResolver(formSchema),\n defaultValues: {\n port: \"9999\",\n },\n });\n\n // 当配置加载后,更新表单默认值\n useEffect(() => {\n if (config?.webUI?.port) {\n form.reset({\n port: config.webUI.port.toString(),\n });\n }\n }, [config, form]);\n\n // 获取按钮文本\n const getButtonText = () => {\n if (isLoading) {\n return \"处理中...\";\n }\n\n if (portChangeStatus?.status === \"checking\") {\n return \"检测端口...\";\n }\n\n if (portChangeStatus?.status === \"polling\") {\n const { currentAttempt, maxAttempts } = portChangeStatus;\n return `等待服务重启 (${currentAttempt || 0}/${maxAttempts || 45})`;\n }\n\n if (portChangeStatus?.status === \"connecting\") {\n return \"连接中...\";\n }\n\n return connected ? \"保存并重启\" : \"保存并连接\";\n };\n\n async function onSubmit(values: z.infer<typeof formSchema>) {\n const newPort = Number(values.port);\n const currentPort = config?.webUI?.port;\n\n // 如果端口号没有变化,直接关闭对话框\n if (newPort === currentPort) {\n setOpen(false);\n return;\n }\n\n console.log(\n `[WebUrlSettingButton] 开始端口切换: ${currentPort} -> ${newPort}`\n );\n setIsLoading(true);\n\n try {\n // 显示开始处理的提示\n toast.info(\n connected\n ? `正在将端口从 ${currentPort} 切换到 ${newPort}...`\n : `正在连接到端口 ${newPort}...`\n );\n\n await changePort(newPort);\n\n // 成功提示\n toast.success(\n connected\n ? `端口已成功切换到 ${newPort},页面即将刷新...`\n : `已成功连接到端口 ${newPort},页面即将刷新...`\n );\n setOpen(false);\n } catch (error) {\n console.error(\"端口切换失败:\", error);\n const errorMessage =\n error instanceof Error ? error.message : \"端口切换失败\";\n toast.error(`端口切换失败: ${errorMessage}`);\n } finally {\n setIsLoading(false);\n }\n }\n\n return (\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogTrigger asChild>\n <Button variant=\"secondary\" size=\"icon\" className=\"size-8\">\n <SettingsIcon className=\"h-4 w-4\" />\n </Button>\n </DialogTrigger>\n <DialogContent className=\"sm:max-w-[250px]\">\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)}>\n <DialogHeader className=\"mb-4\">\n <DialogTitle>配置服务端端口</DialogTitle>\n <DialogDescription>\n {connected\n ? \"修改端口后将自动重启服务并重新连接。\"\n : \"请输入服务端端口号,系统将尝试连接。\"}\n </DialogDescription>\n </DialogHeader>\n <div className=\"flex items-center gap-2\">\n <div>ws://{window.location.hostname}:</div>\n <div className=\"w-[100px]\">\n <FormField\n control={form.control}\n name=\"port\"\n render={({ field }) => (\n <FormItem>\n <FormControl>\n <Input\n placeholder=\"服务端端口,默认9999\"\n className=\"font-mono text-sm\"\n disabled={isLoading}\n type=\"number\"\n {...field}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n </div>\n </div>\n <DialogFooter className=\"mt-4\">\n <DialogClose asChild>\n <Button variant=\"outline\" disabled={isLoading}>\n 取消\n </Button>\n </DialogClose>\n <Button\n type=\"submit\"\n disabled={isLoading || portChangeStatus?.status === \"polling\"}\n >\n {getButtonText()}\n </Button>\n </DialogFooter>\n </form>\n </Form>\n </DialogContent>\n </Dialog>\n );\n}\n","/**\n * 客户端连接状态卡片组件\n *\n * 显示 Xiaozhi Client 的连接状态和 WebSocket URL。\n */\n\nimport {\n Card,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from \"@/components/ui/card\";\nimport { WebUrlSettingButton } from \"@/components/web-url-setting-button\";\nimport { useWebSocketConnected, useWebSocketUrl } from \"@/stores/websocket\";\nimport { MiniCircularProgress } from \"./mini-circular-progress\";\n\n/**\n * 客户端连接状态卡片组件\n *\n * 显示当前 WebSocket 连接状态和 URL,右上角显示连接状态指示器。\n */\nexport function ClientStatusCard() {\n const connected = useWebSocketConnected();\n const wsUrl = useWebSocketUrl();\n\n return (\n <Card className=\"@container/card\">\n <CardHeader className=\"relative\">\n <CardDescription>Xiaozhi Client</CardDescription>\n <CardTitle className=\"@[250px]/card:text-3xl text-2xl font-semibold tabular-nums\">\n {connected ? \"已连接\" : \"未连接\"}\n </CardTitle>\n <div className=\"absolute right-4 top-4\">\n <MiniCircularProgress\n showValue={false}\n value={1}\n maxValue={1}\n activeColor={connected ? \"#16a34a\" : \"#f87171\"}\n inactiveColor={connected ? \"#16a34a\" : \"#f87171\"}\n size={30}\n symbol=\"\"\n />\n </div>\n </CardHeader>\n <CardFooter className=\"flex items-center justify-between gap-1 text-sm\">\n <div className=\"text-muted-foreground\">{wsUrl}</div>\n <WebUrlSettingButton />\n </CardFooter>\n </Card>\n );\n}\n","import * as SelectPrimitive from \"@radix-ui/react-select\";\nimport { Check, ChevronDown, ChevronUp } from \"lucide-react\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst Select = SelectPrimitive.Root;\n\nconst SelectGroup = SelectPrimitive.Group;\n\nconst SelectValue = SelectPrimitive.Value;\n\nconst SelectTrigger = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Trigger>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Trigger\n ref={ref}\n className={cn(\n \"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background data-[placeholder]:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1\",\n className\n )}\n {...props}\n >\n {children}\n <SelectPrimitive.Icon asChild>\n <ChevronDown className=\"h-4 w-4 opacity-50\" />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n));\nSelectTrigger.displayName = SelectPrimitive.Trigger.displayName;\n\nconst SelectScrollUpButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.ScrollUpButton\n ref={ref}\n className={cn(\n \"flex cursor-default items-center justify-center py-1\",\n className\n )}\n {...props}\n >\n <ChevronUp className=\"h-4 w-4\" />\n </SelectPrimitive.ScrollUpButton>\n));\nSelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;\n\nconst SelectScrollDownButton = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.ScrollDownButton\n ref={ref}\n className={cn(\n \"flex cursor-default items-center justify-center py-1\",\n className\n )}\n {...props}\n >\n <ChevronDown className=\"h-4 w-4\" />\n </SelectPrimitive.ScrollDownButton>\n));\nSelectScrollDownButton.displayName =\n SelectPrimitive.ScrollDownButton.displayName;\n\nconst SelectContent = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>\n>(({ className, children, position = \"popper\", ...props }, ref) => (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n ref={ref}\n className={cn(\n \"relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-select-content-transform-origin]\",\n position === \"popper\" &&\n \"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1\",\n className\n )}\n position={position}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport\n className={cn(\n \"p-1\",\n position === \"popper\" &&\n \"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]\"\n )}\n >\n {children}\n </SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n));\nSelectContent.displayName = SelectPrimitive.Content.displayName;\n\nconst SelectLabel = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Label\n ref={ref}\n className={cn(\"py-1.5 pl-8 pr-2 text-sm font-semibold\", className)}\n {...props}\n />\n));\nSelectLabel.displayName = SelectPrimitive.Label.displayName;\n\nconst SelectItem = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Item\n ref={ref}\n className={cn(\n \"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n className\n )}\n {...props}\n >\n <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <Check className=\"h-4 w-4\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n));\nSelectItem.displayName = SelectPrimitive.Item.displayName;\n\nconst SelectSeparator = React.forwardRef<\n React.ElementRef<typeof SelectPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Separator\n ref={ref}\n className={cn(\"-mx-1 my-1 h-px bg-muted\", className)}\n {...props}\n />\n));\nSelectSeparator.displayName = SelectPrimitive.Separator.displayName;\n\nexport {\n Select,\n SelectGroup,\n SelectValue,\n SelectTrigger,\n SelectContent,\n SelectLabel,\n SelectItem,\n SelectSeparator,\n SelectScrollUpButton,\n SelectScrollDownButton,\n};\n","import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst Textarea = React.forwardRef<\n HTMLTextAreaElement,\n React.ComponentProps<\"textarea\">\n>(({ className, ...props }, ref) => {\n return (\n <textarea\n className={cn(\n \"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-base ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 md:text-sm\",\n className\n )}\n ref={ref}\n {...props}\n />\n );\n});\nTextarea.displayName = \"Textarea\";\n\nexport { Textarea };\n","/**\n * 通用表单字段渲染组件\n * 根据配置自动渲染不同类型的表单字段\n */\n\nimport {\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@/components/ui/select\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport type { FieldConfig } from \"@/types/form-config\";\nimport type { UseFormReturn } from \"react-hook-form\";\nimport type { z } from \"zod\";\n\n/**\n * 渲染单个表单字段\n * @param config - 字段配置\n * @param form - React Hook Form 实例\n * @param disabled - 是否禁用(覆盖配置中的 disabled)\n */\nexport function renderFormField<\n T extends z.ZodType,\n Output extends Record<string, any> = z.infer<T> & Record<string, any>,\n>(\n config: FieldConfig<T, Output>,\n form: UseFormReturn<Output>,\n disabled?: boolean\n) {\n const {\n name,\n type,\n label,\n required,\n placeholder,\n description,\n condition,\n options,\n inputType,\n } = config;\n\n // 检查显示条件\n if (condition && !condition(form)) {\n return null;\n }\n\n const isDisabled = disabled ?? config.disabled;\n\n // 辅助函数:根据类型渲染输入组件\n // 必须在 FormControl 外部提前决定渲染哪个组件\n // 因为 FormControl 使用的 Slot 组件通过 React.Children.only 严格要求只有一个子元素\n const renderInput = (field: any) => {\n switch (type) {\n case \"text\":\n return (\n <Input\n {...field}\n type={inputType}\n placeholder={placeholder}\n disabled={isDisabled}\n className={config.className}\n />\n );\n case \"textarea\":\n return (\n <Textarea\n {...field}\n placeholder={placeholder}\n className={config.className}\n rows={config.rows}\n disabled={isDisabled}\n />\n );\n case \"select\":\n return (\n <Select\n value={field.value}\n onValueChange={field.onChange}\n disabled={isDisabled}\n >\n <SelectTrigger>\n <SelectValue placeholder={placeholder} />\n </SelectTrigger>\n <SelectContent>\n {options?.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n );\n default:\n return null;\n }\n };\n\n return (\n <FormField\n key={String(name)}\n control={form.control}\n name={String(name) as any}\n render={({ field }) => (\n <FormItem>\n <FormLabel>\n {required && <span className=\"text-red-500 mr-1\">*</span>}\n {label}\n </FormLabel>\n <FormControl>{renderInput(field)}</FormControl>\n {typeof description === \"function\" ? (\n <FormDescription>{description(form)}</FormDescription>\n ) : description ? (\n <FormDescription>{description}</FormDescription>\n ) : null}\n <FormMessage />\n </FormItem>\n )}\n />\n );\n}\n\n/**\n * 渲染带自定义 onChange 处理的 select 字段\n * @param config - 字段配置\n * @param form - React Hook Form 实例\n * @param onValueChange - 自定义值变化处理函数\n * @param disabled - 是否禁用\n */\nexport function renderSelectFieldWithHandler<\n T extends z.ZodType,\n Output extends Record<string, any> = z.infer<T> & Record<string, any>,\n V extends string = string,\n>(\n config: FieldConfig<T, Output>,\n form: UseFormReturn<Output>,\n onValueChange: (value: V) => void,\n disabled?: boolean\n) {\n const { name, type, label, required, placeholder, description, options } =\n config;\n\n if (type !== \"select\") {\n console.warn(\"renderSelectFieldWithHandler 只能用于 select 类型字段\");\n return renderFormField(config, form, disabled);\n }\n\n return (\n <FormField\n key={String(name)}\n control={form.control}\n name={String(name) as any}\n render={({ field }) => (\n <FormItem>\n <FormLabel>\n {required && <span className=\"text-red-500 mr-1\">*</span>}\n {label}\n </FormLabel>\n <FormControl>\n <Select\n value={field.value}\n onValueChange={onValueChange}\n disabled={disabled ?? config.disabled}\n >\n <SelectTrigger>\n <SelectValue placeholder={placeholder} />\n </SelectTrigger>\n <SelectContent>\n {options?.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </FormControl>\n {typeof description === \"function\" ? (\n <FormDescription>{description(form)}</FormDescription>\n ) : description ? (\n <FormDescription>{description}</FormDescription>\n ) : null}\n <FormMessage />\n </FormItem>\n )}\n />\n );\n}\n","/**\n * MCP 服务表单字段配置\n * 定义所有表单字段的 UI 属性和验证规则\n */\n\nimport type { mcpFormSchema } from \"@/schemas/mcp-form\";\nimport type { FieldConfig } from \"@/types/form-config\";\nimport type { UseFormReturn } from \"react-hook-form\";\nimport type { z } from \"zod\";\n\n/**\n * MCP 服务表单字段配置数组\n * 使用类型断言处理 discriminated union 的类型推断限制\n */\nexport const mcpFormFields = [\n {\n name: \"type\",\n type: \"select\",\n label: \"MCP 类型\",\n required: true,\n placeholder: \"选择 MCP 类型\",\n description: (form: UseFormReturn<z.infer<typeof mcpFormSchema>>) => {\n const mcpType = form.watch(\"type\");\n if (mcpType === \"stdio\") return \"本地通过命令行启动的 MCP 服务\";\n if (mcpType === \"http\") return \"通过 HTTP 协议访问的远程 MCP 服务\";\n if (mcpType === \"sse\") return \"通过 SSE 协议访问的远程 MCP 服务\";\n return \"\";\n },\n options: [\n { value: \"stdio\", label: \"本地进程 (stdio)\" },\n { value: \"http\", label: \"远程服务 (HTTP)\" },\n { value: \"sse\", label: \"服务器推送 (SSE)\" },\n ],\n },\n {\n name: \"name\",\n type: \"text\",\n label: \"MCP 名称\",\n required: true,\n placeholder: \"例如: xxx-mcp\",\n description: \"服务的唯一标识符,建议使用小写字母和连字符\",\n },\n {\n name: \"command\",\n type: \"text\",\n label: \"启动命令\",\n required: true,\n placeholder: \"例如: npx -y xxx\",\n description: \"完整的启动命令,空格分隔命令和参数\",\n condition: (form: UseFormReturn<z.infer<typeof mcpFormSchema>>) =>\n form.watch(\"type\") === \"stdio\",\n },\n {\n name: \"env\",\n type: \"textarea\",\n label: \"环境变量 (可选)\",\n placeholder: \"KEY1=value1\\nKEY2=value2\",\n description: \"每行一个环境变量,支持 KEY=value 或 KEY: value 格式\",\n className: \"min-h-[100px] font-mono text-sm\",\n condition: (form: UseFormReturn<z.infer<typeof mcpFormSchema>>) =>\n form.watch(\"type\") === \"stdio\",\n },\n {\n name: \"url\",\n type: \"text\",\n label: \"服务地址\",\n required: true,\n inputType: \"url\",\n placeholder: \"https://example.com/mcp\",\n description: \"MCP 服务的完整 URL 地址\",\n condition: (form: UseFormReturn<z.infer<typeof mcpFormSchema>>) => {\n const type = form.watch(\"type\");\n return type === \"http\" || type === \"sse\";\n },\n },\n {\n name: \"headers\",\n type: \"textarea\",\n label: \"请求头 (可选)\",\n placeholder:\n \"Authorization: Bearer your-key\\nContent-Type: application/json\",\n description: \"每行一个请求头,格式: Header-Name: value\",\n className: \"min-h-[100px] font-mono text-sm\",\n condition: (form: UseFormReturn<z.infer<typeof mcpFormSchema>>) => {\n const type = form.watch(\"type\");\n return type === \"http\" || type === \"sse\";\n },\n },\n] as FieldConfig<typeof mcpFormSchema>[];\n","/**\n * MCP 服务表单验证 Schema\n * 使用 Zod 进行表单验证\n */\n\nimport { z } from \"zod\";\n\n/**\n * 字符串或空字符串(用于可选的文本输入)\n */\nconst optionalStringSchema = z.string().trim();\n\n/**\n * MCP 名称验证规则\n */\nconst nameSchema = z\n .string()\n .min(1, \"MCP 名称不能为空\")\n .regex(\n /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/,\n \"MCP 名称只能包含小写字母、数字和连字符,且必须以字母或数字开头和结尾\"\n );\n\n/**\n * stdio 类型表单验证 Schema\n */\nexport const stdioFormSchema = z.object({\n type: z.literal(\"stdio\"),\n name: nameSchema,\n command: z.string().min(1, \"启动命令不能为空\"),\n env: optionalStringSchema,\n});\n\n/**\n * http 类型表单验证 Schema\n */\nexport const httpFormSchema = z.object({\n type: z.literal(\"http\"),\n name: nameSchema,\n url: z.string().url(\"请输入有效的 URL 地址\"),\n headers: optionalStringSchema,\n});\n\n/**\n * sse 类型表单验证 Schema\n */\nexport const sseFormSchema = z.object({\n type: z.literal(\"sse\"),\n name: nameSchema,\n url: z.string().url(\"请输入有效的 URL 地址\"),\n headers: optionalStringSchema,\n});\n\n/**\n * MCP 服务表单联合验证 Schema\n * 使用 discriminated union 确保类型安全\n */\nexport const mcpFormSchema = z.discriminatedUnion(\"type\", [\n stdioFormSchema,\n httpFormSchema,\n sseFormSchema,\n]);\n\n/**\n * 类型推断\n */\nexport type McpFormSchema = z.infer<typeof mcpFormSchema>;\n","/**\n * MCP 服务表单组件\n * 用于可视化添加 MCP 服务配置\n * 采用配置驱动的方式自动生成表单字段\n */\n\nimport {\n renderFormField,\n renderSelectFieldWithHandler,\n} from \"@/components/form-fields\";\nimport { Button } from \"@/components/ui/button\";\nimport { Form } from \"@/components/ui/form\";\nimport { mcpFormFields } from \"@/config/mcp-form-fields\";\nimport { mcpFormSchema } from \"@/schemas/mcp-form\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { type UseFormReturn, useForm } from \"react-hook-form\";\nimport type { z } from \"zod\";\n\ninterface McpServerFormProps {\n /** 表单实例(可选,如果不提供则内部创建) */\n form?: UseFormReturn<z.infer<typeof mcpFormSchema>>;\n /** 表单默认值 */\n defaultValues?: z.infer<typeof mcpFormSchema>;\n /** 表单提交回调 */\n onSubmit: (values: z.infer<typeof mcpFormSchema>) => void;\n /** 是否禁用表单 */\n disabled?: boolean;\n /** 提交按钮文本 */\n submitText?: string;\n}\n\nexport function McpServerForm({\n form: externalForm,\n defaultValues,\n onSubmit,\n disabled = false,\n submitText = \"保存配置\",\n}: McpServerFormProps) {\n // 使用外部传入的 form 实例,或者内部创建\n const form: UseFormReturn<z.infer<typeof mcpFormSchema>> =\n externalForm ||\n useForm<z.infer<typeof mcpFormSchema>>({\n resolver: zodResolver(mcpFormSchema),\n defaultValues: defaultValues || {\n type: \"stdio\",\n name: \"\",\n command: \"\",\n env: \"\",\n },\n });\n\n // 自定义逻辑:切换类型时重置字段\n const handleTypeChange = (value: \"stdio\" | \"http\" | \"sse\") => {\n form.setValue(\"type\", value);\n // 切换类型时重置部分字段\n // 使用类型断言处理不同类型下的字段差异\n const anyForm = form as unknown as UseFormReturn<Record<string, unknown>>;\n if (value === \"stdio\") {\n anyForm.setValue(\"url\", \"\");\n anyForm.setValue(\"headers\", \"\");\n } else {\n anyForm.setValue(\"command\", \"\");\n anyForm.setValue(\"env\", \"\");\n }\n };\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className=\"space-y-4 py-2\">\n {mcpFormFields.map((config) =>\n config.name === \"type\"\n ? renderSelectFieldWithHandler(\n config,\n form,\n handleTypeChange,\n disabled\n )\n : renderFormField(config, form, disabled)\n )}\n <Button type=\"submit\" className=\"w-full\" disabled={disabled}>\n {submitText}\n </Button>\n </form>\n </Form>\n );\n}\n","\"use client\";\n\nimport * as TabsPrimitive from \"@radix-ui/react-tabs\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst Tabs = TabsPrimitive.Root;\n\nconst TabsList = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.List>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.List\n ref={ref}\n className={cn(\n \"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground\",\n className\n )}\n {...props}\n />\n));\nTabsList.displayName = TabsPrimitive.List.displayName;\n\nconst TabsTrigger = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.Trigger>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.Trigger\n ref={ref}\n className={cn(\n \"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm\",\n className\n )}\n {...props}\n />\n));\nTabsTrigger.displayName = TabsPrimitive.Trigger.displayName;\n\nconst TabsContent = React.forwardRef<\n React.ElementRef<typeof TabsPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>\n>(({ className, ...props }, ref) => (\n <TabsPrimitive.Content\n ref={ref}\n className={cn(\n \"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n className\n )}\n {...props}\n />\n));\nTabsContent.displayName = TabsPrimitive.Content.displayName;\n\nexport { Tabs, TabsList, TabsTrigger, TabsContent };\n","/**\n * MCP 服务表单数据与 API 配置的双向转换工具\n */\n\nimport type {\n HttpFormData,\n McpServerFormData,\n McpServiceType,\n SseFormData,\n StdioFormData,\n} from \"@/types/mcp-form\";\nimport type { MCPServerConfig } from \"@xiaozhi-client/shared-types\";\n\n// ============ 表单 → API 配置 ============\n\n/**\n * 解析命令字符串为 {command, args} 结构\n * 支持带引号的参数,可以处理包含空格的路径\n * @example \"npx -y @z_ai/mcp-server\" → {command: \"npx\", args: [\"-y\", \"@z_ai/mcp-server\"]}\n * @example \"/usr/local/bin/mcp --config /etc/mcp.json\" → {command: \"/usr/local/bin/mcp\", args: [\"--config\", \"/etc/mcp.json\"]}\n * @example \"node \\\"/path with spaces/server.js\\\" --arg\" → {command: \"node\", args: [\"/path with spaces/server.js\", \"--arg\"]}\n */\nexport function parseCommandString(commandStr: string): {\n command: string;\n args: string[];\n} {\n const trimmed = commandStr.trim();\n if (!trimmed) {\n throw new Error(\"命令不能为空\");\n }\n\n const result: string[] = [];\n let current = \"\";\n let inQuote = false;\n let quoteChar = \"\";\n\n for (let i = 0; i < trimmed.length; i++) {\n const char = trimmed[i];\n\n // 处理引号(支持单引号和双引号)\n if (\n (char === '\"' || char === \"'\") &&\n (i === 0 || trimmed[i - 1] !== \"\\\\\")\n ) {\n if (inQuote && char === quoteChar) {\n // 结束引号\n inQuote = false;\n quoteChar = \"\";\n } else if (!inQuote) {\n // 开始引号\n inQuote = true;\n quoteChar = char;\n } else {\n // 引号内不同类型的引号,作为普通字符\n current += char;\n }\n } else if (char === \" \" && !inQuote) {\n // 空格且不在引号内,分割参数\n if (current) {\n result.push(current);\n current = \"\";\n }\n } else {\n current += char;\n }\n }\n\n // 添加最后一个参数\n if (current) {\n result.push(current);\n }\n\n if (result.length === 0) {\n throw new Error(\"命令不能为空\");\n }\n\n return { command: result[0], args: result.slice(1) };\n}\n\n/**\n * 去除字符串两端的配对引号(单引号或双引号)\n * @example '\"hello\"' → 'hello'\n * @example \"'world'\" → 'world'\n * @example '\"unclosed' → '\"unclosed' (不匹配,不处理)\n */\nfunction stripQuotes(str: string): string {\n if (str.length >= 2) {\n const firstChar = str[0];\n const lastChar = str[str.length - 1];\n if (\n (firstChar === '\"' && lastChar === '\"') ||\n (firstChar === \"'\" && lastChar === \"'\")\n ) {\n return str.slice(1, -1);\n }\n }\n return str;\n}\n\n/**\n * 解析多行键值对文本为对象\n * 支持格式: KEY=value 或 KEY: value\n * 支持注释行 (# 开头)\n * 支持值使用引号包裹(引号会被自动去除)\n * @example\n * ACCESS_TOKEN=xxx\n * BASE_URL=yyy\n * # 这是注释\n * ANOTHER: value\n * PATH=\"C:\\Program Files\\app\"\n * → {ACCESS_TOKEN: \"xxx\", BASE_URL: \"yyy\", ANOTHER: \"value\", PATH: \"C:\\Program Files\\app\"}\n */\nexport function parseKeyValuePairs(text: string): Record<string, string> {\n const result: Record<string, string> = {};\n\n for (const line of text.split(\"\\n\")) {\n const trimmed = line.trim();\n // 跳过空行和注释\n if (!trimmed || trimmed.startsWith(\"#\")) {\n continue;\n }\n\n // 尝试 KEY=value 格式\n const eqIndex = trimmed.indexOf(\"=\");\n if (eqIndex > 0) {\n const key = trimmed.slice(0, eqIndex).trim();\n let value = trimmed.slice(eqIndex + 1).trim();\n // 去除值两端的配对引号\n value = stripQuotes(value);\n result[key] = value;\n continue;\n }\n\n // 尝试 KEY: value 格式\n const colonIndex = trimmed.indexOf(\":\");\n if (colonIndex > 0) {\n const key = trimmed.slice(0, colonIndex).trim();\n let value = trimmed.slice(colonIndex + 1).trim();\n // 去除值两端的配对引号\n value = stripQuotes(value);\n result[key] = value;\n }\n }\n\n return result;\n}\n\n/**\n * 将 stdio 类型表单数据转换为 API 配置\n */\nfunction stdioFormToApiConfig(formData: StdioFormData): MCPServerConfig {\n const { command, args } = parseCommandString(formData.command);\n const env = parseKeyValuePairs(formData.env);\n\n const config: MCPServerConfig = {\n command,\n args,\n };\n\n // 只有在有环境变量时才添加 env 字段\n if (Object.keys(env).length > 0) {\n config.env = env;\n }\n\n return config;\n}\n\n/**\n * 将 http 类型表单数据转换为 API 配置\n */\nfunction httpFormToApiConfig(formData: HttpFormData): MCPServerConfig {\n const headers = parseKeyValuePairs(formData.headers);\n\n const config: MCPServerConfig = {\n type: \"streamable-http\", // http 类型转换为 streamable-http\n url: formData.url,\n };\n\n // 只有在有请求头时才添加 headers 字段\n if (Object.keys(headers).length > 0) {\n config.headers = headers;\n }\n\n return config;\n}\n\n/**\n * 将 sse 类型表单数据转换为 API 配置\n */\nfunction sseFormToApiConfig(formData: SseFormData): MCPServerConfig {\n const headers = parseKeyValuePairs(formData.headers);\n\n const config: MCPServerConfig = {\n type: \"sse\",\n url: formData.url,\n };\n\n // 只有在有请求头时才添加 headers 字段\n if (Object.keys(headers).length > 0) {\n config.headers = headers;\n }\n\n return config;\n}\n\n/**\n * 将表单数据转换为 API 配置\n * @returns 包含服务名称和配置的对象\n */\nexport function formToApiConfig(formData: McpServerFormData): {\n name: string;\n config: MCPServerConfig;\n} {\n const { name, type } = formData;\n\n let config: MCPServerConfig;\n switch (type) {\n case \"stdio\":\n config = stdioFormToApiConfig(formData);\n break;\n case \"http\":\n config = httpFormToApiConfig(formData);\n break;\n case \"sse\":\n config = sseFormToApiConfig(formData);\n break;\n default: {\n // TypeScript 的类型保护确保这里永远不会到达\n throw new Error(\"不支持的 MCP 类型\");\n }\n }\n\n return { name, config };\n}\n\n// ============ API 配置 → 表单 ============\n\n/**\n * 从 API 配置推断 MCP 服务类型\n */\nexport function inferMcpType(config: MCPServerConfig): McpServiceType {\n if (\"command\" in config) {\n return \"stdio\";\n }\n if (\"type\" in config && config.type === \"sse\") {\n return \"sse\";\n }\n // 默认为 http (streamable-http)\n return \"http\";\n}\n\n/**\n * 将 {command, args} 结构转换为命令字符串\n */\nfunction buildCommandString(config: {\n command: string;\n args?: string[];\n}): string {\n const parts = [config.command];\n if (config.args && config.args.length > 0) {\n parts.push(...config.args);\n }\n return parts.join(\" \");\n}\n\n/**\n * 将键值对对象转换为多行文本 (KEY: value 格式)\n */\nexport function keyValuePairsToMultilineText(\n obj: Record<string, string> | undefined\n): string {\n if (!obj || Object.keys(obj).length === 0) {\n return \"\";\n }\n return Object.entries(obj)\n .map(([key, value]) => `${key}: ${value}`)\n .join(\"\\n\");\n}\n\n/**\n * 将 stdio 类型 API 配置转换为表单数据\n */\nfunction stdioApiConfigToForm(\n name: string,\n config: MCPServerConfig\n): StdioFormData {\n const stdioConfig = config as {\n command: string;\n args?: string[];\n env?: Record<string, string>;\n };\n return {\n type: \"stdio\",\n name,\n command: buildCommandString(stdioConfig),\n env: keyValuePairsToMultilineText(stdioConfig.env),\n };\n}\n\n/**\n * 将 http 类型 API 配置转换为表单数据\n */\nfunction httpApiConfigToForm(\n name: string,\n config: MCPServerConfig\n): HttpFormData {\n const httpConfig = config as {\n type?: string;\n url: string;\n headers?: Record<string, string>;\n };\n return {\n type: \"http\",\n name,\n url: httpConfig.url,\n headers: keyValuePairsToMultilineText(httpConfig.headers),\n };\n}\n\n/**\n * 将 sse 类型 API 配置转换为表单数据\n */\nfunction sseApiConfigToForm(\n name: string,\n config: MCPServerConfig\n): SseFormData {\n const sseConfig = config as {\n type: string;\n url: string;\n headers?: Record<string, string>;\n };\n return {\n type: \"sse\",\n name,\n url: sseConfig.url,\n headers: keyValuePairsToMultilineText(sseConfig.headers),\n };\n}\n\n/**\n * 将 API 配置转换为表单数据\n */\nexport function apiConfigToForm(\n name: string,\n config: MCPServerConfig\n): McpServerFormData {\n const type = inferMcpType(config);\n\n switch (type) {\n case \"stdio\":\n return stdioApiConfigToForm(name, config);\n case \"http\":\n return httpApiConfigToForm(name, config);\n case \"sse\":\n return sseApiConfigToForm(name, config);\n default: {\n // TypeScript 的类型保护确保这里永远不会到达\n throw new Error(\"无法识别的 MCP 类型\");\n }\n }\n}\n\n/**\n * 验证 JSON 并转换为表单数据\n * 用于高级模式 → 表单模式的数据转换\n * @returns 转换后的表单数据,如果解析失败则返回 null\n */\nexport function jsonToFormData(jsonString: string): McpServerFormData | null {\n try {\n const parsed = JSON.parse(jsonString);\n\n // 尝试解析为包含 mcpServers 的格式\n if (parsed.mcpServers && typeof parsed.mcpServers === \"object\") {\n const entries = Object.entries(parsed.mcpServers);\n if (entries.length === 0) {\n return null;\n }\n // 取第一个服务配置\n const [name, config] = entries[0];\n return apiConfigToForm(name, config as MCPServerConfig);\n }\n\n // 尝试解析为单个服务配置\n // 检查是否是有效的 MCP 配置(有 command、type 或 url 字段)\n if (\n typeof parsed === \"object\" &&\n parsed !== null &&\n (\"command\" in parsed || \"type\" in parsed || \"url\" in parsed)\n ) {\n // 生成默认名称\n let defaultName: string;\n if (\"command\" in parsed && parsed.command) {\n defaultName = parsed.command.split(\"/\").pop() || \"mcp-server\";\n } else if (\"type\" in parsed && parsed.type === \"sse\") {\n defaultName = \"sse-server\";\n } else {\n defaultName = \"http-server\";\n }\n return apiConfigToForm(defaultName, parsed as MCPServerConfig);\n }\n\n return null;\n } catch (error) {\n // 仅在非生产环境下输出详细错误日志\n if (\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\"\n ) {\n console.error(\"[jsonToFormData] JSON 解析或转换失败:\", error);\n }\n return null;\n }\n}\n\n/**\n * 将表单数据转换为 JSON 字符串\n * 用于表单模式 → 高级模式的数据转换\n */\nexport function formToJson(formData: McpServerFormData): string {\n const { name, config } = formToApiConfig(formData);\n return JSON.stringify(\n {\n mcpServers: {\n [name]: config,\n },\n },\n null,\n 2\n );\n}\n","/**\n * MCP 服务配置验证工具\n * 用于验证高级模式下的 JSON 配置\n */\n\nimport type { MCPServerConfig } from \"@xiaozhi-client/shared-types\";\n\n// 验证结果接口\nexport interface ValidationResult {\n success: boolean;\n data?: Record<string, MCPServerConfig>;\n error?: string;\n}\n\n/**\n * 验证单个 MCP 服务配置\n * @param serverName - 服务名称\n * @param serverConfig - 服务配置对象\n * @returns 验证结果,包含是否有效和错误信息\n */\nexport function validateSingleServerConfig(\n serverName: string,\n serverConfig: unknown\n): { valid: boolean; error?: string } {\n if (!serverConfig || typeof serverConfig !== \"object\") {\n return {\n valid: false,\n error: `服务 \"${serverName}\" 的配置必须是一个对象`,\n };\n }\n\n const config = serverConfig as Record<string, unknown>;\n\n // 先进行基本字段检查\n const hasCommand = \"command\" in config;\n const hasType = \"type\" in config;\n const hasUrl = \"url\" in config;\n\n // 判断配置类型并验证相应字段\n if (hasCommand) {\n // stdio 类型\n if (!config.command || typeof config.command !== \"string\") {\n return {\n valid: false,\n error: `服务 \"${serverName}\" 缺少必需的 command 字段或字段类型不正确`,\n };\n }\n if (!Array.isArray(config.args)) {\n return {\n valid: false,\n error: `服务 \"${serverName}\" 的 args 字段必须是数组`,\n };\n }\n } else if (hasType && config.type === \"sse\") {\n // sse 类型\n if (!config.url || typeof config.url !== \"string\") {\n return {\n valid: false,\n error: `服务 \"${serverName}\" 缺少必需的 url 字段或字段类型不正确`,\n };\n }\n } else if (hasUrl) {\n // streamable-http 类型\n if (!config.url || typeof config.url !== \"string\") {\n return {\n valid: false,\n error: `服务 \"${serverName}\" 缺少必需的 url 字段或字段类型不正确`,\n };\n }\n } else {\n // 无法识别的配置类型\n return {\n valid: false,\n error: `服务 \"${serverName}\" 的配置无效: 必须包含 command 字段(stdio)、type: 'sse' 字段(sse)或 url 字段(streamable-http)`,\n };\n }\n\n return { valid: true };\n}\n\n/**\n * 验证 MCP 配置的函数(用于高级模式)\n * @param input - JSON 字符串格式的配置\n * @returns 验证结果,包含是否成功、解析后的数据和错误信息\n */\nexport function validateMCPConfig(input: string): ValidationResult {\n try {\n const trimmed = input.trim();\n if (!trimmed) {\n return { success: false, error: \"配置不能为空\" };\n }\n\n const parsed = JSON.parse(trimmed);\n\n let mcpServers: Record<string, unknown>;\n\n // 检查是否包含 mcpServers 层\n if (parsed.mcpServers && typeof parsed.mcpServers === \"object\") {\n mcpServers = parsed.mcpServers as Record<string, unknown>;\n } else if (typeof parsed === \"object\" && !Array.isArray(parsed)) {\n // 检查是否是单个服务配置\n const hasCommand = \"command\" in parsed;\n const hasType = \"type\" in parsed;\n const hasUrl = \"url\" in parsed;\n\n if (hasCommand || hasType || hasUrl) {\n // 是单个服务配置,生成默认名称\n const defaultName = hasCommand\n ? String(parsed.command).split(\"/\").pop() || \"mcp-server\"\n : hasType && parsed.type === \"sse\"\n ? \"sse-server\"\n : \"http-server\";\n mcpServers = { [defaultName]: parsed };\n } else {\n return { success: false, error: \"配置格式错误: 必须是对象格式\" };\n }\n } else {\n return { success: false, error: \"配置格式错误: 必须是对象格式\" };\n }\n\n // 验证每个服务配置\n for (const [serverName, serverConfig] of Object.entries(mcpServers)) {\n const validation = validateSingleServerConfig(serverName, serverConfig);\n if (!validation.valid) {\n return { success: false, error: validation.error };\n }\n }\n\n return {\n success: true,\n data: mcpServers as Record<string, MCPServerConfig>,\n };\n } catch (error) {\n return {\n success: false,\n error: `JSON 格式错误: ${\n error instanceof Error ? error.message : \"无法解析 JSON\"\n }`,\n };\n }\n}\n","import { McpServerForm } from \"@/components/mcp-server-form\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from \"@/components/ui/dialog\";\nimport {\n Form,\n FormControl,\n FormField,\n FormItem,\n FormMessage,\n} from \"@/components/ui/form\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"@/components/ui/tabs\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { mcpFormSchema } from \"@/schemas/mcp-form\";\nimport { apiClient } from \"@/services/api\";\nimport {\n formToApiConfig,\n formToJson,\n jsonToFormData,\n} from \"@/utils/mcpFormConverter\";\nimport { validateMCPConfig } from \"@/utils/mcpValidation\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport { PlusIcon } from \"lucide-react\";\nimport { useCallback, useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport { toast } from \"sonner\";\nimport z from \"zod\";\n\n// 高级模式的 JSON 表单 schema\nconst jsonFormSchema = z.object({\n config: z.string().min(2, {\n message: \"配置不能为空\",\n }),\n});\n\nexport function AddMcpServerButton() {\n const [open, setOpen] = useState(false);\n const [isLoading, setIsLoading] = useState(false);\n const [inputMode, setInputMode] = useState<\"form\" | \"json\">(\"form\");\n const [jsonInput, setJsonInput] = useState<string>(\"\");\n\n // 表单模式的表单实例\n const form = useForm<z.infer<typeof mcpFormSchema>>({\n resolver: zodResolver(mcpFormSchema),\n defaultValues: {\n type: \"stdio\",\n name: \"\",\n command: \"\",\n env: \"\",\n },\n });\n\n // 高级模式的表单实例(保持原有验证逻辑)\n const advancedForm = useForm<z.infer<typeof jsonFormSchema>>({\n resolver: zodResolver(jsonFormSchema),\n defaultValues: {\n config: \"\",\n },\n });\n\n // 当弹窗关闭时重置状态\n const handleOpenChange = useCallback(\n (newOpen: boolean) => {\n if (!newOpen) {\n // 重置表单和状态\n form.reset();\n advancedForm.reset();\n setJsonInput(\"\");\n setInputMode(\"form\");\n }\n setOpen(newOpen);\n },\n [form, advancedForm]\n );\n\n // 处理模式切换\n const handleModeChange = useCallback(\n (newMode: string) => {\n if (newMode !== \"form\" && newMode !== \"json\") {\n return; // 忽略无效值\n }\n\n if (newMode === \"json\" && inputMode === \"form\") {\n // 表单 → JSON\n const formValues = form.getValues();\n try {\n setJsonInput(formToJson(formValues));\n } catch {\n setJsonInput(\"\");\n }\n } else if (newMode === \"form\" && inputMode === \"json\") {\n // JSON → 表单\n const formData = jsonToFormData(jsonInput);\n if (formData) {\n form.reset(formData);\n }\n }\n setInputMode(newMode);\n },\n [inputMode, form, jsonInput]\n );\n\n // 表单模式提交处理\n const handleFormSubmit = useCallback(\n async (values: z.infer<typeof mcpFormSchema>) => {\n setIsLoading(true);\n try {\n // 转换表单数据为 API 配置\n const { name, config } = formToApiConfig(values);\n\n // 检查重名\n const existingServers = await apiClient.listMCPServers();\n if (existingServers.servers.some((server) => server.name === name)) {\n toast.error(`服务名称 \"${name}\" 已存在`);\n return;\n }\n\n // 调用API添加服务器\n const result = await apiClient.addMCPServer(name, config);\n if (!result) {\n throw new Error(\"添加服务器失败\");\n }\n\n toast.success(`已添加 MCP 服务 \"${name}\"`);\n\n // 重置表单并关闭对话框\n form.reset();\n setOpen(false);\n } catch (error) {\n console.error(\"更新配置失败:\", error);\n toast.error(error instanceof Error ? error.message : \"更新配置失败\");\n } finally {\n setIsLoading(false);\n }\n },\n [form]\n );\n\n // 高级模式提交处理\n const handleAdvancedSubmit = useCallback(\n async (values: z.infer<typeof jsonFormSchema>) => {\n setIsLoading(true);\n try {\n // 验证用户输入的配置\n const validation = validateMCPConfig(values.config);\n\n if (!validation.success) {\n toast.error(validation.error || \"配置验证失败\");\n return;\n }\n\n const parsedServers = validation.data!;\n\n // 检查重名\n const existingServers = await apiClient.listMCPServers();\n const existingNames = Object.keys(parsedServers).filter((name) =>\n existingServers.servers.some((server) => server.name === name)\n );\n if (existingNames.length > 0) {\n toast.error(\n `服务名称冲突: 以下服务已存在: ${existingNames.join(\", \")}`\n );\n return;\n }\n\n // 调用API添加服务器\n for (const [serverName, serverConfig] of Object.entries(\n parsedServers\n )) {\n const result = await apiClient.addMCPServer(serverName, serverConfig);\n if (!result) {\n throw new Error(\"添加服务器失败\");\n }\n }\n\n // 成功反馈\n const addedCount = Object.keys(parsedServers).length;\n toast.success(\n addedCount === 1\n ? `已添加 MCP 服务 \"${Object.keys(parsedServers)[0]}\"`\n : `已添加 ${addedCount} 个 MCP 服务`\n );\n\n // 重置表单并关闭对话框\n advancedForm.reset();\n setOpen(false);\n } catch (error) {\n console.error(\"更新配置失败:\", error);\n toast.error(error instanceof Error ? error.message : \"更新配置失败\");\n } finally {\n setIsLoading(false);\n }\n },\n [advancedForm]\n );\n\n return (\n <Dialog open={open} onOpenChange={handleOpenChange}>\n <DialogTrigger asChild>\n <Button\n variant=\"secondary\"\n size=\"icon\"\n className=\"size-8\"\n aria-label=\"添加MCP服务\"\n title=\"添加MCP服务\"\n >\n <PlusIcon className=\"size-4\" />\n </Button>\n </DialogTrigger>\n <DialogContent className=\"sm:max-w-[600px]\">\n <DialogHeader className=\"mb-4\">\n <DialogTitle>添加MCP服务</DialogTitle>\n <DialogDescription>添加后,需要重启服务才会生效。</DialogDescription>\n </DialogHeader>\n\n {/* 模式切换 */}\n <Tabs value={inputMode} onValueChange={handleModeChange}>\n <TabsList className=\"grid w-full grid-cols-2\">\n <TabsTrigger value=\"form\">表单模式</TabsTrigger>\n <TabsTrigger value=\"json\">高级模式</TabsTrigger>\n </TabsList>\n\n {/* 表单模式 */}\n <TabsContent value=\"form\" className=\"mt-4\">\n <McpServerForm\n form={form}\n onSubmit={handleFormSubmit}\n disabled={isLoading}\n submitText={isLoading ? \"保存中...\" : \"保存配置\"}\n />\n </TabsContent>\n\n {/* 高级模式 */}\n <TabsContent value=\"json\" className=\"mt-4\">\n <Form {...advancedForm}>\n <form onSubmit={advancedForm.handleSubmit(handleAdvancedSubmit)}>\n <div className=\"grid gap-4\">\n <FormField\n control={advancedForm.control}\n name=\"config\"\n render={({ field }) => (\n <FormItem>\n <FormControl>\n <Textarea\n className=\"resize-none h-[300px] font-mono text-sm\"\n disabled={isLoading}\n placeholder={`支持三种通信方式:\n\n1. 本地进程 (stdio):\n{\n \"mcpServers\": {\n \"local-server\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@example/mcp-server\"]\n }\n }\n}\n\n2. 服务器推送 (SSE):\n{\n \"mcpServers\": {\n \"sse-server\": {\n \"type\": \"sse\",\n \"url\": \"https://example.com/sse\"\n }\n }\n}\n\n3. 流式 HTTP:\n{\n \"mcpServers\": {\n \"http-server\": {\n \"url\": \"https://example.com/mcp\"\n }\n }\n}`}\n {...field}\n onChange={(event) => {\n field.onChange(event);\n setJsonInput(event.target.value);\n }}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n </div>\n <DialogFooter className=\"mt-4\">\n <DialogClose asChild>\n <Button variant=\"outline\" disabled={isLoading}>\n 取消\n </Button>\n </DialogClose>\n <Button type=\"submit\" disabled={isLoading}>\n {isLoading ? \"保存中...\" : \"保存\"}\n </Button>\n </DialogFooter>\n </form>\n </Form>\n </TabsContent>\n </Tabs>\n </DialogContent>\n </Dialog>\n );\n}\n","import * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport { type VariantProps, cva } from \"class-variance-authority\";\n\n// Context 类型定义\ninterface TableSizeContextValue {\n size: \"default\" | \"compact\";\n}\n\nconst TableSizeContext = React.createContext<TableSizeContextValue>({\n size: \"default\",\n});\n\n// Table 变体\nconst tableVariants = cva(\"w-full caption-bottom text-sm\", {\n variants: {\n size: {\n default: \"text-sm\",\n compact: \"text-xs\",\n },\n },\n defaultVariants: {\n size: \"default\",\n },\n});\n\n// TableHead 变体\nconst tableHeadVariants = cva(\n \"text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0\",\n {\n variants: {\n size: {\n default: \"h-12 px-4\",\n compact: \"h-10 p-4\",\n },\n },\n defaultVariants: {\n size: \"default\",\n },\n }\n);\n\n// TableCell 变体\nconst tableCellVariants = cva(\"align-middle [&:has([role=checkbox])]:pr-0\", {\n variants: {\n size: {\n default: \"p-4\",\n compact: \"py-2 px-4\",\n },\n },\n defaultVariants: {\n size: \"default\",\n },\n});\n\n// 类型导出\nexport type TableProps = React.HTMLAttributes<HTMLTableElement> &\n VariantProps<typeof tableVariants>;\n\nexport type TableHeadProps = React.ThHTMLAttributes<HTMLTableCellElement>;\n\nexport type TableCellProps = React.TdHTMLAttributes<HTMLTableCellElement>;\n\nconst Table = React.forwardRef<HTMLTableElement, TableProps>(\n ({ className, size, children, ...props }, ref) => {\n const tableSize = size ?? \"default\";\n return (\n <TableSizeContext.Provider value={{ size: tableSize }}>\n <div className=\"relative w-full overflow-auto\">\n <table\n ref={ref}\n className={cn(tableVariants({ size: tableSize, className }))}\n {...props}\n >\n {children}\n </table>\n </div>\n </TableSizeContext.Provider>\n );\n }\n);\nTable.displayName = \"Table\";\n\nconst TableHeader = React.forwardRef<\n HTMLTableSectionElement,\n React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n <thead ref={ref} className={cn(\"[&_tr]:border-b\", className)} {...props} />\n));\nTableHeader.displayName = \"TableHeader\";\n\nconst TableBody = React.forwardRef<\n HTMLTableSectionElement,\n React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n <tbody\n ref={ref}\n className={cn(\"[&_tr:last-child]:border-0\", className)}\n {...props}\n />\n));\nTableBody.displayName = \"TableBody\";\n\nconst TableFooter = React.forwardRef<\n HTMLTableSectionElement,\n React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n <tfoot\n ref={ref}\n className={cn(\n \"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0\",\n className\n )}\n {...props}\n />\n));\nTableFooter.displayName = \"TableFooter\";\n\nconst TableRow = React.forwardRef<\n HTMLTableRowElement,\n React.HTMLAttributes<HTMLTableRowElement>\n>(({ className, ...props }, ref) => (\n <tr\n ref={ref}\n className={cn(\n \"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted\",\n className\n )}\n {...props}\n />\n));\nTableRow.displayName = \"TableRow\";\n\nconst TableHead = React.forwardRef<HTMLTableCellElement, TableHeadProps>(\n ({ className, ...props }, ref) => {\n const { size } = React.useContext(TableSizeContext);\n return (\n <th\n ref={ref}\n className={cn(tableHeadVariants({ size, className }))}\n {...props}\n />\n );\n }\n);\nTableHead.displayName = \"TableHead\";\n\nconst TableCell = React.forwardRef<HTMLTableCellElement, TableCellProps>(\n ({ className, ...props }, ref) => {\n const { size } = React.useContext(TableSizeContext);\n return (\n <td\n ref={ref}\n className={cn(tableCellVariants({ size, className }))}\n {...props}\n />\n );\n }\n);\nTableCell.displayName = \"TableCell\";\n\nconst TableCaption = React.forwardRef<\n HTMLTableCaptionElement,\n React.HTMLAttributes<HTMLTableCaptionElement>\n>(({ className, ...props }, ref) => (\n <caption\n ref={ref}\n className={cn(\"mt-4 text-sm text-muted-foreground\", className)}\n {...props}\n />\n));\nTableCaption.displayName = \"TableCaption\";\n\nexport {\n Table,\n TableHeader,\n TableBody,\n TableFooter,\n TableHead,\n TableRow,\n TableCell,\n TableCaption,\n};\n","/**\n * 服务器搜索 Hook\n *\n * 提供服务器列表搜索和筛选功能,支持根据通信类型和关键词进行筛选\n */\n\nimport type { ServerRowData } from \"@/components/mcp-server/mcp-server-table\";\nimport { useCallback, useMemo, useState } from \"react\";\n\ninterface UseServerSearchResult {\n /** 搜索关键词 */\n searchValue: string;\n /** 设置搜索关键词 */\n setSearchValue: (value: string) => void;\n /** 筛选后的服务器列表 */\n filteredServers: ServerRowData[];\n /** 清除搜索 */\n clearSearch: () => void;\n}\n\n/** 通信类型显示名称映射 */\nconst COMMUNICATION_TYPE_LABELS: Record<\n ServerRowData[\"communicationType\"],\n string\n> = {\n stdio: \"本地进程\",\n sse: \"服务器推送\",\n \"streamable-http\": \"流式 HTTP\",\n};\n\n/**\n * 获取通信类型的显示名称\n */\nfunction getCommunicationTypeLabel(\n type: ServerRowData[\"communicationType\"]\n): string {\n return COMMUNICATION_TYPE_LABELS[type];\n}\n\n/**\n * 服务器搜索状态管理 Hook\n * 提供搜索功能,根据服务器名称和通信类型进行筛选\n */\nexport function useServerSearch(\n servers: ServerRowData[]\n): UseServerSearchResult {\n const [searchValue, setSearchValue] = useState(\"\");\n\n // 确保 servers 是有效数组\n const safeServers = Array.isArray(servers) ? servers : [];\n\n // 筛选匹配的服务器\n const filteredServers = useMemo(() => {\n if (!searchValue.trim()) return safeServers;\n const keyword = searchValue.toLowerCase();\n return safeServers.filter((server) => {\n const nameMatch = (server?.name?.toLowerCase() || \"\").includes(keyword);\n const typeMatch = (\n server?.communicationType?.toLowerCase() || \"\"\n ).includes(keyword);\n const typeLabelMatch = server?.communicationType\n ? getCommunicationTypeLabel(server.communicationType)\n .toLowerCase()\n .includes(keyword)\n : false;\n return nameMatch || typeMatch || typeLabelMatch;\n });\n }, [safeServers, searchValue]);\n\n const clearSearch = useCallback(() => {\n setSearchValue(\"\");\n }, []);\n\n return {\n searchValue,\n setSearchValue,\n filteredServers,\n clearSearch,\n };\n}\n","\"use client\";\n\nimport { useEffect, useState } from \"react\";\n\n/**\n * 排序持久化 Hook 的配置选项\n */\ninterface UseSortPersistenceOptions<T extends { field: string }> {\n /** localStorage 存储键 */\n storageKey: string;\n /** 默认排序配置 */\n defaultConfig: T;\n /** 有效的排序字段列表 */\n validFields: string[];\n /** 日志名称,用于调试信息 */\n loggerName: string;\n}\n\n/**\n * 通用的排序配置持久化 Hook\n * 自动将用户选择的排序方式保存到 localStorage\n *\n * @param options - 配置选项\n * @returns 排序配置和设置函数\n *\n * @example\n * ```typescript\n * export function useServerSortPersistence() {\n * return useSortPersistence({\n * storageKey: \"mcp-server-sort-config\",\n * defaultConfig: { field: \"name\" as const },\n * validFields: [\"name\", \"communicationType\", \"toolCount\"],\n * loggerName: \"useServerSortPersistence\",\n * });\n * }\n * ```\n */\nexport function useSortPersistence<T extends { field: string }>(\n options: UseSortPersistenceOptions<T>\n) {\n const { storageKey, defaultConfig, validFields, loggerName } = options;\n\n const [sortConfig, setSortConfig] = useState<T>(() => {\n // 从 localStorage 读取保存的配置\n if (typeof window !== \"undefined\") {\n try {\n const saved = localStorage.getItem(storageKey);\n if (saved) {\n const parsed = JSON.parse(saved) as T;\n // 验证 field 是否有效\n if (parsed && validFields.includes(parsed.field)) {\n return parsed;\n }\n // 无效数据,使用默认配置\n console.warn(`[${loggerName}] 无效的排序字段,使用默认配置`);\n }\n } catch (error) {\n console.warn(`[${loggerName}] 读取排序配置失败:`, error);\n }\n }\n return defaultConfig;\n });\n\n // 当配置变化时,保存到 localStorage\n // biome-ignore lint/correctness/useExhaustiveDependencies: loggerName 是从 options 解构的常量,不会在组件生命周期中改变\n useEffect(() => {\n if (typeof window !== \"undefined\") {\n try {\n localStorage.setItem(storageKey, JSON.stringify(sortConfig));\n } catch (error) {\n console.warn(`[${loggerName}] 保存排序配置失败:`, error);\n }\n }\n }, [sortConfig, storageKey]);\n\n return { sortConfig, setSortConfig };\n}\n","\"use client\";\n\n/**\n * 服务器排序配置持久化 Hook\n *\n * 自动将用户选择的服务器排序方式保存到 localStorage,支持按名称、通信类型、工具数量等字段排序\n */\n\nimport type {\n ServerSortConfig,\n ServerSortField,\n} from \"@/components/mcp-server/server-sort-selector\";\nimport { useSortPersistence } from \"@/hooks/useSortPersistence\";\n\n/** 有效的排序字段列表 */\nconst VALID_SORT_FIELDS: ServerSortField[] = [\n \"name\",\n \"communicationType\",\n \"toolCount\",\n];\n\n/**\n * 服务器排序配置持久化 Hook\n * 自动将用户选择的排序方式保存到 localStorage\n */\nexport function useServerSortPersistence() {\n return useSortPersistence<ServerSortConfig>({\n storageKey: \"mcp-server-sort-config\",\n defaultConfig: { field: \"name\" },\n validFields: VALID_SORT_FIELDS,\n loggerName: \"useServerSortPersistence\",\n });\n}\n","/**\n * 工具分页 Hook\n *\n * 提供工具列表的分页功能,自动计算分页状态和当前页数据\n */\n\nimport type { ToolRowData } from \"@/components/mcp-tool/mcp-tool-table\";\nimport { useCallback, useMemo, useState } from \"react\";\n\ninterface UseToolPaginationResult {\n /** 当前页码 */\n currentPage: number;\n /** 每页显示数量 */\n pageSize: number;\n /** 总页数 */\n totalPages: number;\n /** 当前页的工具列表 */\n paginatedTools: ToolRowData[];\n /** 设置页码 */\n setPage: (page: number) => void;\n /** 设置每页显示数量 */\n setPageSize: (size: number) => void;\n /** 重置到第一页 */\n resetPage: () => void;\n}\n\n/**\n * 工具分页状态管理 Hook\n * 提供客户端分页功能\n *\n * @param tools - 工具列表数据\n * @param initialPageSize - 初始每页显示数量,默认为 10\n */\nexport function useToolPagination(\n tools: ToolRowData[],\n initialPageSize = 10\n): UseToolPaginationResult {\n const [currentPage, setCurrentPage] = useState(1);\n const [pageSize, setPageSize] = useState(initialPageSize);\n\n // 确保 tools 是有效数组\n const safeTools = Array.isArray(tools) ? tools : [];\n\n // 计算总页数\n const totalPages = useMemo(\n () => Math.ceil(safeTools.length / pageSize) || 1,\n [safeTools.length, pageSize]\n );\n\n // 计算当前页的数据\n const paginatedTools = useMemo(() => {\n const startIndex = (currentPage - 1) * pageSize;\n const endIndex = startIndex + pageSize;\n return safeTools.slice(startIndex, endIndex);\n }, [safeTools, currentPage, pageSize]);\n\n // 设置页码,带边界检查\n const setPage = useCallback(\n (page: number) => {\n setCurrentPage(Math.max(1, Math.min(page, totalPages)));\n },\n [totalPages]\n );\n\n // 设置每页显示数量,并重置到第一页\n const handleSetPageSize = useCallback((size: number) => {\n setPageSize(size);\n setCurrentPage(1);\n }, []);\n\n // 重置到第一页\n const resetPage = useCallback(() => {\n setCurrentPage(1);\n }, []);\n\n return {\n currentPage,\n pageSize,\n totalPages,\n paginatedTools,\n setPage,\n setPageSize: handleSetPageSize,\n resetPage,\n };\n}\n","import { McpServerForm } from \"@/components/mcp-server-form\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from \"@/components/ui/dialog\";\nimport {\n Form,\n FormControl,\n FormField,\n FormItem,\n FormMessage,\n} from \"@/components/ui/form\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"@/components/ui/tabs\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { useNetworkServiceActions } from \"@/providers/WebSocketProvider\";\nimport { mcpFormSchema } from \"@/schemas/mcp-form\";\nimport { useConfig } from \"@/stores/config\";\nimport {\n apiConfigToForm,\n formToApiConfig,\n formToJson,\n jsonToFormData,\n} from \"@/utils/mcpFormConverter\";\nimport { validateMCPConfig } from \"@/utils/mcpValidation\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport type { MCPServerConfig } from \"@xiaozhi-client/shared-types\";\nimport { SettingsIcon } from \"lucide-react\";\nimport { useCallback, useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport { toast } from \"sonner\";\nimport z from \"zod\";\n\n// 高级模式的 JSON 表单 schema\nconst jsonFormSchema = z.object({\n config: z.string().min(2, {\n message: \"配置不能为空\",\n }),\n});\n\nexport function McpServerSettingButton({\n mcpServer,\n mcpServerName,\n}: {\n mcpServer: MCPServerConfig;\n mcpServerName: string;\n}) {\n const [open, setOpen] = useState(false);\n const [isLoading, setIsLoading] = useState(false);\n const [inputMode, setInputMode] = useState<\"form\" | \"json\">(\"form\");\n const [jsonInput, setJsonInput] = useState<string>(\"\");\n const config = useConfig();\n const { updateConfig } = useNetworkServiceActions();\n\n // 将现有配置转换为表单数据\n const defaultFormValues = apiConfigToForm(mcpServerName, mcpServer);\n\n // 表单模式的表单实例\n const form = useForm<z.infer<typeof mcpFormSchema>>({\n resolver: zodResolver(mcpFormSchema),\n defaultValues: defaultFormValues,\n });\n\n // 高级模式的表单实例(保持原有验证逻辑)\n const advancedForm = useForm<z.infer<typeof jsonFormSchema>>({\n resolver: zodResolver(jsonFormSchema),\n defaultValues: {\n config: JSON.stringify(\n { mcpServers: { [mcpServerName]: mcpServer } },\n null,\n 2\n ),\n },\n });\n\n // 当弹窗关闭时重置状态\n const handleOpenChange = useCallback(\n (newOpen: boolean) => {\n if (!newOpen) {\n // 重置表单和状态\n form.reset(defaultFormValues);\n advancedForm.reset();\n setJsonInput(\"\");\n setInputMode(\"form\");\n }\n setOpen(newOpen);\n },\n [form, advancedForm, defaultFormValues]\n );\n\n // 处理模式切换\n const handleModeChange = useCallback(\n (newMode: string) => {\n if (newMode !== \"form\" && newMode !== \"json\") {\n return; // 忽略无效值\n }\n\n if (newMode === \"json\" && inputMode === \"form\") {\n // 表单 → JSON\n const formValues = form.getValues();\n try {\n setJsonInput(formToJson(formValues));\n } catch {\n setJsonInput(\"\");\n }\n } else if (newMode === \"form\" && inputMode === \"json\") {\n // JSON → 表单\n const formData = jsonToFormData(jsonInput);\n if (formData) {\n form.reset(formData);\n }\n }\n setInputMode(newMode);\n },\n [inputMode, form, jsonInput]\n );\n\n // 表单模式提交处理\n const handleFormSubmit = useCallback(\n async (values: z.infer<typeof mcpFormSchema>) => {\n if (!config) {\n toast.error(\"配置数据未加载,请稍后重试\");\n return;\n }\n\n setIsLoading(true);\n try {\n // 转换表单数据为 API 配置\n const { name, config: newServerConfig } = formToApiConfig(values);\n\n // 构建更新后的配置\n let updatedConfig = { ...config };\n\n // 如果名称改变,删除旧名称的配置\n if (name !== mcpServerName) {\n const { [mcpServerName]: _removed, ...remainingServers } =\n updatedConfig.mcpServers;\n updatedConfig.mcpServers = remainingServers;\n }\n\n // 添加新名称的配置\n updatedConfig = {\n ...updatedConfig,\n mcpServers: {\n ...updatedConfig.mcpServers,\n [name]: newServerConfig,\n },\n };\n\n await updateConfig(updatedConfig);\n\n const nameChanged = name !== mcpServerName;\n toast.success(\n nameChanged\n ? `MCP 服务 \"${mcpServerName}\" 已重命名为 \"${name}\"`\n : `MCP 服务 \"${name}\" 配置已更新`\n );\n\n setOpen(false);\n } catch (error) {\n console.error(\"更新配置失败:\", error);\n toast.error(error instanceof Error ? error.message : \"更新配置失败\");\n } finally {\n setIsLoading(false);\n }\n },\n [config, mcpServerName, updateConfig]\n );\n\n // 高级模式提交处理\n const handleAdvancedSubmit = useCallback(\n async (values: z.infer<typeof jsonFormSchema>) => {\n if (!config) {\n toast.error(\"配置数据未加载,请稍后重试\");\n return;\n }\n\n setIsLoading(true);\n try {\n // 验证用户输入的配置\n const validation = validateMCPConfig(values.config);\n\n if (!validation.success) {\n toast.error(validation.error || \"配置验证失败\");\n return;\n }\n\n const parsedServers = validation.data!;\n\n // 高级模式只允许编辑单个服务\n const serverEntries = Object.entries(parsedServers);\n if (serverEntries.length !== 1) {\n toast.error(\"编辑模式只能修改单个 MCP 服务配置\");\n return;\n }\n\n const [newName, newServerConfig] = serverEntries[0] as [\n string,\n MCPServerConfig,\n ];\n\n // 构建更新后的配置\n let updatedConfig = { ...config };\n\n // 如果名称改变,删除旧名称的配置\n if (newName !== mcpServerName) {\n const { [mcpServerName]: _removed, ...remainingServers } =\n updatedConfig.mcpServers;\n updatedConfig.mcpServers = remainingServers;\n }\n\n // 添加新名称的配置\n updatedConfig = {\n ...updatedConfig,\n mcpServers: {\n ...updatedConfig.mcpServers,\n [newName]: newServerConfig,\n },\n };\n\n await updateConfig(updatedConfig);\n\n const nameChanged = newName !== mcpServerName;\n toast.success(\n nameChanged\n ? `MCP 服务 \"${mcpServerName}\" 已重命名为 \"${newName}\"`\n : `MCP 服务 \"${newName}\" 配置已更新`\n );\n\n setOpen(false);\n } catch (error) {\n console.error(\"更新配置失败:\", error);\n toast.error(error instanceof Error ? error.message : \"更新配置失败\");\n } finally {\n setIsLoading(false);\n }\n },\n [config, mcpServerName, updateConfig]\n );\n\n return (\n <Dialog open={open} onOpenChange={handleOpenChange}>\n <DialogTrigger asChild>\n <button\n type=\"button\"\n className=\"flex items-center gap-1 hover:cursor-pointer hover:text-primary transition-all duration-100\"\n >\n <SettingsIcon size={14} />\n <span>配置</span>\n </button>\n </DialogTrigger>\n <DialogContent className=\"sm:max-w-[600px]\">\n <DialogHeader className=\"mb-4\">\n <DialogTitle>配置 {mcpServerName} MCP</DialogTitle>\n <DialogDescription>\n 点击保存后,需要重启服务才会生效。\n </DialogDescription>\n </DialogHeader>\n\n {/* 模式切换 */}\n <Tabs value={inputMode} onValueChange={handleModeChange}>\n <TabsList className=\"grid w-full grid-cols-2\">\n <TabsTrigger value=\"form\">表单模式</TabsTrigger>\n <TabsTrigger value=\"json\">高级模式 (JSON)</TabsTrigger>\n </TabsList>\n\n {/* 表单模式 */}\n <TabsContent value=\"form\" className=\"mt-4\">\n <McpServerForm\n form={form}\n defaultValues={defaultFormValues}\n onSubmit={handleFormSubmit}\n disabled={isLoading}\n submitText={isLoading ? \"保存中...\" : \"保存\"}\n />\n </TabsContent>\n\n {/* 高级模式 */}\n <TabsContent value=\"json\" className=\"mt-4\">\n <Form {...advancedForm}>\n <form onSubmit={advancedForm.handleSubmit(handleAdvancedSubmit)}>\n <div className=\"grid gap-4\">\n <FormField\n control={advancedForm.control}\n name=\"config\"\n render={({ field }) => (\n <FormItem>\n <FormControl>\n <Textarea\n className=\"resize-none h-[300px] font-mono text-sm\"\n disabled={isLoading}\n placeholder={`支持三种通信方式:\n\n1. 本地进程 (stdio):\n{\n \"mcpServers\": {\n \"local-server\": {\n \"command\": \"npx\",\n \"args\": [\"-y\", \"@example/mcp-server\"]\n }\n }\n}\n\n2. 服务器推送 (SSE):\n{\n \"mcpServers\": {\n \"sse-server\": {\n \"type\": \"sse\",\n \"url\": \"https://example.com/sse\"\n }\n }\n}\n\n3. 流式 HTTP:\n{\n \"mcpServers\": {\n \"http-server\": {\n \"url\": \"https://example.com/mcp\"\n }\n }\n}`}\n {...field}\n onChange={(event) => {\n field.onChange(event);\n setJsonInput(event.target.value);\n }}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n </div>\n <DialogFooter className=\"mt-4\">\n <DialogClose asChild>\n <Button variant=\"outline\" disabled={isLoading}>\n 取消\n </Button>\n </DialogClose>\n <Button type=\"submit\" disabled={isLoading}>\n {isLoading ? \"保存中...\" : \"保存\"}\n </Button>\n </DialogFooter>\n </form>\n </Form>\n </TabsContent>\n </Tabs>\n </DialogContent>\n </Dialog>\n );\n}\n","/**\n * RemoveMcpServerButton 组件 - MCP 服务器删除按钮\n *\n * 功能:\n * - 删除指定的 MCP 服务器配置\n * - 删除前显示确认对话框\n * - 支持自定义删除成功回调\n * - 显示加载状态和错误提示\n */\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n AlertDialogTrigger,\n} from \"@/components/ui/alert-dialog\";\nimport { cn } from \"@/lib/utils\";\nimport { apiClient } from \"@/services/api\";\nimport { TrashIcon } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { toast } from \"sonner\";\n\nexport function RemoveMcpServerButton({\n mcpServerName,\n onRemoveSuccess,\n disabled = false,\n}: {\n mcpServerName: string;\n onRemoveSuccess?: () => Promise<void>;\n disabled?: boolean;\n}) {\n const [isLoading, setIsLoading] = useState(false);\n\n const onRemove = async () => {\n try {\n setIsLoading(true);\n\n // 调用API删除服务器\n const result = await apiClient.removeMCPServer(mcpServerName);\n\n if (!result) {\n throw new Error(\"删除服务器失败\");\n }\n\n toast.success(`MCP 服务 \"${mcpServerName}\" 已删除`);\n\n // 调用成功回调\n if (onRemoveSuccess) {\n await onRemoveSuccess();\n }\n } catch (error) {\n console.error(\"删除 MCP 服务失败:\", error);\n toast.error(\n `删除 MCP 服务失败: ${error instanceof Error ? error.message : \"未知错误\"}`\n );\n } finally {\n setIsLoading(false);\n }\n };\n\n return (\n <AlertDialog>\n <AlertDialogTrigger asChild>\n <button\n type=\"button\"\n disabled={disabled || isLoading}\n className={cn(\n \"flex items-center gap-1 hover:cursor-pointer text-destructive hover:text-red-700 transition-all duration-100\",\n (disabled || isLoading) && \"opacity-50 cursor-not-allowed\"\n )}\n >\n <TrashIcon size={14} />\n <span>卸载</span>\n </button>\n </AlertDialogTrigger>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>\n 确定要删除这个({mcpServerName})MCP服务吗?\n </AlertDialogTitle>\n <AlertDialogDescription>\n 删除后,对应的工具列表也会移除。\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>取消</AlertDialogCancel>\n <AlertDialogAction\n onClick={onRemove}\n disabled={disabled || isLoading}\n className=\"bg-destructive text-destructive-foreground hover:bg-destructive/90\"\n >\n {isLoading ? \"删除中...\" : \"确定\"}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n );\n}\n","import { ChevronLeft, ChevronRight, MoreHorizontal } from \"lucide-react\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\nimport { type ButtonProps, buttonVariants } from \"./button\";\n\nconst Pagination = ({ className, ...props }: React.ComponentProps<\"nav\">) => (\n <nav\n aria-label=\"pagination\"\n className={cn(\"mx-auto flex w-full justify-center\", className)}\n {...props}\n />\n);\nPagination.displayName = \"Pagination\";\n\nconst PaginationContent = React.forwardRef<\n HTMLUListElement,\n React.ComponentProps<\"ul\">\n>(({ className, ...props }, ref) => (\n <ul\n ref={ref}\n className={cn(\"flex flex-row items-center gap-1\", className)}\n {...props}\n />\n));\nPaginationContent.displayName = \"PaginationContent\";\n\nconst PaginationItem = React.forwardRef<\n HTMLLIElement,\n React.ComponentProps<\"li\">\n>(({ className, ...props }, ref) => (\n <li ref={ref} className={cn(\"\", className)} {...props} />\n));\nPaginationItem.displayName = \"PaginationItem\";\n\ntype PaginationLinkProps = {\n isActive?: boolean;\n} & Pick<ButtonProps, \"size\"> &\n React.ComponentProps<\"a\">;\n\nconst PaginationLink = ({\n className,\n isActive,\n size = \"icon\",\n ...props\n}: PaginationLinkProps) => (\n <a\n aria-current={isActive ? \"page\" : undefined}\n className={cn(\n buttonVariants({\n variant: isActive ? \"outline\" : \"ghost\",\n size,\n }),\n className\n )}\n {...props}\n />\n);\nPaginationLink.displayName = \"PaginationLink\";\n\nconst PaginationPrevious = ({\n className,\n ...props\n}: React.ComponentProps<typeof PaginationLink>) => (\n <PaginationLink\n aria-label=\"Go to previous page\"\n size=\"default\"\n className={cn(\"gap-1 pl-2.5\", className)}\n {...props}\n >\n <ChevronLeft className=\"h-4 w-4\" />\n <span>上一页</span>\n </PaginationLink>\n);\nPaginationPrevious.displayName = \"PaginationPrevious\";\n\nconst PaginationNext = ({\n className,\n ...props\n}: React.ComponentProps<typeof PaginationLink>) => (\n <PaginationLink\n aria-label=\"Go to next page\"\n size=\"default\"\n className={cn(\"gap-1 pr-2.5\", className)}\n {...props}\n >\n <span>下一页</span>\n <ChevronRight className=\"h-4 w-4\" />\n </PaginationLink>\n);\nPaginationNext.displayName = \"PaginationNext\";\n\nconst PaginationEllipsis = ({\n className,\n ...props\n}: React.ComponentProps<\"span\">) => (\n <span\n aria-hidden\n className={cn(\"flex h-9 w-9 items-center justify-center\", className)}\n {...props}\n >\n <MoreHorizontal className=\"h-4 w-4\" />\n <span className=\"sr-only\">更多页</span>\n </span>\n);\nPaginationEllipsis.displayName = \"PaginationEllipsis\";\n\nexport {\n Pagination,\n PaginationContent,\n PaginationEllipsis,\n PaginationItem,\n PaginationLink,\n PaginationNext,\n PaginationPrevious,\n};\n","\"use client\";\n\nimport {\n Pagination,\n PaginationContent,\n PaginationEllipsis,\n PaginationItem,\n PaginationLink,\n PaginationNext,\n PaginationPrevious,\n} from \"@/components/ui/pagination\";\nimport { cn } from \"@/lib/utils\";\nimport { useMemo } from \"react\";\n\nexport interface ToolPaginationProps {\n currentPage: number;\n totalPages: number;\n setPage: (page: number) => void;\n}\n\n/**\n * 生成页码数组,使用智能省略逻辑\n * @param currentPage 当前页码\n * @param totalPages 总页数\n * @returns 页码数组,-1 表示前省略号,-2 表示后省略号\n */\nfunction getPageNumbers(\n currentPage: number,\n totalPages: number\n): Array<number | -1 | -2> {\n const pageNumbers: Array<number | -1 | -2> = [];\n const maxVisible = 7;\n\n if (totalPages <= maxVisible) {\n // 总页数 ≤ 7:显示所有页码\n for (let i = 1; i <= totalPages; i++) {\n pageNumbers.push(i);\n }\n } else if (currentPage <= 4) {\n // 前 4 页:显示 1,2,3,4,5,...,last\n for (let i = 1; i <= 5; i++) {\n pageNumbers.push(i);\n }\n pageNumbers.push(-2, totalPages);\n } else if (currentPage >= totalPages - 3) {\n // 后 4 页:显示 1,...,last-3,last-2,last-1,last\n pageNumbers.push(1, -1);\n for (let i = totalPages - 3; i <= totalPages; i++) {\n pageNumbers.push(i);\n }\n } else {\n // 中间页:显示 1,...,cur-1,cur,cur+1,...,last\n pageNumbers.push(\n 1,\n -1,\n currentPage - 1,\n currentPage,\n currentPage + 1,\n -2,\n totalPages\n );\n }\n\n return pageNumbers;\n}\n\n/**\n * 工具表格分页组件\n * 提供智能省略的分页导航 UI\n */\nexport function ToolPagination({\n currentPage,\n totalPages,\n setPage,\n}: ToolPaginationProps) {\n // 使用 useMemo 缓存页码数组,避免每次渲染都重新计算\n const pageNumbers = useMemo(\n () => getPageNumbers(currentPage, totalPages),\n [currentPage, totalPages]\n );\n\n // 当数据量不超过一页时,不显示分页控件\n const shouldShowPagination = totalPages > 1;\n\n if (!shouldShowPagination) {\n return null;\n }\n\n return (\n <div className=\"flex items-end justify-start py-4\">\n <Pagination>\n <PaginationContent>\n {/* 上一页按钮 */}\n <PaginationItem>\n <PaginationPrevious\n onClick={() => setPage(currentPage - 1)}\n className={cn(\n \"cursor-pointer\",\n currentPage === 1 && \"pointer-events-none opacity-50\"\n )}\n />\n </PaginationItem>\n\n {/* 页码生成 */}\n {pageNumbers.map((pageNum) => {\n // 前省略号\n if (pageNum === -1) {\n return (\n <PaginationItem key=\"ellipsis-start\">\n <PaginationEllipsis />\n </PaginationItem>\n );\n }\n\n // 后省略号\n if (pageNum === -2) {\n return (\n <PaginationItem key=\"ellipsis-end\">\n <PaginationEllipsis />\n </PaginationItem>\n );\n }\n\n // 正常页码\n return (\n <PaginationItem key={`page-${pageNum}`}>\n <PaginationLink\n onClick={() => setPage(pageNum)}\n isActive={currentPage === pageNum}\n className=\"cursor-pointer\"\n >\n {pageNum}\n </PaginationLink>\n </PaginationItem>\n );\n })}\n\n {/* 下一页按钮 */}\n <PaginationItem>\n <PaginationNext\n onClick={() => setPage(currentPage + 1)}\n className={cn(\n \"cursor-pointer\",\n currentPage === totalPages && \"pointer-events-none opacity-50\"\n )}\n />\n </PaginationItem>\n </PaginationContent>\n </Pagination>\n </div>\n );\n}\n","\"use client\";\n\nimport { Input } from \"@/components/ui/input\";\nimport { cn } from \"@/lib/utils\";\nimport { Search, X } from \"lucide-react\";\nimport * as React from \"react\";\n\ninterface ToolSearchInputProps {\n /** 搜索框的值 */\n value: string;\n /** 值变化回调 */\n onChange: (value: string) => void;\n /** 占位符文本 */\n placeholder?: string;\n /** 额外的类名 */\n className?: string;\n}\n\n/**\n * 工具搜索输入组件\n * 提供简单的搜索输入框和清除功能\n */\nexport function ToolSearchInput({\n value,\n onChange,\n placeholder = \"搜索服务名、工具名、描述...\",\n className,\n}: ToolSearchInputProps) {\n const inputRef = React.useRef<HTMLInputElement>(null);\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n onChange(e.target.value);\n };\n\n const handleClear = () => {\n onChange(\"\");\n inputRef.current?.focus();\n };\n\n return (\n <div className={cn(\"relative\", className)}>\n <div className=\"relative flex items-center\">\n <Search className=\"absolute left-3 h-4 w-4 text-muted-foreground pointer-events-none\" />\n <Input\n ref={inputRef}\n type=\"text\"\n value={value}\n onChange={handleInputChange}\n placeholder={placeholder}\n className=\"pl-9 pr-9 w-64\"\n aria-label=\"搜索工具\"\n />\n {value && (\n <button\n type=\"button\"\n onClick={handleClear}\n className=\"absolute right-3 text-muted-foreground hover:text-foreground transition-colors\"\n aria-label=\"清除搜索\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n )}\n </div>\n </div>\n );\n}\n","\"use client\";\n\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@/components/ui/select\";\n\n/** 排序字段类型 */\nexport type ServerSortField = \"name\" | \"communicationType\" | \"toolCount\";\n\n/** 排序配置 */\nexport interface ServerSortConfig {\n field: ServerSortField;\n}\n\ninterface ServerSortSelectorProps {\n value: ServerSortConfig;\n onChange: (config: ServerSortConfig) => void;\n}\n\n/** 排序选项配置 */\nconst SORT_OPTIONS = [\n { value: \"name\", label: \"按名称排序\" },\n { value: \"communicationType\", label: \"按通信类型排序\" },\n { value: \"toolCount\", label: \"按工具数量排序\" },\n] as const;\n\n/**\n * MCP 服务器排序选择器组件\n * 提供按名称、通信类型、工具数量排序功能\n */\nexport function ServerSortSelector({\n value,\n onChange,\n}: ServerSortSelectorProps) {\n return (\n <div className=\"flex items-center gap-2\">\n <Select\n value={value.field}\n onValueChange={(field) => onChange({ field: field as ServerSortField })}\n >\n <SelectTrigger id=\"server-sort-field\" className=\"w-40\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SORT_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n );\n}\n","import { cn } from \"@/lib/utils\";\n\n/**\n * MCP 服务器状态类型\n */\nexport type MCPServerStatusType =\n | \"connected\"\n | \"disconnected\"\n | \"connecting\"\n | \"error\";\n\n/**\n * 状态徽章组件属性\n */\nexport interface StatusBadgeProps {\n /** 服务器状态 */\n status: MCPServerStatusType;\n}\n\n/**\n * 状态徽章组件\n * 显示 MCP 服务器的连接状态\n */\nexport function StatusBadge({ status }: StatusBadgeProps) {\n const color = {\n connected: \"bg-green-500\",\n disconnected: \"bg-red-500\",\n connecting: \"bg-yellow-500\",\n error: \"bg-gray-500\",\n };\n const labels = {\n connected: \"已连接\",\n disconnected: \"未连接\",\n connecting: \"连接中\",\n error: \"错误\",\n };\n\n return (\n <div className=\"flex items-center gap-2\">\n <span\n className={cn(\"flex size-2 rounded-full\", color[status])}\n title={labels[status]}\n />\n {labels[status]}\n </div>\n );\n}\n","\"use client\";\n\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from \"@/components/ui/table\";\nimport { useServerSearch } from \"@/hooks/useServerSearch\";\nimport { useServerSortPersistence } from \"@/hooks/useServerSortPersistence\";\nimport { useToolPagination } from \"@/hooks/useToolPagination\";\nimport { cn } from \"@/lib/utils\";\nimport { useMcpServersWithStatus } from \"@/stores/config\";\nimport type { MCPServerConfig } from \"@xiaozhi-client/shared-types\";\nimport { CoffeeIcon } from \"lucide-react\";\nimport { useCallback, useEffect, useMemo } from \"react\";\nimport { toast } from \"sonner\";\nimport { McpServerSettingButton } from \"../mcp-server-setting-button\";\nimport { RemoveMcpServerButton } from \"../remove-mcp-server-button\";\nimport { ServerPagination } from \"./server-pagination\";\nimport { ServerSearchInput } from \"./server-search-input\";\nimport { ServerSortSelector } from \"./server-sort-selector\";\nimport { StatusBadge } from \"./status-badge\";\n\n/** 服务器行数据 */\nexport interface ServerRowData {\n /** 服务器名称 */\n name: string;\n /** 服务器配置 */\n config: MCPServerConfig;\n /** 通信类型 */\n communicationType: \"stdio\" | \"sse\" | \"streamable-http\";\n /** 工具数量(可选) */\n toolCount?: number;\n /** 服务器状态 */\n status?: \"connected\" | \"disconnected\" | \"connecting\" | \"error\";\n}\n\ninterface McpServerTableProps {\n /** 额外的类名 */\n className?: string;\n}\n\n/**\n * 格式化服务器数据为行数据\n */\nfunction formatServer(\n name: string,\n config: MCPServerConfig,\n communicationType: \"stdio\" | \"sse\" | \"streamable-http\",\n toolCount?: number,\n status?: \"connected\" | \"disconnected\" | \"connecting\" | \"error\"\n): ServerRowData {\n return {\n name,\n config,\n communicationType,\n toolCount,\n status,\n };\n}\n\n/**\n * 获取通信类型\n */\nfunction getCommunicationType(\n config: MCPServerConfig\n): \"stdio\" | \"sse\" | \"streamable-http\" {\n if (\"command\" in config && typeof config.command === \"string\") {\n return \"stdio\";\n }\n if (\"type\" in config && config.type === \"sse\") {\n return \"sse\";\n }\n return \"streamable-http\";\n}\n\n/**\n * 通信类型显示名称映射\n */\nconst COMMUNICATION_TYPE_LABELS: Record<\n ServerRowData[\"communicationType\"],\n string\n> = {\n stdio: \"stdio\",\n sse: \"sse\",\n \"streamable-http\": \"http\",\n};\n\n/**\n * MCP 服务器表格组件\n * 提供服务器列表展示、搜索、排序和分页功能\n */\nexport function McpServerTable({ className }: McpServerTableProps) {\n const { servers, loading, refresh } = useMcpServersWithStatus();\n\n // 使用持久化排序 Hook\n const { sortConfig, setSortConfig } = useServerSortPersistence();\n\n // 组件挂载时刷新服务器状态\n useEffect(() => {\n refresh();\n }, [refresh]);\n\n // 格式化服务器数据\n const serverList = useMemo(() => {\n return servers.map((server) =>\n formatServer(\n server.name,\n server.config,\n getCommunicationType(server.config),\n server.tools.length,\n server.status\n )\n );\n }, [servers]);\n\n // 排序后的服务器列表\n const sortedServers = useMemo(() => {\n const sorted = [...serverList];\n const { field } = sortConfig;\n\n sorted.sort((a, b) => {\n switch (field) {\n case \"name\":\n return a.name.localeCompare(b.name, \"zh-CN\");\n case \"communicationType\":\n return a.communicationType.localeCompare(b.communicationType);\n case \"toolCount\":\n return (b.toolCount ?? 0) - (a.toolCount ?? 0);\n default:\n return 0;\n }\n });\n\n return sorted;\n }, [serverList, sortConfig]);\n\n // 使用搜索 Hook\n const { searchValue, setSearchValue, filteredServers, clearSearch } =\n useServerSearch(sortedServers);\n\n // 使用分页 Hook(复用工具分页,泛型兼容)\n const { currentPage, totalPages, paginatedTools, setPage } =\n useToolPagination(filteredServers as unknown as any, 10);\n\n const paginatedServers = paginatedTools as unknown as ServerRowData[];\n\n // 手动刷新\n const handleRefresh = useCallback(async () => {\n try {\n await refresh();\n toast.success(\"刷新成功\");\n } catch {\n toast.error(\"刷新失败\");\n }\n }, [refresh]);\n\n // 搜索条件变化时重置分页\n // biome-ignore lint/correctness/useExhaustiveDependencies: 需要在搜索值变化时重置分页\n useEffect(() => {\n setPage(1);\n }, [searchValue, setPage]);\n\n return (\n <div className={cn(\"flex flex-col gap-4 w-full\", className)}>\n {/* 排序选择器和搜索框 */}\n <div className=\"flex items-center justify-between gap-4\">\n <ServerSortSelector value={sortConfig} onChange={setSortConfig} />\n <ServerSearchInput\n value={searchValue}\n onChange={setSearchValue}\n placeholder=\"搜索服务器名称、通信类型...\"\n />\n </div>\n\n {/* 搜索结果提示 */}\n {searchValue && (\n <div className=\"text-sm text-muted-foreground\">\n 找到 {filteredServers.length} 个结果\n {filteredServers.length > 0 && (\n <button\n type=\"button\"\n onClick={clearSearch}\n className=\"ml-2 text-primary hover:underline\"\n >\n 清除搜索\n </button>\n )}\n </div>\n )}\n\n {/* 表格容器 */}\n <div className=\"rounded-md border\">\n {paginatedServers.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-12 gap-4\">\n <CoffeeIcon className=\"h-12 w-12 text-muted-foreground\" />\n <span className=\"text-sm text-muted-foreground\">\n {searchValue ? \"没有找到匹配的服务器\" : \"暂无可用服务器\"}\n </span>\n {searchValue && (\n <Button variant=\"outline\" size=\"sm\" onClick={clearSearch}>\n 清除搜索\n </Button>\n )}\n </div>\n ) : (\n <>\n <Table size=\"compact\">\n <TableHeader>\n <TableRow>\n <TableHead>服务器名称</TableHead>\n <TableHead className=\"w-[100px]\">状态</TableHead>\n <TableHead className=\"w-[120px]\">通信类型</TableHead>\n <TableHead className=\"w-[100px] text-right\">\n 工具数量\n </TableHead>\n <TableHead className=\"w-[220px] text-right\">操作</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {paginatedServers.map((server) => (\n <TableRow key={server.name}>\n <TableCell className=\"font-medium\">{server.name}</TableCell>\n <TableCell>\n {server.status ? (\n <StatusBadge status={server.status} />\n ) : (\n \"-\"\n )}\n </TableCell>\n <TableCell>\n <Badge variant=\"secondary\" className=\"rounded-md\">\n {COMMUNICATION_TYPE_LABELS[server.communicationType]}\n </Badge>\n </TableCell>\n <TableCell className=\"text-right text-muted-foreground\">\n {server.toolCount ?? \"-\"}\n </TableCell>\n <TableCell>\n <div className=\"flex items-center justify-end gap-2\">\n <McpServerSettingButton\n mcpServerName={server.name}\n mcpServer={server.config}\n />\n <RemoveMcpServerButton\n mcpServerName={server.name}\n onRemoveSuccess={handleRefresh}\n disabled={loading}\n />\n </div>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n\n {/* 分页控件 */}\n <ServerPagination\n currentPage={currentPage}\n totalPages={totalPages}\n setPage={setPage}\n />\n </>\n )}\n </div>\n </div>\n );\n}\n","/**\n * MCP 服务器表格对话框组件\n *\n * 在对话框中展示 MCP 服务器列表,提供搜索、排序和分页功能。\n */\n\n\"use client\";\n\nimport { McpServerTable } from \"@/components/mcp-server/mcp-server-table\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from \"@/components/ui/dialog\";\nimport { Server } from \"lucide-react\";\nimport { useState } from \"react\";\n\n/**\n * MCP 服务器表格对话框组件\n */\nexport function McpServerTableDialog() {\n const [open, setOpen] = useState(false);\n\n return (\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogTrigger asChild>\n <Button\n variant=\"secondary\"\n size=\"icon\"\n className=\"size-8\"\n aria-label=\"MCP服务列表\"\n title=\"MCP服务列表\"\n >\n <Server className=\"size-4\" />\n </Button>\n </DialogTrigger>\n <DialogContent className=\"max-w-5xl max-h-[85vh]\">\n <DialogHeader>\n <DialogTitle>MCP 服务器列表</DialogTitle>\n </DialogHeader>\n <McpServerTable />\n </DialogContent>\n </Dialog>\n );\n}\n","import { cn } from \"@/lib/utils\";\nimport * as React from \"react\";\n\ninterface ScrollAreaProps extends React.HTMLAttributes<HTMLDivElement> {\n children: React.ReactNode;\n}\n\nconst ScrollArea = React.forwardRef<HTMLDivElement, ScrollAreaProps>(\n ({ className, children, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\"relative overflow-auto\", className)}\n {...props}\n >\n {children}\n </div>\n )\n);\nScrollArea.displayName = \"ScrollArea\";\n\nconst ScrollBar = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement> & {\n orientation?: \"vertical\" | \"horizontal\";\n }\n>(({ className, orientation = \"vertical\", ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\n \"flex touch-none select-none transition-colors\",\n orientation === \"vertical\" &&\n \"h-full w-2.5 border-l border-l-transparent p-[1px]\",\n orientation === \"horizontal\" &&\n \"h-2.5 w-full border-t border-t-transparent p-[1px]\",\n className\n )}\n {...props}\n >\n <div className=\"relative flex-1 rounded-full bg-border\" />\n </div>\n));\nScrollBar.displayName = \"ScrollBar\";\n\nexport { ScrollArea, ScrollBar };\n","import * as SeparatorPrimitive from \"@radix-ui/react-separator\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst Separator = React.forwardRef<\n React.ElementRef<typeof SeparatorPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>\n>(\n (\n { className, orientation = \"horizontal\", decorative = true, ...props },\n ref\n ) => (\n <SeparatorPrimitive.Root\n ref={ref}\n decorative={decorative}\n orientation={orientation}\n className={cn(\n \"shrink-0 bg-border\",\n orientation === \"horizontal\" ? \"h-[1px] w-full\" : \"h-full w-[1px]\",\n className\n )}\n {...props}\n />\n )\n);\nSeparator.displayName = SeparatorPrimitive.Root.displayName;\n\nexport { Separator };\n","/**\n * 格式化工具函数\n */\n\nimport type { ToolCallRecord } from \"@xiaozhi-client/shared-types\";\n\n/**\n * 格式化时间戳为本地化字符串\n * @param timestamp 时间戳(毫秒)\n * @returns 格式化后的时间字符串\n */\nexport const formatTimestamp = (timestamp?: number): string => {\n if (!timestamp) return \"未知时间\";\n return new Date(timestamp).toLocaleString(\"zh-CN\", {\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n });\n};\n\n/**\n * 格式化持续时间为易读字符串\n * @param duration 持续时间(毫秒)\n * @returns 格式化后的持续时间字符串\n */\nexport const formatDuration = (duration?: number): string => {\n if (!duration) return \"-\";\n if (duration < 1000) return `${duration}ms`;\n return `${(duration / 1000).toFixed(1)}s`;\n};\n\n/**\n * 生成稳定的React键值\n * @param log 工具调用记录\n * @param index 索引\n * @returns 稳定的键值\n */\nexport const generateStableKey = (\n log: ToolCallRecord,\n index: number\n): string => {\n // 使用工具名、时间戳和索引组合生成稳定的键\n // 如果时间戳不存在,使用索引作为后备\n const timestamp = log.timestamp || Date.now();\n return `${log.toolName}-${timestamp}-${index}`;\n};\n\n/**\n * 格式化JSON数据为可读字符串\n * @param data 要格式化的数据\n * @returns 格式化后的JSON字符串\n */\nexport const formatJson = (data: unknown): string | null => {\n if (!data) return null;\n try {\n return JSON.stringify(data, null, 2);\n } catch (error) {\n return String(data);\n }\n};\n\n/**\n * 重新抛出错误的工具函数\n * @param error 错误对象\n * @returns 错误字符串\n */\nexport const formatError = (error: unknown): string => {\n if (typeof error === \"string\") return error;\n if (error instanceof Error) return error.message;\n return String(error);\n};\n","/**\n * MCP 工具调用日志对话框组件\n *\n * 功能:\n * - 显示最近的 MCP 工具调用记录\n * - 支持查看调用详情(入参、出参、原始数据)\n * - 支持刷新日志列表\n * - 支持复制日志内容\n * - 显示调用状态和耗时\n *\n * @module apps/frontend/src/components/tool-call-logs-dialog\n */\n\n\"use client\";\n\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport { Card } from \"@/components/ui/card\";\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from \"@/components/ui/dialog\";\nimport { ScrollArea } from \"@/components/ui/scroll-area\";\nimport { Separator } from \"@/components/ui/separator\";\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from \"@/components/ui/table\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"@/components/ui/tabs\";\nimport {\n formatDuration,\n formatJson,\n formatTimestamp,\n generateStableKey,\n} from \"@/utils/formatUtils\";\nimport type {\n ApiResponse,\n ToolCallLogsResponse,\n ToolCallRecord,\n} from \"@xiaozhi-client/shared-types\";\nimport {\n CheckCircle,\n CheckIcon,\n Code,\n CopyIcon,\n FileText,\n Loader2,\n RefreshCw,\n RotateCwIcon,\n XCircle,\n} from \"lucide-react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\n\nexport function ToolCallLogsDialog() {\n const [open, setOpen] = useState(false);\n const [logs, setLogs] = useState<ToolCallRecord[]>([]);\n const [loading, setLoading] = useState(false);\n const [refreshing, setRefreshing] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [selectedLog, setSelectedLog] = useState<ToolCallRecord | null>(null);\n const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);\n const [limit] = useState(50);\n const [total, setTotal] = useState(0);\n\n const fetchLogs = useCallback(\n async (isRefresh = false) => {\n if (isRefresh) {\n setRefreshing(true);\n } else {\n setLoading(true);\n }\n setError(null);\n\n try {\n const response = await fetch(`/api/tool-calls/logs?limit=${limit}`);\n const data: ApiResponse<ToolCallLogsResponse> = await response.json();\n\n if (data.success && data.data) {\n setLogs(data.data.records);\n setTotal(data.data.total);\n } else {\n setError(data.error?.message || \"获取日志失败\");\n }\n } catch (err) {\n setError(\"网络请求失败\");\n console.error(\"获取工具调用日志失败:\", err);\n } finally {\n if (isRefresh) {\n setRefreshing(false);\n } else {\n setLoading(false);\n }\n }\n },\n [limit]\n );\n\n useEffect(() => {\n if (open) {\n fetchLogs();\n } else {\n // 关闭弹窗时清空选中状态\n setSelectedLog(null);\n setHoveredIndex(null);\n }\n }, [open, fetchLogs]);\n\n // 处理数据行悬停\n const handleRowMouseEnter = (log: ToolCallRecord, index: number) => {\n if (hoveredIndex !== index) {\n setSelectedLog(log);\n setHoveredIndex(index);\n }\n };\n\n // 处理详情面板鼠标进入\n const handleDetailMouseEnter = () => {\n // 确保鼠标在详情面板时保持显示状态\n // 不需要做任何操作,保持当前状态\n };\n\n // 处理弹窗容器鼠标离开\n const handleDialogMouseLeave = () => {\n // 只有当鼠标完全离开弹窗时才清空状态\n setSelectedLog(null);\n setHoveredIndex(null);\n };\n\n return (\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogTrigger asChild>\n <Button\n variant=\"secondary\"\n size=\"icon\"\n className=\"size-8\"\n aria-label=\"MCP工具调用日志\"\n title=\"MCP工具调用日志\"\n >\n <FileText className=\"size-4\" />\n </Button>\n </DialogTrigger>\n <DialogContent\n className=\"max-w-6xl max-h-[80vh]\"\n onMouseLeave={handleDialogMouseLeave}\n >\n <DialogHeader>\n <div className=\"flex items-center justify-between\">\n <DialogTitle className=\"flex items-center gap-2\">\n <FileText className=\"h-5 w-5\" />\n MCP 工具调用日志\n {total > 0 && (\n <span className=\"text-sm font-normal text-muted-foreground ml-2\">\n (共 {total} 条记录)\n </span>\n )}\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => fetchLogs(true)}\n disabled={refreshing}\n >\n <RotateCwIcon\n className={`h-4 w-4 ${refreshing ? \"animate-spin\" : \"\"}`}\n />\n 刷新\n </Button>\n </DialogTitle>\n </div>\n </DialogHeader>\n\n <div className=\"flex-1 flex gap-4\">\n {/* 左侧表格区域 */}\n <div className=\"flex-1\">\n {loading ? (\n <div className=\"flex flex-col items-center justify-center py-8\">\n <div className=\"relative\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <FileText className=\"h-4 w-4 text-muted-foreground/50\" />\n </div>\n </div>\n <div className=\"mt-4 text-center\">\n <p className=\"text-muted-foreground font-medium\">\n 正在加载日志数据\n </p>\n <p className=\"text-muted-foreground/70 text-sm mt-1\">\n 请稍候片刻...\n </p>\n </div>\n </div>\n ) : error ? (\n <div className=\"text-center py-8\">\n <div className=\"bg-destructive/10 border border-destructive/20 rounded-lg p-6 max-w-md mx-auto\">\n <XCircle className=\"h-12 w-12 text-destructive mx-auto mb-4\" />\n <h3 className=\"text-destructive font-semibold text-lg mb-2\">\n 加载失败\n </h3>\n <p className=\"text-muted-foreground text-sm mb-4 leading-relaxed\">\n {error}\n </p>\n <div className=\"flex flex-col gap-2\">\n <Button\n onClick={() => fetchLogs()}\n variant=\"outline\"\n size=\"sm\"\n className=\"gap-2\"\n >\n <RefreshCw className=\"h-4 w-4\" />\n 重试加载\n </Button>\n <Button\n onClick={() => fetchLogs(true)}\n variant=\"ghost\"\n size=\"sm\"\n className=\"text-xs\"\n >\n 强制刷新\n </Button>\n </div>\n </div>\n </div>\n ) : logs.length === 0 ? (\n <div className=\"text-center py-8\">\n <div className=\"bg-muted/30 border border-muted rounded-lg p-6 max-w-md mx-auto\">\n <FileText className=\"h-12 w-12 text-muted-foreground mx-auto mb-4\" />\n <h3 className=\"font-medium text-lg mb-2\">暂无工具调用记录</h3>\n <p className=\"text-muted-foreground text-sm leading-relaxed mb-4\">\n 当前还没有任何工具调用记录。当您开始使用工具时,相关的调用信息会在这里显示。\n </p>\n <Button\n onClick={() => fetchLogs(true)}\n variant=\"outline\"\n size=\"sm\"\n className=\"gap-2\"\n >\n <RefreshCw className=\"h-4 w-4\" />\n 检查更新\n </Button>\n </div>\n </div>\n ) : (\n <ScrollArea className=\"h-[60vh]\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead>工具名称</TableHead>\n <TableHead>服务器</TableHead>\n <TableHead>状态</TableHead>\n <TableHead>耗时</TableHead>\n <TableHead>时间</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {logs.map((log, index) => (\n <TableRow\n key={generateStableKey(log, index)}\n className={`cursor-pointer transition-colors ${\n hoveredIndex === index\n ? \"bg-muted/50\"\n : \"hover:bg-muted/30\"\n }`}\n onMouseEnter={() => handleRowMouseEnter(log, index)}\n >\n <TableCell className=\"font-medium\">\n <div>\n <div>{log.toolName}</div>\n {log.originalToolName &&\n log.originalToolName !== log.toolName && (\n <div className=\"text-xs text-muted-foreground\">\n 原始: {log.originalToolName}\n </div>\n )}\n </div>\n </TableCell>\n <TableCell>\n {log.serverName || (\n <span className=\"text-muted-foreground\">-</span>\n )}\n </TableCell>\n <TableCell>\n <Badge\n variant={log.success ? undefined : \"destructive\"}\n className={`gap-1 w-[50px] text-center ${\n log.success\n ? \"bg-green-600 hover:bg-green-600\"\n : \"bg-red-600 hover:bg-red-600\"\n }`}\n >\n {log.success ? \"成功\" : \"失败\"}\n </Badge>\n </TableCell>\n <TableCell>{formatDuration(log.duration)}</TableCell>\n <TableCell className=\"text-muted-foreground text-sm\">\n {formatTimestamp(log.timestamp)}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </ScrollArea>\n )}\n </div>\n\n {/* 右侧详情面板 */}\n {selectedLog && (\n <div\n className=\"w-96 border-l pl-4\"\n onMouseEnter={handleDetailMouseEnter}\n >\n <CallLogDetail log={selectedLog} />\n </div>\n )}\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n\n// 复制按钮组件\ninterface CopyButtonProps {\n size?: \"sm\" | \"default\" | \"lg\";\n copyContent: string;\n className?: string;\n}\n\nfunction CopyButton({\n size = \"sm\",\n copyContent,\n className = \"\",\n}: CopyButtonProps) {\n const [copied, setCopied] = useState(false);\n const copiedTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const handleCopy = async () => {\n try {\n await navigator.clipboard.writeText(copyContent);\n setCopied(true);\n\n // 清除之前的定时器\n if (copiedTimerRef.current) {\n clearTimeout(copiedTimerRef.current);\n }\n\n // 保存定时器引用,3秒后自动恢复状态\n copiedTimerRef.current = setTimeout(() => {\n setCopied(false);\n }, 3000);\n } catch (error) {\n console.error(\"复制失败:\", error);\n }\n };\n\n // 组件卸载时清理定时器\n useEffect(() => {\n return () => {\n if (copiedTimerRef.current) {\n clearTimeout(copiedTimerRef.current);\n }\n };\n }, []);\n\n return (\n <Button\n variant=\"ghost\"\n size={size}\n className={`${className} ${\n copied ? \"text-green-600 hover:text-green-700\" : \"\"\n }`}\n onClick={handleCopy}\n >\n {copied ? <CheckIcon /> : <CopyIcon />}\n </Button>\n );\n}\n\n// 详情展示组件\ninterface CallLogDetailProps {\n log: ToolCallRecord;\n}\n\nfunction CallLogDetail({ log }: CallLogDetailProps) {\n const formatRawData = (log: ToolCallRecord) => {\n try {\n return JSON.stringify(log, null, 2);\n } catch (error) {\n return String(log);\n }\n };\n\n return (\n <Card className=\"h-[60vh]\">\n <div className=\"p-4 h-full flex flex-col\">\n {/* 详情内容 */}\n <Tabs defaultValue=\"arguments\" className=\"flex-1 flex flex-col min-h-0\">\n <TabsList className=\"grid w-full grid-cols-3 flex-shrink-0\">\n <TabsTrigger value=\"arguments\">入参</TabsTrigger>\n <TabsTrigger value=\"result\">出参</TabsTrigger>\n <TabsTrigger value=\"raw\">原始数据</TabsTrigger>\n </TabsList>\n\n <div className=\"flex-1 min-h-0\">\n <TabsContent\n value=\"arguments\"\n className=\"h-full data-[state=active]:flex data-[state=active]:flex-col\"\n >\n <ScrollArea className=\"h-full\">\n {log.arguments ? (\n <div className=\"relative\">\n <pre className=\"text-xs bg-muted p-3 rounded-md overflow-x-auto text-wrap break-words\">\n {formatJson(log.arguments)}\n </pre>\n <CopyButton\n copyContent={formatJson(log.arguments) || \"\"}\n className=\"absolute top-2 right-2 hover:bg-slate-200 w-[30px] h-[30px]\"\n />\n </div>\n ) : (\n <div className=\"text-center text-muted-foreground py-8\">\n <Code className=\"h-8 w-8 mx-auto mb-2 opacity-50\" />\n <p>无入参</p>\n </div>\n )}\n </ScrollArea>\n </TabsContent>\n\n <TabsContent\n value=\"result\"\n className=\"h-full data-[state=active]:flex data-[state=active]:flex-col\"\n >\n <ScrollArea className=\"h-full\">\n {log.success ? (\n log.result ? (\n <div className=\"relative\">\n <pre className=\"text-xs bg-muted p-3 rounded-md overflow-x-auto text-wrap break-words\">\n {formatJson(log.result)}\n </pre>\n <CopyButton\n copyContent={formatJson(log.result) || \"\"}\n className=\"absolute top-2 right-2 hover:bg-slate-200 w-[30px] h-[30px]\"\n />\n </div>\n ) : (\n <div className=\"text-center text-muted-foreground py-8\">\n <CheckCircle className=\"h-8 w-8 mx-auto mb-2 opacity-50\" />\n <p>无出参</p>\n </div>\n )\n ) : (\n <div className=\"relative\">\n <div className=\"bg-destructive/10 border border-destructive/20 p-3 rounded-md\">\n <div className=\"flex items-center gap-2 mb-2\">\n <XCircle className=\"h-4 w-4 text-destructive\" />\n <span className=\"font-medium text-destructive\">\n 调用失败\n </span>\n </div>\n {log.error && (\n <pre className=\"text-xs text-destructive/80 whitespace-pre-wrap\">\n {log.error}\n </pre>\n )}\n </div>\n {log.error && (\n <CopyButton\n copyContent={log.error || \"\"}\n className=\"absolute top-2 right-2\"\n />\n )}\n </div>\n )}\n </ScrollArea>\n </TabsContent>\n\n <TabsContent\n value=\"raw\"\n className=\"h-full data-[state=active]:flex data-[state=active]:flex-col\"\n >\n <ScrollArea className=\"h-full\">\n <div className=\"relative\">\n <pre className=\"text-xs bg-muted p-3 rounded-md overflow-x-auto text-wrap break-words\">\n {formatRawData(log)}\n </pre>\n <CopyButton\n copyContent={formatRawData(log)}\n className=\"absolute top-2 right-2 hover:bg-slate-200 w-[30px] h-[30px]\"\n />\n </div>\n </ScrollArea>\n </TabsContent>\n </div>\n </Tabs>\n\n <Separator className=\"my-4 flex-shrink-0\" />\n <div className=\"text-xs text-muted-foreground flex justify-between flex-shrink-0\">\n <span>耗时: {formatDuration(log.duration)}</span>\n <span>{formatTimestamp(log.timestamp)}</span>\n </div>\n </div>\n </Card>\n );\n}\n","/**\n * MCP 服务状态卡片组件\n *\n * 显示当前连接的 MCP 服务数量,并提供服务器列表和工具调用日志查看入口。\n */\n\nimport { AddMcpServerButton } from \"@/components/add-mcp-server-button\";\nimport { McpServerTableDialog } from \"@/components/mcp-server/mcp-server-table-dialog\";\nimport { ToolCallLogsDialog } from \"@/components/tool-call-logs-dialog\";\nimport {\n Card,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from \"@/components/ui/card\";\nimport { useMcpServersWithStatus } from \"@/stores/config\";\nimport { MiniCircularProgress } from \"./mini-circular-progress\";\n\n/**\n * MCP 服务状态卡片组件\n *\n * 显示当前连接的 MCP 服务数量,右上角显示服务数量指示器。\n */\nexport function ServerStatusCard() {\n const { servers } = useMcpServersWithStatus();\n\n const totalServers = servers.length;\n const connectedServers = servers.filter((s) => s.connected).length;\n\n return (\n <Card className=\"@container/card\">\n <CardHeader className=\"relative\">\n <CardDescription>MCP服务</CardDescription>\n <CardTitle className=\"@[250px]/card:text-3xl text-2xl font-semibold tabular-nums\">\n {connectedServers}/{totalServers}\n </CardTitle>\n <div className=\"absolute right-4 top-4\">\n <MiniCircularProgress\n showValue={false}\n value={connectedServers}\n maxValue={Math.max(totalServers, 1)}\n activeColor=\"#16a34a\"\n inactiveColor=\"#f87171\"\n size={30}\n symbol=\"\"\n />\n </div>\n </CardHeader>\n <CardFooter className=\"flex items-center justify-between gap-1 text-sm\">\n <div className=\"text-muted-foreground\">\n 已连接 {connectedServers} 个,共 {totalServers} 个服务\n </div>\n <div className=\"flex gap-2\">\n <AddMcpServerButton />\n <McpServerTableDialog />\n <ToolCallLogsDialog />\n </div>\n </CardFooter>\n </Card>\n );\n}\n","import { Button } from \"@/components/ui/button\";\nimport { useRestartPollingStatus, useStatusStore } from \"@/stores/status\";\nimport clsx from \"clsx\";\nimport { LoaderCircleIcon, PowerIcon } from \"lucide-react\";\n\n/**\n * RestartButton 组件属性接口\n */\nexport interface RestartButtonProps {\n /** 是否禁用按钮 */\n disabled?: boolean;\n /** 按钮样式变体 */\n variant?:\n | \"default\"\n | \"destructive\"\n | \"outline\"\n | \"secondary\"\n | \"ghost\"\n | \"link\";\n /** 自定义样式类 */\n className?: string;\n /** 重启中的文本 */\n restartingText?: string;\n /** 默认文本 */\n defaultText?: string;\n /** 图标模式(纯 icon 按钮,用于卡片中) */\n iconMode?: boolean;\n}\n\n/**\n * 独立的重启按钮组件\n * 基于 ConfigEditor.tsx 中的重启服务功能实现\n */\nexport function RestartButton({\n disabled = false,\n variant = \"outline\",\n className = \"\",\n restartingText = \"重启中...\",\n defaultText = \"重启服务\",\n iconMode = false,\n}: RestartButtonProps) {\n const {\n loading: { isRestarting },\n restartService,\n } = useStatusStore();\n const restartPollingStatus = useRestartPollingStatus();\n\n // 处理重启点击事件\n const handleRestart = async () => {\n try {\n await restartService();\n } catch (error) {\n console.error(\"[RestartButton] 重启失败:\", error);\n }\n };\n\n // 计算显示文本\n const getDisplayText = () => {\n if (!isRestarting) {\n return defaultText;\n }\n\n // 如果重启轮询正在进行,显示进度信息\n if (restartPollingStatus.enabled && restartPollingStatus.startTime) {\n return \"重连中...\";\n }\n\n return restartingText;\n };\n\n // 图标模式:纯 icon 按钮,与卡片样式一致\n if (iconMode) {\n return (\n <Button\n type=\"button\"\n onClick={handleRestart}\n variant=\"secondary\"\n size=\"icon\"\n className=\"size-8\"\n disabled={isRestarting || disabled}\n aria-label=\"重启服务\"\n title=\"重启服务\"\n >\n {!isRestarting ? (\n <PowerIcon className=\"size-4\" />\n ) : (\n <LoaderCircleIcon className=\"size-4 animate-spin\" />\n )}\n </Button>\n );\n }\n\n // 默认模式:带文字的按钮\n return (\n <Button\n type=\"button\"\n onClick={handleRestart}\n variant={variant}\n disabled={isRestarting || disabled}\n className={clsx(\"flex items-center gap-2 w-[120px]\", className)}\n >\n {!isRestarting ? (\n <PowerIcon className=\"size-4\" />\n ) : (\n <LoaderCircleIcon className=\"size-4 animate-spin\" />\n )}\n {getDisplayText()}\n </Button>\n );\n}\n","/**\n * 系统设置对话框组件\n *\n * 在对话框中展示和编辑系统配置,包括平台认证和连接参数。\n */\n\nimport { Button } from \"@/components/ui/button\";\nimport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from \"@/components/ui/dialog\";\nimport {\n Form,\n FormControl,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\nimport { useWebSocketActions } from \"@/providers/WebSocketProvider\";\nimport { useConfig } from \"@/stores/config\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport type { AppConfig } from \"@xiaozhi-client/shared-types\";\nimport { SettingsIcon } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport { toast } from \"sonner\";\nimport z from \"zod\";\n\nconst formSchema = z.object({\n modelscope: z.object({\n apiKey: z.string().optional(),\n }),\n platforms: z.object({\n coze: z.object({\n token: z.string().optional(),\n }),\n }),\n connection: z.object({\n heartbeatInterval: z.number().min(1000, {\n message: \"心跳间隔不能小于1000毫秒\",\n }),\n heartbeatTimeout: z.number().min(1000, {\n message: \"心跳超时不能小于1000毫秒\",\n }),\n reconnectInterval: z.number().min(1000, {\n message: \"重连间隔不能小于1000毫秒\",\n }),\n }),\n});\n\n/**\n * 系统设置对话框组件\n *\n * 提供在对话框中编辑系统配置的功能,复用 SettingsPage 的表单逻辑。\n */\nexport function SystemSettingDialog() {\n const [open, setOpen] = useState(false);\n const [isLoading, setIsLoading] = useState(false);\n const config = useConfig();\n const { updateConfig } = useWebSocketActions();\n\n const form = useForm<z.infer<typeof formSchema>>({\n resolver: zodResolver(formSchema),\n defaultValues: {\n platforms: {\n coze: {\n token: config?.platforms?.coze?.token || \"\",\n },\n },\n modelscope: {\n apiKey: config?.modelscope?.apiKey || \"\",\n },\n connection: {\n heartbeatInterval: config?.connection?.heartbeatInterval || 30000,\n heartbeatTimeout: config?.connection?.heartbeatTimeout || 10000,\n reconnectInterval: config?.connection?.reconnectInterval || 5000,\n },\n },\n });\n\n // 当配置加载后,更新表单默认值\n useEffect(() => {\n form.reset({\n modelscope: {\n apiKey: config?.modelscope?.apiKey || \"\",\n },\n connection: {\n heartbeatInterval: config?.connection?.heartbeatInterval || 30000,\n heartbeatTimeout: config?.connection?.heartbeatTimeout || 10000,\n reconnectInterval: config?.connection?.reconnectInterval || 5000,\n },\n });\n }, [config, form]);\n\n async function onSubmit(values: z.infer<typeof formSchema>) {\n if (!config) {\n toast.error(\"配置数据未加载,请稍后重试\");\n return;\n }\n\n setIsLoading(true);\n try {\n const newConfig: AppConfig = {\n ...config,\n modelscope: {\n apiKey: values.modelscope.apiKey,\n },\n connection: {\n heartbeatInterval: values.connection.heartbeatInterval,\n heartbeatTimeout: values.connection.heartbeatTimeout,\n reconnectInterval: values.connection.reconnectInterval,\n },\n platforms: {\n ...(config?.platforms ?? {}),\n coze: {\n ...(config?.platforms?.coze ?? {}),\n token: values.platforms.coze.token,\n },\n },\n };\n\n await updateConfig(newConfig);\n toast.success(\"配置已更新\");\n setOpen(false);\n } catch (error) {\n console.error(\"更新配置失败:\", error);\n toast.error(error instanceof Error ? error.message : \"更新配置失败\");\n } finally {\n setIsLoading(false);\n }\n }\n\n return (\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogTrigger asChild>\n <Button variant=\"secondary\" size=\"icon\" className=\"size-8\">\n <SettingsIcon className=\"size-4\" />\n </Button>\n </DialogTrigger>\n <DialogContent className=\"sm:max-w-[600px]\">\n <DialogHeader>\n <DialogTitle>系统设置</DialogTitle>\n <DialogDescription>配置平台认证和连接参数</DialogDescription>\n </DialogHeader>\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)}>\n <div className=\"grid gap-4 max-h-[60vh] overflow-y-auto\">\n <FormField\n control={form.control}\n name=\"modelscope.apiKey\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>魔搭社区 API Key</FormLabel>\n <div className=\"flex gap-2\">\n <FormControl>\n <Input\n placeholder=\"魔搭社区 API Key\"\n className=\"font-mono text-sm\"\n type=\"password\"\n disabled={isLoading}\n autoComplete=\"off\"\n data-1p-ignore\n {...field}\n />\n </FormControl>\n <Button\n variant=\"outline\"\n type=\"button\"\n onClick={() => {\n window.open(\n \"https://www.modelscope.cn/my/myaccesstoken\",\n \"_blank\"\n );\n }}\n >\n 打开魔搭社区\n </Button>\n </div>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name=\"platforms.coze.token\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>扣子身份凭证</FormLabel>\n <div className=\"flex gap-2\">\n <FormControl>\n <Input\n placeholder=\"扣子身份凭证\"\n className=\"font-mono text-sm\"\n type=\"password\"\n autoComplete=\"off\"\n data-1p-ignore\n disabled={isLoading}\n {...field}\n />\n </FormControl>\n <Button\n variant=\"outline\"\n type=\"button\"\n onClick={() => {\n window.open(\n \"https://www.coze.cn/open/oauth/sats\",\n \"_blank\"\n );\n }}\n >\n 打开扣子平台\n </Button>\n </div>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name=\"connection.heartbeatInterval\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>心跳间隔(毫秒)</FormLabel>\n <div className=\"flex gap-2 items-center\">\n <FormControl>\n <Input\n placeholder=\"心跳间隔(毫秒)\"\n className=\"font-mono text-sm\"\n type=\"number\"\n disabled={isLoading}\n {...field}\n value={field.value || \"\"}\n onChange={(e) => {\n const value = e.target.value;\n field.onChange(value === \"\" ? \"\" : Number(value));\n }}\n />\n </FormControl>\n <span className=\"text-sm text-muted-foreground w-[50px]\">\n 毫秒\n </span>\n </div>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name=\"connection.heartbeatTimeout\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>心跳超时(毫秒)</FormLabel>\n <div className=\"flex gap-2 items-center\">\n <FormControl>\n <Input\n placeholder=\"心跳超时(毫秒)\"\n className=\"font-mono text-sm\"\n type=\"number\"\n disabled={isLoading}\n {...field}\n value={field.value || \"\"}\n onChange={(e) => {\n const value = e.target.value;\n field.onChange(value === \"\" ? \"\" : Number(value));\n }}\n />\n </FormControl>\n <span className=\"text-sm text-muted-foreground w-[50px]\">\n 毫秒\n </span>\n </div>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name=\"connection.reconnectInterval\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>重连间隔(毫秒)</FormLabel>\n <div className=\"flex gap-2 items-center\">\n <FormControl>\n <Input\n placeholder=\"重连间隔(毫秒)\"\n className=\"font-mono text-sm\"\n type=\"number\"\n disabled={isLoading}\n {...field}\n value={field.value || \"\"}\n onChange={(e) => {\n const value = e.target.value;\n field.onChange(value === \"\" ? \"\" : Number(value));\n }}\n />\n </FormControl>\n <span className=\"text-sm text-muted-foreground w-[50px]\">\n 毫秒\n </span>\n </div>\n <FormMessage />\n </FormItem>\n )}\n />\n </div>\n <DialogFooter className=\"mt-4\">\n <DialogClose asChild>\n <Button variant=\"outline\" disabled={isLoading}>\n 取消\n </Button>\n </DialogClose>\n <Button type=\"submit\" disabled={isLoading}>\n {isLoading ? \"保存中...\" : \"保存\"}\n </Button>\n </DialogFooter>\n </form>\n </Form>\n </DialogContent>\n </Dialog>\n );\n}\n","/**\n * 系统设置状态卡片组件\n *\n * 显示系统配置完成度和快速打开设置对话框的入口。\n */\n\nimport { RestartButton } from \"@/components/restart-button\";\nimport { SystemSettingDialog } from \"@/components/system-setting-dialog\";\nimport {\n Card,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from \"@/components/ui/card\";\nimport { useConfig } from \"@/stores/config\";\nimport { useMemo } from \"react\";\nimport { MiniCircularProgress } from \"./mini-circular-progress\";\n\n/**\n * 系统配置项总数\n */\nconst TOTAL_CONFIG_ITEMS = 5;\n\n/**\n * 系统设置状态卡片组件\n *\n * 显示系统配置完成度(已配置项/总配置项),右上角显示配置完成度的圆形进度指示器。\n * 底部提供快速打开设置对话框的按钮。\n */\nexport function SystemStatusCard() {\n const config = useConfig();\n\n // 计算已配置项数量\n const configuredCount = useMemo(() => {\n if (!config) return 0;\n let count = 0;\n if (config.modelscope?.apiKey) count++;\n if (config.platforms?.coze?.token) count++;\n if (config.connection?.heartbeatInterval) count++;\n if (config.connection?.heartbeatTimeout) count++;\n if (config.connection?.reconnectInterval) count++;\n return count;\n }, [config]);\n\n const completionRate = configuredCount / TOTAL_CONFIG_ITEMS;\n\n return (\n <Card className=\"@container/card\">\n <CardHeader className=\"relative\">\n <CardDescription>系统设置</CardDescription>\n <CardTitle className=\"@[250px]/card:text-3xl text-2xl font-semibold tabular-nums\">\n 已配置 {configuredCount}/{TOTAL_CONFIG_ITEMS}\n </CardTitle>\n <div className=\"absolute right-4 top-4\">\n <MiniCircularProgress\n showValue={true}\n value={configuredCount}\n maxValue={TOTAL_CONFIG_ITEMS}\n activeColor={\n completionRate >= 0.8\n ? \"#16a34a\"\n : completionRate >= 0.5\n ? \"#f59e0b\"\n : \"#f87171\"\n }\n inactiveColor=\"#e5e7eb\"\n size={30}\n symbol=\"\"\n />\n </div>\n </CardHeader>\n <CardFooter className=\"flex items-center justify-between gap-1 text-sm\">\n <div className=\"text-muted-foreground\">\n {completionRate === 1\n ? \"配置已完成\"\n : completionRate >= 0.6\n ? `还差 ${TOTAL_CONFIG_ITEMS - configuredCount} 项配置`\n : \"请完善系统配置\"}\n </div>\n <div className=\"flex gap-2\">\n <RestartButton iconMode />\n <SystemSettingDialog />\n </div>\n </CardFooter>\n </Card>\n );\n}\n","import { type VariantProps, cva } from \"class-variance-authority\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst alertVariants = cva(\n \"relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground\",\n {\n variants: {\n variant: {\n default: \"bg-background text-foreground\",\n destructive:\n \"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n },\n }\n);\n\nconst Alert = React.forwardRef<\n HTMLDivElement,\n React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>\n>(({ className, variant, ...props }, ref) => (\n <div\n ref={ref}\n role=\"alert\"\n className={cn(alertVariants({ variant }), className)}\n {...props}\n />\n));\nAlert.displayName = \"Alert\";\n\nconst AlertTitle = React.forwardRef<\n HTMLParagraphElement,\n React.HTMLAttributes<HTMLHeadingElement>\n>(({ className, ...props }, ref) => (\n <h5\n ref={ref}\n className={cn(\"mb-1 font-medium leading-none tracking-tight\", className)}\n {...props}\n />\n));\nAlertTitle.displayName = \"AlertTitle\";\n\nconst AlertDescription = React.forwardRef<\n HTMLParagraphElement,\n React.HTMLAttributes<HTMLParagraphElement>\n>(({ className, ...props }, ref) => (\n <div\n ref={ref}\n className={cn(\"text-sm [&_p]:leading-relaxed\", className)}\n {...props}\n />\n));\nAlertDescription.displayName = \"AlertDescription\";\n\nexport { Alert, AlertTitle, AlertDescription };\n","/**\n * 提示词编辑器对话框组件\n *\n * 提供查看、编辑、新建提示词文件的功能\n */\n\nimport { Alert, AlertDescription } from \"@/components/ui/alert\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from \"@/components/ui/dialog\";\nimport { Input } from \"@/components/ui/input\";\nimport { Label } from \"@/components/ui/label\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { apiClient } from \"@/services/api\";\nimport { AlertCircle, FileText, Loader2, Plus, Trash2 } from \"lucide-react\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport { toast } from \"sonner\";\n\n/**\n * 对话框模式\n */\ntype DialogMode = \"view\" | \"edit\" | \"create\";\n\ninterface PromptEditorDialogProps {\n /** 对话框是否打开 */\n open: boolean;\n /** 打开/关闭状态变化回调 */\n onOpenChange: (open: boolean) => void;\n /** 当前选中的提示词文件路径(用于编辑模式) */\n selectedPath?: string;\n /** 提示词文件更新后的回调 */\n onPromptUpdated?: () => void;\n /** 新提示词文件创建后的回调 */\n onPromptCreated?: (relativePath: string) => void;\n /** 提示词文件删除后的回调 */\n onPromptDeleted?: () => void;\n}\n\n/**\n * 提示词编辑器对话框组件\n */\nexport function PromptEditorDialog({\n open,\n onOpenChange,\n selectedPath,\n onPromptUpdated,\n onPromptCreated,\n onPromptDeleted,\n}: PromptEditorDialogProps) {\n const [mode, setMode] = useState<DialogMode>(\"view\");\n const [isLoading, setIsLoading] = useState(false);\n const [isSaving, setIsSaving] = useState(false);\n const [content, setContent] = useState(\"\");\n const [originalContent, setOriginalContent] = useState(\"\");\n const [fileName, setFileName] = useState(\"\");\n const [error, setError] = useState<string | null>(null);\n\n // 加载提示词文件内容\n const loadPromptContent = useCallback(async (path: string) => {\n setIsLoading(true);\n setError(null);\n try {\n const result = await apiClient.getPromptFileContent(path);\n setContent(result.content);\n setOriginalContent(result.content);\n setFileName(result.fileName);\n } catch (err) {\n setError(err instanceof Error ? err.message : \"加载提示词文件失败\");\n } finally {\n setIsLoading(false);\n }\n }, []);\n\n // 当对话框打开或选中的路径变化时,加载内容\n useEffect(() => {\n if (open) {\n if (mode === \"create\") {\n // 新建模式:清空内容\n setContent(\"\");\n setOriginalContent(\"\");\n setFileName(\"\");\n setError(null);\n } else if (selectedPath) {\n // 查看/编辑模式:加载文件内容\n loadPromptContent(selectedPath);\n } else {\n // 没有选中文件:切换到新建模式\n setMode(\"create\");\n setContent(\"\");\n setOriginalContent(\"\");\n setFileName(\"\");\n setError(null);\n }\n }\n }, [open, selectedPath, mode, loadPromptContent]);\n\n // 重置模式\n useEffect(() => {\n if (!open) {\n setMode(\"view\");\n }\n }, [open]);\n\n // 保存提示词\n const handleSave = async () => {\n if (mode === \"create\") {\n // 新建模式:验证文件名\n if (!fileName.trim()) {\n setError(\"请输入文件名\");\n return;\n }\n if (!fileName.endsWith(\".md\")) {\n setError(\"文件名必须以 .md 结尾\");\n return;\n }\n }\n\n setIsSaving(true);\n setError(null);\n try {\n if (mode === \"create\") {\n // 创建新文件\n const result = await apiClient.createPromptFile(fileName, content);\n toast.success(\"提示词文件创建成功\");\n onPromptCreated?.(result.relativePath);\n onOpenChange(false);\n } else if (selectedPath) {\n // 更新现有文件\n await apiClient.updatePromptFileContent(selectedPath, content);\n toast.success(\"提示词文件保存成功\");\n setOriginalContent(content);\n onPromptUpdated?.();\n setMode(\"view\");\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : \"保存失败\");\n } finally {\n setIsSaving(false);\n }\n };\n\n // 删除提示词\n const handleDelete = async () => {\n if (!selectedPath) return;\n\n // 确认删除\n if (!window.confirm(`确定要删除文件 \"${fileName}\" 吗?此操作无法撤销。`)) {\n return;\n }\n\n setIsSaving(true);\n setError(null);\n try {\n await apiClient.deletePromptFile(selectedPath);\n toast.success(\"提示词文件删除成功\");\n onPromptDeleted?.();\n onOpenChange(false);\n } catch (err) {\n setError(err instanceof Error ? err.message : \"删除失败\");\n } finally {\n setIsSaving(false);\n }\n };\n\n // 判断是否有修改\n const hasChanges = content !== originalContent;\n\n // 获取对话框标题\n const getTitle = () => {\n switch (mode) {\n case \"create\":\n return \"新建提示词文件\";\n case \"edit\":\n return \"编辑提示词\";\n default:\n return \"查看提示词\";\n }\n };\n\n // 获取对话框描述\n const getDescription = () => {\n if (mode === \"create\") {\n return \"创建一个新的系统提示词文件\";\n }\n return selectedPath || \"提示词文件\";\n };\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"sm:max-w-[700px]\">\n <DialogHeader>\n <DialogTitle className=\"flex items-center gap-2\">\n <FileText className=\"size-5\" />\n {getTitle()}\n </DialogTitle>\n <DialogDescription>{getDescription()}</DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4 py-4\">\n {error && (\n <Alert variant=\"destructive\">\n <AlertCircle className=\"size-4\" />\n <AlertDescription>{error}</AlertDescription>\n </Alert>\n )}\n\n {mode === \"create\" && (\n <div className=\"space-y-2\">\n <Label htmlFor=\"fileName\">文件名</Label>\n <Input\n id=\"fileName\"\n placeholder=\"例如:custom-prompt.md\"\n value={fileName}\n onChange={(e) => setFileName(e.target.value)}\n disabled={isSaving}\n />\n </div>\n )}\n\n {isLoading ? (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"size-6 animate-spin text-muted-foreground\" />\n <span className=\"ml-2 text-muted-foreground\">加载中...</span>\n </div>\n ) : (\n <div className=\"space-y-2\">\n <Label htmlFor=\"content\">提示词内容</Label>\n <Textarea\n id=\"content\"\n placeholder=\"请输入系统提示词内容...\"\n className=\"min-h-[300px] font-mono text-sm\"\n value={content}\n onChange={(e) => setContent(e.target.value)}\n disabled={isSaving || (mode === \"view\" && !isSaving)}\n />\n </div>\n )}\n </div>\n\n <DialogFooter className=\"gap-2 sm:gap-0\">\n {mode === \"view\" && selectedPath && (\n <>\n <Button\n variant=\"destructive\"\n size=\"sm\"\n onClick={handleDelete}\n disabled={isSaving}\n className=\"mr-auto\"\n >\n <Trash2 className=\"size-4 mr-1\" />\n 删除\n </Button>\n <Button\n variant=\"outline\"\n onClick={() => setMode(\"edit\")}\n disabled={isSaving}\n >\n 编辑\n </Button>\n <DialogClose asChild>\n <Button variant=\"secondary\">关闭</Button>\n </DialogClose>\n </>\n )}\n\n {mode === \"edit\" && (\n <>\n <Button\n variant=\"outline\"\n onClick={() => {\n setMode(\"view\");\n setContent(originalContent);\n setError(null);\n }}\n disabled={isSaving}\n >\n 取消\n </Button>\n <Button onClick={handleSave} disabled={isSaving || !hasChanges}>\n {isSaving ? (\n <>\n <Loader2 className=\"size-4 mr-1 animate-spin\" />\n 保存中...\n </>\n ) : (\n \"保存\"\n )}\n </Button>\n </>\n )}\n\n {mode === \"create\" && (\n <>\n <DialogClose asChild>\n <Button variant=\"outline\" disabled={isSaving}>\n 取消\n </Button>\n </DialogClose>\n <Button\n onClick={handleSave}\n disabled={isSaving || !content.trim() || !fileName.trim()}\n >\n {isSaving ? (\n <>\n <Loader2 className=\"size-4 mr-1 animate-spin\" />\n 创建中...\n </>\n ) : (\n <>\n <Plus className=\"size-4 mr-1\" />\n 创建\n </>\n )}\n </Button>\n </>\n )}\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n","import { Eye, EyeOff } from \"lucide-react\";\nimport * as React from \"react\";\n\nimport { Button } from \"@/components/ui/button\";\nimport { Input } from \"@/components/ui/input\";\nimport { cn } from \"@/lib/utils\";\n\ninterface PasswordInputProps\n extends React.ComponentPropsWithoutRef<typeof Input> {\n /** 是否显示密码 */\n showPassword?: boolean;\n /** 显示密码变更回调 */\n onShowPasswordChange?: (show: boolean) => void;\n}\n\n/**\n * 密码输入框组件\n *\n * 支持明文/密文切换,通过眼睛图标控制显示状态。\n */\nconst PasswordInput = React.forwardRef<HTMLInputElement, PasswordInputProps>(\n ({ className, showPassword, onShowPasswordChange, ...props }, ref) => {\n const [internalShow, setInternalShow] = React.useState(false);\n const isControlled = showPassword !== undefined;\n const isShow = isControlled ? showPassword : internalShow;\n\n // 开发环境下警告:受控模式但缺少回调\n if (\n process.env.NODE_ENV === \"development\" &&\n isControlled &&\n !onShowPasswordChange\n ) {\n console.warn(\n \"PasswordInput: showPassword 已作为受控属性传入,但未提供 onShowPasswordChange 回调,切换按钮将无法正常工作\"\n );\n }\n\n const handleToggle = () => {\n if (isControlled && onShowPasswordChange) {\n onShowPasswordChange(!showPassword);\n } else {\n setInternalShow(!internalShow);\n }\n };\n\n return (\n <div className=\"relative\">\n <Input\n type={isShow ? \"text\" : \"password\"}\n className={cn(\"pr-10\", className)}\n ref={ref}\n {...props}\n />\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"icon\"\n className=\"absolute right-0 top-0 h-full px-3 hover:bg-transparent\"\n onClick={handleToggle}\n aria-label={isShow ? \"隐藏密码\" : \"显示密码\"}\n aria-pressed={isShow}\n >\n {isShow ? (\n <EyeOff className=\"h-4 w-4 text-muted-foreground hover:text-foreground\" />\n ) : (\n <Eye className=\"h-4 w-4 text-muted-foreground hover:text-foreground\" />\n )}\n <span className=\"sr-only\">{isShow ? \"隐藏密码\" : \"显示密码\"}</span>\n </Button>\n </div>\n );\n }\n);\nPasswordInput.displayName = \"PasswordInput\";\n\nexport { PasswordInput };\n","/**\n * 语音交互设置对话框组件\n *\n * 在对话框中展示和编辑 ASR/LLM/TTS 配置。\n */\n\nimport { PromptEditorDialog } from \"@/components/prompt-editor-dialog\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from \"@/components/ui/dialog\";\nimport {\n Form,\n FormControl,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\nimport { PasswordInput } from \"@/components/ui/password-input\";\nimport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectTrigger,\n SelectValue,\n} from \"@/components/ui/select\";\nimport { Separator } from \"@/components/ui/separator\";\nimport { useWebSocketActions } from \"@/providers/WebSocketProvider\";\nimport { type PromptFileInfo, apiClient } from \"@/services/api\";\nimport { useConfig } from \"@/stores/config\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport type {\n ASRConfig,\n AppConfig,\n LLMConfig,\n TTSConfig,\n VoiceInfo,\n} from \"@xiaozhi-client/shared-types\";\nimport { Edit, Plus, SettingsIcon } from \"lucide-react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport { toast } from \"sonner\";\nimport z from \"zod\";\n\n/**\n * 语音交互配置表单 Schema\n */\nconst voiceInteractionSchema = z.object({\n asr: z.object({\n appid: z.string().optional(),\n accessToken: z.string().optional(),\n }),\n llm: z.object({\n model: z.string().min(1, { message: \"模型名称不能为空\" }),\n apiKey: z.string().min(1, { message: \"API 密钥不能为空\" }),\n baseURL: z.string().url({ message: \"请输入有效的 URL\" }),\n prompt: z.string().optional(),\n }),\n tts: z.object({\n appid: z.string().optional(),\n accessToken: z.string().optional(),\n voice_type: z.string().optional(),\n }),\n});\n\ntype VoiceInteractionFormValues = z.infer<typeof voiceInteractionSchema>;\n\n/**\n * 语音交互设置对话框组件\n *\n * 提供在对话框中编辑 ASR/LLM/TTS 配置的功能。\n */\nexport function VoiceInteractionSettingDialog() {\n const [open, setOpen] = useState(false);\n const [isLoading, setIsLoading] = useState(false);\n const [promptFiles, setPromptFiles] = useState<PromptFileInfo[]>([]);\n const [isLoadingPrompts, setIsLoadingPrompts] = useState(false);\n const [promptEditorOpen, setPromptEditorOpen] = useState(false);\n const [selectedPromptPath, setSelectedPromptPath] = useState<\n string | undefined\n >(undefined);\n const [voices, setVoices] = useState<VoiceInfo[]>([]);\n const [isLoadingVoices, setIsLoadingVoices] = useState(false);\n const config = useConfig();\n const { updateConfig } = useWebSocketActions();\n\n const form = useForm<VoiceInteractionFormValues>({\n resolver: zodResolver(voiceInteractionSchema),\n defaultValues: {\n asr: {\n appid: config?.asr?.appid || \"\",\n accessToken: config?.asr?.accessToken || \"\",\n },\n llm: {\n model: config?.llm?.model || \"\",\n apiKey: config?.llm?.apiKey || \"\",\n baseURL: config?.llm?.baseURL || \"\",\n prompt: config?.llm?.prompt || \"\",\n },\n tts: {\n appid: config?.tts?.appid || \"\",\n accessToken: config?.tts?.accessToken || \"\",\n voice_type: config?.tts?.voice_type || \"\",\n },\n },\n });\n\n // 使用 ref 追踪本次打开是否已初始化表单\n const initializedRef = useRef(false);\n\n // 当弹窗打开且 config 就绪时,初始化表单数据\n // 只在本次打开期间初始化一次,关闭时重置标记和表单状态\n useEffect(() => {\n if (open && !initializedRef.current && config) {\n form.reset({\n asr: {\n appid: config.asr?.appid || \"\",\n accessToken: config.asr?.accessToken || \"\",\n },\n llm: {\n model: config.llm?.model || \"\",\n apiKey: config.llm?.apiKey || \"\",\n baseURL: config.llm?.baseURL || \"\",\n prompt: config.llm?.prompt || \"\",\n },\n tts: {\n appid: config.tts?.appid || \"\",\n accessToken: config.tts?.accessToken || \"\",\n voice_type: config.tts?.voice_type || \"\",\n },\n });\n initializedRef.current = true;\n }\n // 弹窗关闭时重置初始化标记和表单状态\n if (!open) {\n initializedRef.current = false;\n form.reset();\n }\n }, [open, config, form]);\n\n // 加载提示词文件列表\n const loadPromptFiles = useCallback(async () => {\n setIsLoadingPrompts(true);\n try {\n const files = await apiClient.getPromptFiles();\n setPromptFiles(files);\n } catch (error) {\n console.error(\"加载提示词文件列表失败:\", error);\n setPromptFiles([]);\n } finally {\n setIsLoadingPrompts(false);\n }\n }, []);\n\n // 加载音色列表\n const loadVoices = useCallback(async () => {\n setIsLoadingVoices(true);\n try {\n const response = await apiClient.getTTSVoices();\n setVoices(response.voices);\n } catch (error) {\n console.error(\"加载音色列表失败:\", error);\n setVoices([]);\n } finally {\n setIsLoadingVoices(false);\n }\n }, []);\n\n // 对话框打开时加载提示词文件列表\n useEffect(() => {\n if (open) {\n loadPromptFiles();\n loadVoices();\n }\n }, [open, loadPromptFiles, loadVoices]);\n\n // 打开提示词编辑器(编辑模式)\n const handleEditPrompt = useCallback(() => {\n const currentPath = form.getValues(\"llm.prompt\");\n if (currentPath && currentPath !== \"__none__\") {\n setSelectedPromptPath(currentPath);\n } else {\n setSelectedPromptPath(undefined);\n }\n setPromptEditorOpen(true);\n }, [form]);\n\n // 打开提示词编辑器(新建模式)\n const handleCreatePrompt = useCallback(() => {\n setSelectedPromptPath(undefined);\n setPromptEditorOpen(true);\n }, []);\n\n // 提示词更新后的处理\n const handlePromptUpdated = useCallback(() => {\n loadPromptFiles();\n }, [loadPromptFiles]);\n\n // 提示词创建后的处理\n const handlePromptCreated = useCallback(\n (relativePath: string) => {\n loadPromptFiles();\n // 自动选择新创建的提示词文件\n form.setValue(\"llm.prompt\", relativePath);\n },\n [loadPromptFiles, form]\n );\n\n // 提示词删除后的处理\n const handlePromptDeleted = useCallback(() => {\n loadPromptFiles();\n // 清空当前选择\n form.setValue(\"llm.prompt\", \"\");\n }, [loadPromptFiles, form]);\n\n async function onSubmit(values: VoiceInteractionFormValues) {\n if (!config) {\n toast.error(\"配置数据未加载,请稍后重试\");\n return;\n }\n\n setIsLoading(true);\n try {\n // 构建新的配置对象\n // 当表单字段全为空时,使用空对象 {} 显式告知服务端要清空配置\n // 而不是使用 undefined(因为 undefined 在 JSON 序列化时会被丢弃)\n const newASR: ASRConfig | undefined =\n values.asr.appid || values.asr.accessToken\n ? {\n appid: values.asr.appid || undefined,\n accessToken: values.asr.accessToken || undefined,\n }\n : {};\n\n const newLLM: LLMConfig = {\n model: values.llm.model,\n apiKey: values.llm.apiKey,\n baseURL: values.llm.baseURL,\n prompt: values.llm.prompt || undefined,\n };\n\n const newTTS: TTSConfig | undefined =\n values.tts.appid || values.tts.accessToken || values.tts.voice_type\n ? {\n appid: values.tts.appid || undefined,\n accessToken: values.tts.accessToken || undefined,\n voice_type: values.tts.voice_type || undefined,\n }\n : {};\n\n const newConfig: AppConfig = {\n ...config,\n asr: newASR,\n llm: newLLM,\n tts: newTTS,\n };\n\n await updateConfig(newConfig);\n toast.success(\"语音交互配置已更新\");\n setOpen(false);\n } catch (error) {\n console.error(\"更新语音交互配置失败:\", error);\n toast.error(error instanceof Error ? error.message : \"更新配置失败\");\n } finally {\n setIsLoading(false);\n }\n }\n\n return (\n <>\n <Dialog open={open} onOpenChange={setOpen}>\n <DialogTrigger asChild>\n <Button variant=\"secondary\" size=\"icon\" className=\"size-8\">\n <SettingsIcon className=\"size-4\" />\n </Button>\n </DialogTrigger>\n <DialogContent className=\"sm:max-w-[600px]\">\n <DialogHeader>\n <DialogTitle>语音交互设置</DialogTitle>\n <DialogDescription>\n 配置 ASR(语音识别)、LLM(大语言模型)、TTS(语音合成)服务\n </DialogDescription>\n </DialogHeader>\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)}>\n <div className=\"max-h-[60vh] overflow-y-auto pr-2\">\n {/* ASR 配置区块 */}\n <div className=\"mb-4\">\n <h3 className=\"text-sm font-medium text-foreground mb-3\">\n ASR 配置\n </h3>\n <div className=\"grid gap-4\">\n <FormField\n control={form.control}\n name=\"asr.appid\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>应用 ID</FormLabel>\n <FormControl>\n <Input\n placeholder=\"请输入火山引擎语音识别应用 ID\"\n className=\"font-mono text-sm\"\n disabled={isLoading}\n {...field}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name=\"asr.accessToken\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>访问令牌</FormLabel>\n <FormControl>\n <PasswordInput\n placeholder=\"请输入访问令牌\"\n className=\"font-mono text-sm\"\n disabled={isLoading}\n autoComplete=\"off\"\n {...field}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n </div>\n </div>\n\n <Separator className=\"my-4\" />\n\n {/* LLM 配置区块 */}\n <div className=\"mb-4\">\n <h3 className=\"text-sm font-medium text-foreground mb-3\">\n LLM 配置\n </h3>\n <div className=\"grid gap-4\">\n <FormField\n control={form.control}\n name=\"llm.model\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>\n 模型名称 <span className=\"text-red-500\">*</span>\n </FormLabel>\n <FormControl>\n <Input\n placeholder=\"如:gpt-4、deepseek-chat\"\n className=\"font-mono text-sm\"\n disabled={isLoading}\n {...field}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name=\"llm.apiKey\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>\n API 密钥 <span className=\"text-red-500\">*</span>\n </FormLabel>\n <FormControl>\n <PasswordInput\n placeholder=\"请输入 API 密钥\"\n className=\"font-mono text-sm\"\n disabled={isLoading}\n autoComplete=\"off\"\n {...field}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name=\"llm.baseURL\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>\n API 基础地址 <span className=\"text-red-500\">*</span>\n </FormLabel>\n <FormControl>\n <Input\n placeholder=\"如:https://api.openai.com/v1\"\n className=\"font-mono text-sm\"\n disabled={isLoading}\n {...field}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name=\"llm.prompt\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>系统提示词文件</FormLabel>\n <div className=\"flex gap-2\">\n <FormControl>\n <Select\n disabled={isLoading || isLoadingPrompts}\n value={field.value || \"__none__\"}\n onValueChange={(value) => {\n // 将特殊值转换为空字符串\n field.onChange(\n value === \"__none__\" ? \"\" : value\n );\n }}\n >\n <SelectTrigger className=\"font-mono text-sm flex-1\">\n <SelectValue placeholder=\"选择提示词文件(可选)\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__none__\">\n 不使用提示词文件\n </SelectItem>\n {promptFiles.map((file) => (\n <SelectItem\n key={file.relativePath}\n value={file.relativePath}\n >\n {file.fileName}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </FormControl>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n onClick={handleEditPrompt}\n disabled={\n isLoading ||\n isLoadingPrompts ||\n !form.watch(\"llm.prompt\") ||\n form.watch(\"llm.prompt\") === \"__none__\"\n }\n title=\"编辑选中的提示词文件\"\n >\n <Edit className=\"size-4\" />\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n onClick={handleCreatePrompt}\n disabled={isLoading || isLoadingPrompts}\n title=\"新建提示词文件\"\n >\n <Plus className=\"size-4\" />\n </Button>\n </div>\n <FormMessage />\n </FormItem>\n )}\n />\n </div>\n </div>\n\n <Separator className=\"my-4\" />\n\n {/* TTS 配置区块 */}\n <div className=\"mb-4\">\n <h3 className=\"text-sm font-medium text-foreground mb-3\">\n TTS 配置\n </h3>\n <div className=\"grid gap-4\">\n <FormField\n control={form.control}\n name=\"tts.appid\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>应用 ID</FormLabel>\n <FormControl>\n <Input\n placeholder=\"请输入火山引擎语音合成应用 ID\"\n className=\"font-mono text-sm\"\n disabled={isLoading}\n {...field}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name=\"tts.accessToken\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>访问令牌</FormLabel>\n <FormControl>\n <PasswordInput\n placeholder=\"请输入访问令牌\"\n className=\"font-mono text-sm\"\n disabled={isLoading}\n autoComplete=\"off\"\n {...field}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name=\"tts.voice_type\"\n render={({ field }) => (\n <FormItem>\n <FormLabel>声音类型</FormLabel>\n <FormControl>\n <Select\n disabled={isLoading || isLoadingVoices}\n value={field.value || \"__none__\"}\n onValueChange={(value) => {\n field.onChange(\n value === \"__none__\" ? \"\" : value\n );\n }}\n >\n <SelectTrigger className=\"font-mono text-sm\">\n <SelectValue placeholder=\"选择音色(可选)\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"__none__\">\n 不指定音色\n </SelectItem>\n {field.value &&\n field.value !== \"__none__\" &&\n !voices.some(\n (v) => v.voiceType === field.value\n ) && (\n <SelectItem value={field.value}>\n {field.value} (自定义音色)\n </SelectItem>\n )}\n {Object.entries(\n voices.reduce(\n (acc, voice) => {\n if (!acc[voice.scene]) {\n acc[voice.scene] = [];\n }\n acc[voice.scene].push(voice);\n return acc;\n },\n {} as Record<string, VoiceInfo[]>\n )\n ).map(([scene, sceneVoices]) => (\n <SelectGroup key={scene}>\n <SelectLabel>{scene}</SelectLabel>\n {sceneVoices.map((voice) => (\n <SelectItem\n key={voice.voiceType}\n value={voice.voiceType}\n >\n {voice.name}\n </SelectItem>\n ))}\n </SelectGroup>\n ))}\n </SelectContent>\n </Select>\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n </div>\n </div>\n </div>\n <DialogFooter className=\"mt-4\">\n <DialogClose asChild>\n <Button type=\"button\" variant=\"outline\" disabled={isLoading}>\n 取消\n </Button>\n </DialogClose>\n <Button type=\"submit\" disabled={isLoading}>\n {isLoading ? \"保存中...\" : \"保存\"}\n </Button>\n </DialogFooter>\n </form>\n </Form>\n </DialogContent>\n </Dialog>\n\n {/* 提示词编辑器对话框 */}\n <PromptEditorDialog\n open={promptEditorOpen}\n onOpenChange={setPromptEditorOpen}\n selectedPath={selectedPromptPath}\n onPromptUpdated={handlePromptUpdated}\n onPromptCreated={handlePromptCreated}\n onPromptDeleted={handlePromptDeleted}\n />\n </>\n );\n}\n","/**\n * 实时语音卡片组件\n *\n * 展示 ASR/LLM/TTS 配置状态,提供语音交互设置入口。\n */\n\nimport {\n Card,\n CardDescription,\n CardFooter,\n CardHeader,\n CardTitle,\n} from \"@/components/ui/card\";\nimport { VoiceInteractionSettingDialog } from \"@/components/voice-interaction-setting-dialog\";\nimport { useVoiceInteractionConfig } from \"@/stores/config\";\nimport { useMemo } from \"react\";\nimport { MiniCircularProgress } from \"./mini-circular-progress\";\n\n/**\n * 检查 ASR 是否已配置\n */\nfunction isASRReady(\n asr: { appid?: string; accessToken?: string } | undefined\n): boolean {\n return Boolean(asr?.appid && asr?.accessToken);\n}\n\n/**\n * 检查 LLM 是否已配置\n */\nfunction isLLMReady(\n llm: { model?: string; apiKey?: string; baseURL?: string } | undefined\n): boolean {\n return Boolean(llm?.model && llm?.apiKey && llm?.baseURL);\n}\n\n/**\n * 检查 TTS 是否已配置\n */\nfunction isTTSReady(\n tts: { appid?: string; accessToken?: string; voice_type?: string } | undefined\n): boolean {\n return Boolean(tts?.appid && tts?.accessToken && tts?.voice_type);\n}\n\n/**\n * 实时语音卡片组件\n *\n * 显示 ASR/LLM/TTS 配置完成度,右上角显示配置完成度进度指示器。\n * 底部提供快速打开语音交互设置对话框的按钮。\n */\nexport function VoiceInteractionCard() {\n const { asr, llm, tts } = useVoiceInteractionConfig();\n\n // 计算已配置项数量\n const configuredCount = useMemo(() => {\n let count = 0;\n if (isASRReady(asr)) count++;\n if (isLLMReady(llm)) count++;\n if (isTTSReady(tts)) count++;\n return count;\n }, [asr, llm, tts]);\n\n const totalItems = 3;\n const completionRate = configuredCount / totalItems;\n\n // 生成状态描述\n const statusParts: string[] = [];\n if (isASRReady(asr)) statusParts.push(\"ASR\");\n if (isLLMReady(llm)) statusParts.push(\"LLM\");\n if (isTTSReady(tts)) statusParts.push(\"TTS\");\n\n return (\n <Card className=\"@container/card\">\n <CardHeader className=\"relative\">\n <CardDescription>实时语音</CardDescription>\n <CardTitle className=\"@[250px]/card:text-3xl text-2xl font-semibold tabular-nums\">\n {configuredCount}/{totalItems} 就绪\n </CardTitle>\n <div className=\"absolute right-4 top-4\">\n <MiniCircularProgress\n showValue={true}\n value={configuredCount}\n maxValue={totalItems}\n activeColor={\n completionRate >= 1\n ? \"#16a34a\"\n : completionRate >= 0.66\n ? \"#f59e0b\"\n : \"#f87171\"\n }\n inactiveColor=\"#e5e7eb\"\n size={30}\n symbol=\"\"\n />\n </div>\n </CardHeader>\n <CardFooter className=\"flex items-center justify-between gap-1 text-sm\">\n <div className=\"text-muted-foreground\">\n {completionRate === 1\n ? \"语音交互已就绪\"\n : statusParts.length > 0\n ? `${statusParts.join(\"、\")} 已配置`\n : \"请配置语音服务\"}\n </div>\n <VoiceInteractionSettingDialog />\n </CardFooter>\n </Card>\n );\n}\n","/**\n * 仪表板状态卡片容器组件\n *\n * 组合显示小智接入点、客户端连接、MCP 服务、实时语音和系统设置的状态卡片。\n */\n\nimport {\n ClientStatusCard,\n EndpointStatusCard,\n ServerStatusCard,\n SystemStatusCard,\n VoiceInteractionCard,\n} from \"@/components/status-cards\";\n\n/**\n * 仪表板状态卡片组件\n *\n * 按网格布局排列五个状态卡片:小智接入点、Xiaozhi Client 连接状态、MCP 服务数量、实时语音、系统设置。\n */\nexport function DashboardStatusCard() {\n return (\n <div className=\"*:data-[slot=card]:shadow-xs @xl/main:grid-cols-2 @5xl/main:grid-cols-5 grid grid-cols-1 gap-4 px-4 *:data-[slot=card]:bg-gradient-to-t *:data-[slot=card]:from-primary/5 *:data-[slot=card]:to-card dark:*:data-[slot=card]:bg-card lg:px-6\">\n <EndpointStatusCard />\n <ClientStatusCard />\n <ServerStatusCard />\n <VoiceInteractionCard />\n <SystemStatusCard />\n </div>\n );\n}\n","\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport { ChevronDownIcon, ChevronUpIcon } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\n\n/**\n * CollapsibleText 组件属性\n */\ninterface CollapsibleTextProps {\n /** 要展示的文本 */\n text: string;\n /** 最大展示字数(默认:100) */\n maxLength?: number;\n /** localStorage 存储键(可选,不传则不持久化) */\n storageKey?: string;\n /** 自定义样式类 */\n className?: string;\n /** 文字大小(默认:text-sm) */\n textSize?: \"text-xs\" | \"text-sm\" | \"text-base\" | \"text-lg\" | \"text-xl\";\n}\n\n/**\n * CollapsibleText - 通用文本折叠/展开组件\n *\n * 功能:\n * - 可设定最大展示字数\n * - 超过部分隐藏,显示省略号\n * - 显示\"展开\"按钮,点击后展示完整内容\n * - 展开后显示\"收起\"按钮,点击恢复折叠状态\n * - 支持使用 localStorage 持久化展开/收起状态\n */\nexport function CollapsibleText({\n text,\n maxLength = 100,\n storageKey,\n className,\n textSize = \"text-sm\",\n}: CollapsibleTextProps) {\n // 从 localStorage 读取初始状态\n const getInitialExpandedState = (): boolean => {\n if (!storageKey) return false;\n\n try {\n const stored = localStorage.getItem(storageKey);\n return stored === \"true\";\n } catch {\n return false;\n }\n };\n\n const [isExpanded, setIsExpanded] = useState<boolean>(\n getInitialExpandedState\n );\n\n // 状态变化时写入 localStorage\n useEffect(() => {\n if (!storageKey) return;\n\n try {\n localStorage.setItem(storageKey, String(isExpanded));\n } catch (error) {\n console.warn(\"无法写入 localStorage:\", error);\n }\n }, [isExpanded, storageKey]);\n\n // 当 storageKey 变化时,重新读取 localStorage\n useEffect(() => {\n if (storageKey) {\n const stored = localStorage.getItem(storageKey);\n setIsExpanded(stored === \"true\");\n }\n }, [storageKey]);\n\n // 切换展开/收起状态\n const toggleExpanded = () => {\n setIsExpanded((prev) => !prev);\n };\n\n // 空文本处理\n const displayText = text || \"-\";\n\n // 判断是否需要折叠\n const needsCollapsing = displayText.length > maxLength;\n\n return (\n <div className={cn(\"flex flex-col gap-1\", className)}>\n {/* 文本内容 */}\n {isExpanded || !needsCollapsing ? (\n <p className={cn(\"break-words\", textSize)}>{displayText}</p>\n ) : (\n <p className={cn(\"break-words\", textSize)}>\n {displayText.slice(0, maxLength)}\n <span className=\"text-muted-foreground\">...</span>\n </p>\n )}\n\n {/* 展开/收起按钮 */}\n {needsCollapsing && (\n <button\n type=\"button\"\n onClick={toggleExpanded}\n className={cn(\n \"text-primary hover:underline flex items-center gap-1 w-fit\",\n textSize\n )}\n >\n {isExpanded ? (\n <>\n 收起 <ChevronUpIcon className=\"h-4 w-4\" />\n </>\n ) : (\n <>\n 展开 <ChevronDownIcon className=\"h-4 w-4\" />\n </>\n )}\n </button>\n )}\n </div>\n );\n}\n","import * as TooltipPrimitive from \"@radix-ui/react-tooltip\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst TooltipProvider = TooltipPrimitive.Provider;\n\nconst Tooltip = TooltipPrimitive.Root;\n\nconst TooltipTrigger = TooltipPrimitive.Trigger;\n\nconst TooltipContent = React.forwardRef<\n React.ElementRef<typeof TooltipPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>\n>(({ className, sideOffset = 4, ...props }, ref) => (\n <TooltipPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n className={cn(\n \"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-tooltip-content-transform-origin]\",\n className\n )}\n {...props}\n />\n));\nTooltipContent.displayName = TooltipPrimitive.Content.displayName;\n\nexport { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };\n","import { z } from \"zod\";\n\n/**\n * 根据 JSON Schema 动态生成 Zod schema\n */\nexport function createZodSchemaFromJsonSchema(jsonSchema: any): z.ZodTypeAny {\n if (!jsonSchema || typeof jsonSchema !== \"object\") {\n return z.any();\n }\n\n switch (jsonSchema.type) {\n case \"string\": {\n if (jsonSchema.enum) {\n return z.enum(jsonSchema.enum as [string, ...string[]]);\n }\n let stringSchema = z.string();\n if (jsonSchema.minLength) {\n stringSchema = stringSchema.min(jsonSchema.minLength);\n }\n if (jsonSchema.maxLength) {\n stringSchema = stringSchema.max(jsonSchema.maxLength);\n }\n if (jsonSchema.pattern) {\n stringSchema = stringSchema.regex(new RegExp(jsonSchema.pattern));\n }\n return stringSchema;\n }\n\n case \"number\":\n case \"integer\": {\n let numberSchema = z.number();\n if (jsonSchema.type === \"integer\") {\n numberSchema = numberSchema.int();\n }\n if (typeof jsonSchema.minimum === \"number\") {\n numberSchema = numberSchema.min(jsonSchema.minimum);\n }\n if (typeof jsonSchema.maximum === \"number\") {\n numberSchema = numberSchema.max(jsonSchema.maximum);\n }\n if (typeof jsonSchema.multipleOf === \"number\") {\n numberSchema = numberSchema.multipleOf(jsonSchema.multipleOf);\n }\n return numberSchema;\n }\n\n case \"boolean\":\n return z.boolean();\n\n case \"array\":\n if (jsonSchema.items) {\n const itemSchema = createZodSchemaFromJsonSchema(jsonSchema.items);\n let arraySchema = z.array(itemSchema);\n if (typeof jsonSchema.minItems === \"number\") {\n arraySchema = arraySchema.min(jsonSchema.minItems);\n }\n if (typeof jsonSchema.maxItems === \"number\") {\n arraySchema = arraySchema.max(jsonSchema.maxItems);\n }\n return arraySchema;\n }\n return z.array(z.any());\n\n case \"object\":\n if (\n jsonSchema.properties &&\n Object.keys(jsonSchema.properties).length > 0\n ) {\n const shape: Record<string, z.ZodTypeAny> = {};\n const requiredFields = jsonSchema.required || [];\n\n for (const [key, propSchema] of Object.entries(jsonSchema.properties)) {\n let fieldSchema = createZodSchemaFromJsonSchema(propSchema);\n\n // 如果字段不是必填的,设为可选\n if (!requiredFields.includes(key)) {\n fieldSchema = fieldSchema.optional();\n }\n\n shape[key] = fieldSchema;\n }\n\n return z.object(shape);\n }\n return z.record(z.string(), z.any());\n\n default:\n return z.any();\n }\n}\n\n/**\n * 获取字段的默认值\n */\nexport function getDefaultValueForSchema(schema: any): any {\n if (!schema) return undefined;\n\n switch (schema.type) {\n case \"string\":\n if (schema.enum) return schema.enum[0];\n return \"\";\n\n case \"number\":\n case \"integer\":\n return 0;\n\n case \"boolean\":\n return false;\n\n case \"array\":\n return [];\n\n case \"object\":\n if (schema.properties) {\n const defaults: Record<string, any> = {};\n for (const [key, propSchema] of Object.entries(schema.properties)) {\n defaults[key] = getDefaultValueForSchema(propSchema);\n }\n return defaults;\n }\n return {};\n\n default:\n return undefined;\n }\n}\n\n/**\n * 根据 JSON Schema 生成默认值对象\n */\nexport function createDefaultValues(jsonSchema: any): Record<string, any> {\n if (!jsonSchema || !jsonSchema.properties) return {};\n\n const defaults: Record<string, any> = {};\n const requiredFields = jsonSchema.required || [];\n\n for (const [key, propSchema] of Object.entries(jsonSchema.properties)) {\n if (requiredFields.includes(key)) {\n defaults[key] = getDefaultValueForSchema(propSchema);\n } else {\n // 对于可选字段,只设置明显的默认值\n const defaultValue = getDefaultValueForSchema(propSchema);\n if (defaultValue !== undefined && defaultValue !== \"\") {\n defaults[key] = defaultValue;\n }\n }\n }\n\n return defaults;\n}\n","\"use client\";\n\nimport { Alert, AlertDescription } from \"@/components/ui/alert\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"@/components/ui/card\";\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n} from \"@/components/ui/dialog\";\nimport {\n Form,\n FormControl,\n FormDescription,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n} from \"@/components/ui/form\";\nimport { Input } from \"@/components/ui/input\";\nimport { ScrollArea } from \"@/components/ui/scroll-area\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@/components/ui/select\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"@/components/ui/tabs\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from \"@/components/ui/tooltip\";\nimport {\n createDefaultValues,\n createZodSchemaFromJsonSchema,\n} from \"@/lib/schema-utils\";\nimport { apiClient } from \"@/services/api\";\nimport { zodResolver } from \"@hookform/resolvers/zod\";\nimport {\n AlertCircle,\n BrushCleaningIcon,\n CheckIcon,\n Code,\n CopyIcon,\n InfoIcon,\n Loader2,\n PlayIcon,\n Plus,\n Trash2,\n Zap,\n} from \"lucide-react\";\nimport type React from \"react\";\nimport { memo, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { Controller, useFieldArray, useForm } from \"react-hook-form\";\nimport { toast } from \"sonner\";\nimport { z } from \"zod\";\n\n/**\n * 数组字段渲染器组件\n *\n * @private\n * @description 这是 ToolDebugDialog 的专用内部组件,用于渲染数组类型的 JSON Schema 字段。\n * 该组件针对工具调试场景进行了优化,支持嵌套对象和数组。\n *\n * 务实开发说明:此组件未作为独立组件导出,因为:\n * 1. 当前项目中只有 ToolDebugDialog 需要这种特定的 JSON Schema 数组渲染逻辑\n * 2. 其他组件(如 WorkflowParameterConfigDialog)的用途是配置元数据,而非渲染动态表单\n * 3. 避免预防性设计 - 如果未来其他组件需要类似功能,再考虑提取\n */\ninterface ArrayFieldProps {\n name: string;\n schema: any;\n form: any;\n renderFormField: (\n fieldName: string,\n fieldSchema: any\n ) => React.ReactElement | null;\n}\n\nconst ArrayField = memo(function ArrayField({\n name,\n schema,\n form,\n renderFormField,\n}: ArrayFieldProps) {\n const { fields, append, remove } = useFieldArray({\n control: form.control,\n name: name as any,\n });\n\n const addItem = () => {\n const itemSchema = schema.items;\n let newItem: any;\n\n switch (itemSchema.type) {\n case \"string\":\n newItem = itemSchema.enum ? itemSchema.enum[0] : \"\";\n break;\n case \"number\":\n case \"integer\":\n newItem = 0;\n break;\n case \"boolean\":\n newItem = false;\n break;\n case \"array\":\n newItem = [];\n break;\n case \"object\":\n newItem = {};\n break;\n default:\n newItem = \"\";\n }\n\n append(newItem);\n };\n\n return (\n <div className=\"space-y-2\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-medium\">\n 数组项目 ({schema.items?.type || \"unknown\"})\n </span>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={addItem}\n className=\"h-8 px-2\"\n >\n <Plus className=\"h-3 w-3 mr-1\" />\n 添加\n </Button>\n </div>\n {fields.length === 0 ? (\n <div className=\"text-center py-4 border border-dashed rounded-md\">\n <span className=\"text-sm text-muted-foreground\">暂无数组项目</span>\n </div>\n ) : (\n <div className=\"space-y-3\">\n {fields.map((field, index) => (\n <div\n key={field.id}\n className=\"relative p-3 border rounded-md bg-muted/20\"\n >\n <div className=\"absolute top-2 right-2\">\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => remove(index)}\n className=\"h-6 w-6 p-0 text-red-500 hover:text-red-700\"\n >\n <Trash2 className=\"h-3 w-3\" />\n </Button>\n </div>\n <div className=\"pr-8\">\n <span className=\"text-xs font-medium text-muted-foreground mb-2 block\">\n 项目 {index + 1}\n </span>\n <FormField\n control={form.control}\n name={`${name}.${index}` as any}\n render={() => (\n <FormItem>\n {(() => {\n if (\n schema.items?.type === \"object\" ||\n schema.items?.type === \"array\"\n ) {\n return (\n <div className=\"ml-6 border-l-2 border-muted pl-4\">\n {renderFormField(\n `${name}.${index}`,\n schema.items\n )}\n </div>\n );\n }\n return renderFormField(\n `${name}.${index}`,\n schema.items\n );\n })()}\n </FormItem>\n )}\n />\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n );\n});\n\n/**\n * 对象字段渲染器组件\n *\n * @private\n * @description 这是 ToolDebugDialog 的专用内部组件,用于渲染对象类型的 JSON Schema 字段。\n * 该组件支持嵌套属性、必填标记和类型徽章显示。\n *\n * 务实开发说明:此组件未作为独立组件导出,因为:\n * 1. 当前项目中只有 ToolDebugDialog 需要这种特定的 JSON Schema 对象渲染逻辑\n * 2. 其他组件(如 WorkflowParameterConfigDialog)的用途是配置元数据,而非渲染动态表单\n * 3. 避免预防性设计 - 如果未来其他组件需要类似功能,再考虑提取\n */\ninterface ObjectFieldProps {\n name: string;\n schema: any;\n form: any;\n renderFormField: (\n fieldName: string,\n fieldSchema: any\n ) => React.ReactElement | null;\n getTypeBadge: (type: string) => string;\n}\n\nconst ObjectField = memo(function ObjectField({\n name,\n schema,\n form,\n renderFormField,\n getTypeBadge,\n}: ObjectFieldProps) {\n if (!schema.properties || Object.keys(schema.properties).length === 0) {\n return (\n <div className=\"text-center py-4 border border-dashed rounded-md\">\n <span className=\"text-sm text-muted-foreground\">对象无定义字段</span>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4\">\n <span className=\"text-sm font-medium\">对象字段</span>\n {Object.entries(schema.properties).map(\n ([fieldName, fieldSchema]: [string, any]) => (\n <div\n key={`${name}-${fieldName}`}\n className=\"ml-6 border-l-2 border-muted pl-4\"\n >\n <FormField\n control={form.control}\n name={`${name}.${fieldName}` as any}\n render={() => (\n <FormItem>\n <div className=\"flex items-center gap-2\">\n <FormLabel>\n {schema.required?.includes(fieldName) && (\n <span className=\"text-red-500 mr-1\">*</span>\n )}\n {fieldName}\n </FormLabel>\n <Badge\n variant=\"secondary\"\n className={`text-xs ${getTypeBadge(fieldSchema.type)}`}\n >\n {fieldSchema.type}\n </Badge>\n {fieldSchema.description && (\n <Tooltip>\n <TooltipTrigger>\n <InfoIcon className=\"h-4 w-4 text-muted-foreground\" />\n </TooltipTrigger>\n <TooltipContent>\n <p className=\"max-w-xs whitespace-pre-wrap\">\n {fieldSchema.description}\n </p>\n </TooltipContent>\n </Tooltip>\n )}\n </div>\n {renderFormField(`${name}.${fieldName}`, fieldSchema)}\n {fieldSchema.description && (\n <FormDescription>{fieldSchema.description}</FormDescription>\n )}\n <FormMessage />\n </FormItem>\n )}\n />\n </div>\n )\n )}\n </div>\n );\n});\n\n/**\n * 无参数提示组件\n *\n * @private\n * @description 这是 ToolDebugDialog 的专用内部组件,用于显示无需输入参数的提示信息。\n *\n * 务实开发说明:此组件未作为独立组件导出,因为:\n * 1. 这是一个简单的 UI 组件,复制成本极低\n * 2. 其他场景的\"无参数\"提示可能需要不同的文案和样式\n * 3. 避免预防性设计 - 如果未来其他组件需要类似功能,再考虑提取\n */\nconst NoParamsMessage = memo(function NoParamsMessage() {\n return (\n <div className=\"h-full flex items-center justify-center\">\n <div className=\"text-center space-y-4 max-w-sm mx-auto p-6\">\n <div className=\"mx-auto w-16 h-16 bg-green-100 rounded-full flex items-center justify-center\">\n <CheckIcon className=\"h-8 w-8 text-green-600\" />\n </div>\n <div className=\"space-y-2\">\n <h3 className=\"text-lg font-semibold text-foreground\">\n 无需输入参数\n </h3>\n <p className=\"text-sm text-muted-foreground\">\n 点击\"调用工具\"按钮执行,无需输入任何参数。\n </p>\n </div>\n </div>\n </div>\n );\n});\n\n/**\n * 表单渲染器组件\n *\n * @private\n * @description 这是 ToolDebugDialog 的专用内部组件,用于根据 JSON Schema 渲染完整的表单界面。\n * 该组件遍历工具的 inputSchema 属性,使用 renderFormField 函数渲染每个字段。\n *\n * 务实开发说明:此组件未作为独立组件导出,因为:\n * 1. 当前项目中只有 ToolDebugDialog 需要 JSON Schema 表单渲染\n * 2. 其他组件(如 WorkflowParameterConfigDialog)使用的是固定的参数配置表单,不需要动态 Schema 渲染\n * 3. 避免预防性设计 - 如果未来其他组件需要类似功能,再考虑提取\n */\ninterface FormRendererProps {\n tool: ToolDebugDialogProps[\"tool\"];\n form: any;\n renderFormField: (\n fieldName: string,\n fieldSchema: any\n ) => React.ReactElement | null;\n}\n\nconst FormRenderer = memo(function FormRenderer({\n tool,\n form,\n renderFormField,\n}: FormRendererProps) {\n if (!tool?.inputSchema?.properties) {\n return (\n <div className=\"text-center py-8\">\n <Code className=\"h-8 w-8 mx-auto mb-2 opacity-50\" />\n <p className=\"text-muted-foreground\">该工具无参数定义</p>\n </div>\n );\n }\n\n return (\n <Form {...form}>\n <ScrollArea className=\"h-full\">\n <div className=\"space-y-4 p-2\">\n {Object.entries(tool.inputSchema.properties).map(\n ([fieldName, fieldSchema]: [string, any]) => (\n <FormField\n key={`${tool.name}-${fieldName}`} // 添加工具名称作为前缀,确保 key 的唯一性和稳定性\n control={form.control}\n name={fieldName as any}\n render={() => (\n <FormItem>\n <div className=\"flex items-center gap-2\">\n <FormLabel>\n {tool.inputSchema.required?.includes(fieldName) && (\n <span className=\"text-red-500 mr-1\">*</span>\n )}\n {fieldName}\n </FormLabel>\n <Badge\n variant=\"secondary\"\n className={`text-xs ${\n fieldSchema.type === \"string\"\n ? \"bg-blue-100 text-blue-800\"\n : fieldSchema.type === \"number\" ||\n fieldSchema.type === \"integer\"\n ? \"bg-green-100 text-green-800\"\n : fieldSchema.type === \"boolean\"\n ? \"bg-purple-100 text-purple-800\"\n : fieldSchema.type === \"array\"\n ? \"bg-orange-100 text-orange-800\"\n : fieldSchema.type === \"object\"\n ? \"bg-gray-100 text-gray-800\"\n : \"bg-gray-100 text-gray-800\"\n }`}\n >\n {fieldSchema.type}\n </Badge>\n {fieldSchema.description && (\n <Tooltip>\n <TooltipTrigger>\n <InfoIcon className=\"h-4 w-4 text-muted-foreground\" />\n </TooltipTrigger>\n <TooltipContent>\n <p className=\"max-w-xs whitespace-pre-wrap\">\n {fieldSchema.description}\n </p>\n </TooltipContent>\n </Tooltip>\n )}\n </div>\n {renderFormField(fieldName, fieldSchema)}\n <FormMessage />\n </FormItem>\n )}\n />\n )\n )}\n </div>\n </ScrollArea>\n </Form>\n );\n});\n\ninterface ToolDebugDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n tool: {\n name: string;\n serverName: string;\n toolName: string;\n description?: string;\n inputSchema?: any;\n } | null;\n}\n\nexport function ToolDebugDialog({\n open,\n onOpenChange,\n tool,\n}: ToolDebugDialogProps) {\n const [inputMode, setInputMode] = useState<\"form\" | \"json\">(\"form\");\n const [jsonInput, setJsonInput] = useState<string>(\"{\\n \\n}\");\n const [result, setResult] = useState<any>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [copied, setCopied] = useState(false);\n const copiedTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n // 创建动态 schema\n const formSchema = useMemo(() => {\n if (!tool?.inputSchema) return z.object({});\n return createZodSchemaFromJsonSchema(tool.inputSchema);\n }, [tool?.inputSchema]);\n\n // 创建默认值\n const defaultValues = useMemo(() => {\n if (!tool?.inputSchema) return {};\n return createDefaultValues(tool.inputSchema);\n }, [tool?.inputSchema]);\n\n // 初始化表单\n const form = useForm({\n resolver: zodResolver(formSchema as any),\n defaultValues,\n mode: \"onChange\",\n });\n\n // 当工具变化时重置表单\n useEffect(() => {\n if (tool?.inputSchema) {\n // 重置表单到默认值\n form.reset(defaultValues);\n try {\n setJsonInput(JSON.stringify(defaultValues, null, 2));\n } catch {\n setJsonInput(\"{\\n \\n}\");\n }\n } else {\n form.reset({});\n setJsonInput(\"{\\n \\n}\");\n }\n }, [tool?.inputSchema, defaultValues, form]); // 添加 form 依赖以满足 linter 要求\n\n useEffect(() => {\n return () => {\n if (copiedTimerRef.current) {\n clearTimeout(copiedTimerRef.current);\n }\n };\n }, []);\n\n // 重置状态\n const resetState = useCallback(() => {\n setInputMode(\"form\");\n setJsonInput(\"{\\n \\n}\");\n setResult(null);\n setError(null);\n setCopied(false);\n // 只在有工具且有输入schema时才重置表单\n if (tool?.inputSchema) {\n form.reset(defaultValues);\n }\n }, [tool?.inputSchema, defaultValues, form]);\n\n // 处理Tab切换\n const handleModeChange = useCallback(\n (newMode: \"form\" | \"json\") => {\n if (newMode === \"json\" && inputMode === \"form\") {\n // 从表单模式切换到JSON模式时,同步数据\n const currentValues = form.getValues();\n try {\n setJsonInput(JSON.stringify(currentValues, null, 2));\n } catch {\n setJsonInput(\"{\\n \\n}\");\n }\n } else if (newMode === \"form\" && inputMode === \"json\") {\n // 从JSON模式切换到表单模式时,同步数据\n try {\n const parsedData = JSON.parse(jsonInput);\n // 使用 setValue 而不是 reset 来避免表单重新初始化导致的失焦\n for (const key of Object.keys(parsedData)) {\n form.setValue(key as any, parsedData[key]);\n }\n } catch {\n // JSON 解析失败,保持表单数据不变\n }\n }\n setInputMode(newMode);\n },\n [inputMode, jsonInput, form]\n );\n\n // 当弹窗关闭时重置状态\n const handleOpenChange = useCallback(\n (newOpen: boolean) => {\n if (!newOpen) {\n resetState();\n }\n onOpenChange(newOpen);\n },\n [onOpenChange, resetState]\n );\n\n // 验证JSON格式\n const validateJSON = useCallback((jsonString: string) => {\n try {\n JSON.parse(jsonString);\n return true;\n } catch {\n return false;\n }\n }, []);\n\n // 调用工具\n const handleCallTool = useCallback(async () => {\n if (!tool) return;\n\n let args: any;\n\n // 检查是否无参数工具\n const hasNoParams =\n !tool?.inputSchema?.properties ||\n Object.keys(tool.inputSchema.properties).length === 0;\n\n if (hasNoParams) {\n // 无参数工具使用空对象\n args = {};\n } else if (inputMode === \"form\") {\n const values = form.getValues();\n const isValid = await form.trigger();\n if (!isValid) {\n toast.error(\"请检查表单中的错误\");\n return;\n }\n args = values;\n } else {\n // 验证JSON格式\n if (!validateJSON(jsonInput)) {\n toast.error(\"输入参数不是有效的JSON格式\");\n return;\n }\n args = JSON.parse(jsonInput);\n }\n\n setLoading(true);\n setError(null);\n setResult(null);\n\n try {\n const response = await apiClient.callTool(\n tool.serverName,\n tool.toolName,\n args\n );\n\n setResult(response);\n toast.success(\"工具调用成功\");\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : \"调用工具失败\";\n setError(errorMessage);\n toast.error(errorMessage);\n } finally {\n setLoading(false);\n }\n }, [tool, inputMode, form, jsonInput, validateJSON]);\n\n // 复制结果\n const handleCopy = useCallback(async () => {\n const content = result ? JSON.stringify(result, null, 2) : error || \"\";\n try {\n await navigator.clipboard.writeText(content);\n setCopied(true);\n toast.success(\"已复制到剪贴板\");\n if (copiedTimerRef.current) {\n clearTimeout(copiedTimerRef.current);\n }\n copiedTimerRef.current = setTimeout(() => setCopied(false), 2000);\n } catch {\n toast.error(\"复制失败\");\n }\n }, [result, error]);\n\n // 清空输入\n const handleClear = useCallback(() => {\n setResult(null);\n setError(null);\n\n if (inputMode === \"form\" && tool?.inputSchema) {\n // 在表单模式下,重置为默认值\n form.reset(defaultValues);\n try {\n setJsonInput(JSON.stringify(defaultValues, null, 2));\n } catch {\n setJsonInput(\"{\\n \\n}\");\n }\n } else {\n // 在JSON模式或无schema时,清空JSON输入\n setJsonInput(\"{\\n \\n}\");\n if (tool?.inputSchema) {\n // 如果有schema,也重置表单\n form.reset(defaultValues);\n } else {\n form.reset({});\n }\n }\n }, [inputMode, tool?.inputSchema, defaultValues, form]);\n\n // 渲染表单字段的辅助函数 - 使用 useMemo 优化性能\n const renderFormField = useMemo(() => {\n const getTypeBadge = (type: string) => {\n const colors: Record<string, string> = {\n string: \"bg-blue-100 text-blue-800\",\n number: \"bg-green-100 text-green-800\",\n integer: \"bg-green-100 text-green-800\",\n boolean: \"bg-purple-100 text-purple-800\",\n array: \"bg-orange-100 text-orange-800\",\n object: \"bg-gray-100 text-gray-800\",\n };\n\n return colors[type] || \"bg-gray-100 text-gray-800\";\n };\n\n return (fieldName: string, fieldSchema: any): React.ReactElement | null => {\n switch (fieldSchema.type) {\n case \"string\":\n if (fieldSchema.enum) {\n return (\n <Controller\n name={fieldName as any}\n control={form.control}\n render={({ field }) => (\n <Select value={field.value} onValueChange={field.onChange}>\n <FormControl>\n <SelectTrigger>\n <SelectValue placeholder={`选择${fieldName}`} />\n </SelectTrigger>\n </FormControl>\n <SelectContent>\n {fieldSchema.enum.map((option: string) => (\n <SelectItem key={option} value={option}>\n {option}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n />\n );\n }\n return (\n <Controller\n name={fieldName as any}\n control={form.control}\n render={({ field }) => (\n <FormControl>\n <Input\n {...field}\n placeholder={`输入${fieldName}`}\n type={\n fieldSchema.format === \"password\" ? \"password\" : \"text\"\n }\n />\n </FormControl>\n )}\n />\n );\n\n case \"number\":\n case \"integer\":\n return (\n <Controller\n name={fieldName as any}\n control={form.control}\n render={({ field }) => (\n <FormControl>\n <Input\n {...field}\n type=\"number\"\n placeholder={`输入${fieldName}`}\n step={fieldSchema.type === \"integer\" ? \"1\" : \"0.1\"}\n onChange={(e) => {\n const value = e.target.value;\n field.onChange(value === \"\" ? \"\" : Number(value));\n }}\n />\n </FormControl>\n )}\n />\n );\n\n case \"boolean\":\n return (\n <Controller\n name={fieldName as any}\n control={form.control}\n render={({ field }) => (\n <Select\n value={field.value?.toString()}\n onValueChange={(value) => field.onChange(value === \"true\")}\n >\n <FormControl>\n <SelectTrigger>\n <SelectValue placeholder={`选择${fieldName}`} />\n </SelectTrigger>\n </FormControl>\n <SelectContent>\n <SelectItem value=\"true\">true</SelectItem>\n <SelectItem value=\"false\">false</SelectItem>\n </SelectContent>\n </Select>\n )}\n />\n );\n\n case \"array\":\n return (\n <ArrayField\n name={fieldName}\n schema={fieldSchema}\n form={form}\n renderFormField={renderFormField}\n />\n );\n\n case \"object\":\n return (\n <ObjectField\n name={fieldName}\n schema={fieldSchema}\n form={form}\n renderFormField={renderFormField}\n getTypeBadge={getTypeBadge}\n />\n );\n\n default:\n return (\n <Controller\n name={fieldName as any}\n control={form.control}\n render={({ field }) => (\n <FormControl>\n <Input {...field} placeholder={`输入${fieldName}`} />\n </FormControl>\n )}\n />\n );\n }\n };\n }, [form]);\n\n // 格式化结果显示\n const formatResult = useCallback((data: any) => {\n try {\n return JSON.stringify(data, null, 2);\n } catch {\n return String(data);\n }\n }, []);\n\n // 检测操作系统并获取快捷键文本\n const getShortcutText = useCallback(() => {\n if (typeof window === \"undefined\") return \"⌘+Enter\";\n const isMac = /Mac|iPhone|iPad|iPod/.test(navigator.platform);\n return isMac ? \"⌘+Enter\" : \"Ctrl+Enter\";\n }, []);\n\n // 处理键盘事件\n const handleKeyDown = useCallback(\n async (event: KeyboardEvent) => {\n // 检查是否是 Command+Enter (Mac) 或 Ctrl+Enter (Windows/Linux)\n const isMac = /Mac|iPhone|iPad|iPod/.test(navigator.platform);\n const isShortcutKey = isMac\n ? event.metaKey && event.key === \"Enter\"\n : event.ctrlKey && event.key === \"Enter\";\n\n if (isShortcutKey && open && !loading) {\n // 阻止默认行为\n event.preventDefault();\n\n // 检查是否无参数工具,或者是有参数工具且JSON模式时验证格式\n const hasNoParams =\n !tool?.inputSchema?.properties ||\n Object.keys(tool.inputSchema.properties).length === 0;\n if (!hasNoParams && inputMode === \"json\" && !validateJSON(jsonInput)) {\n toast.error(\"输入参数不是有效的JSON格式\");\n return;\n }\n\n // 调用工具\n await handleCallTool();\n }\n },\n [\n open,\n loading,\n inputMode,\n jsonInput,\n validateJSON,\n handleCallTool,\n tool?.inputSchema?.properties,\n ]\n );\n\n // 添加键盘事件监听器\n useEffect(() => {\n if (open) {\n window.addEventListener(\"keydown\", handleKeyDown);\n return () => {\n window.removeEventListener(\"keydown\", handleKeyDown);\n };\n }\n }, [open, handleKeyDown]);\n\n return (\n <Dialog open={open} onOpenChange={handleOpenChange}>\n <TooltipProvider>\n <DialogContent className=\"max-w-4xl max-h-[90vh] flex flex-col\">\n <DialogHeader>\n <DialogTitle className=\"flex items-center gap-2\">\n <Zap className=\"h-5 w-5\" />\n 工具调试\n </DialogTitle>\n </DialogHeader>\n\n {tool && (\n <div className=\"flex flex-col gap-4 h-[80vh]\">\n {/* 工具信息 */}\n <Card>\n <CardHeader className=\"pb-0\">\n <CardTitle className=\"text-base flex items-center gap-2\">\n <Badge variant=\"secondary\">{tool.serverName}</Badge>\n {tool.toolName}\n </CardTitle>\n </CardHeader>\n <CardContent>\n {tool.description && (\n <p className=\"text-sm text-muted-foreground mt-2\">\n {tool.description}\n </p>\n )}\n </CardContent>\n </Card>\n\n {/* 输入参数 */}\n <div className=\"flex-1 flex min-h-0 w-full overflow-hidden\">\n <div className=\"w-1/2 flex flex-col gap-2 flex-shrink-0 overflow-hidden pr-0.5\">\n <div className=\"flex items-center justify-between h-[40px]\">\n <h3 className=\"text-sm font-medium\">输入参数</h3>\n {tool?.inputSchema?.properties &&\n Object.keys(tool.inputSchema.properties).length > 0 && (\n <Tabs\n value={inputMode}\n onValueChange={(value) =>\n handleModeChange(value as \"form\" | \"json\")\n }\n >\n <TabsList className=\"grid w-full grid-cols-2\">\n <TabsTrigger value=\"form\" className=\"text-xs\">\n 表单模式\n </TabsTrigger>\n <TabsTrigger value=\"json\" className=\"text-xs\">\n 高级模式\n </TabsTrigger>\n </TabsList>\n </Tabs>\n )}\n </div>\n <div className=\"flex-1 min-h-0\">\n {tool?.inputSchema?.properties &&\n Object.keys(tool.inputSchema.properties).length > 0 ? (\n <Tabs\n value={inputMode}\n onValueChange={(value) =>\n handleModeChange(value as \"form\" | \"json\")\n }\n className=\"h-full flex flex-col\"\n >\n <TabsContent\n value=\"form\"\n className=\"flex-1 data-[state=active]:flex data-[state=active]:flex-col mt-0\"\n >\n <FormRenderer\n tool={tool}\n form={form}\n renderFormField={renderFormField}\n />\n </TabsContent>\n <TabsContent\n value=\"json\"\n className=\"flex-1 data-[state=active]:flex data-[state=active]:flex-col mt-0\"\n >\n <div className=\"flex-1 flex flex-col\">\n <Textarea\n value={jsonInput}\n onChange={(e) => setJsonInput(e.target.value)}\n placeholder=\"请输入JSON格式的参数...\"\n className=\"flex-1 font-mono text-sm resize-none min-h-[200px]\"\n disabled={loading}\n />\n {!validateJSON(jsonInput) &&\n jsonInput.trim() !== \"{\\n \\n}\" && (\n <Alert className=\"mt-2\">\n <AlertCircle className=\"h-4 w-4\" />\n <AlertDescription>\n JSON格式错误,请检查输入\n </AlertDescription>\n </Alert>\n )}\n </div>\n </TabsContent>\n </Tabs>\n ) : (\n <NoParamsMessage />\n )}\n </div>\n </div>\n\n {/* 结果显示 */}\n <div className=\"w-1/2 flex flex-col gap-2 flex-shrink-0 overflow-hidden pl-0.5\">\n <div className=\"flex items-center justify-between h-[40px]\">\n <h3 className=\"text-sm font-medium\">调用结果</h3>\n {(result || error) && (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={handleCopy}\n className=\"gap-0\"\n >\n {copied ? (\n <>\n <CheckIcon className=\"h-4 w-4 mr-1\" />\n 已复制\n </>\n ) : (\n <>\n <CopyIcon className=\"h-4 w-4 mr-1\" />\n 复制结果\n </>\n )}\n </Button>\n )}\n </div>\n <div className=\"flex-1 min-h-0\">\n {loading ? (\n <div className=\"h-full flex items-center justify-center border rounded-md\">\n <div className=\"flex flex-col items-center gap-2\">\n <Loader2 className=\"h-8 w-8 animate-spin\" />\n <span className=\"text-sm text-muted-foreground\">\n 正在调用工具...\n </span>\n </div>\n </div>\n ) : error ? (\n <div className=\"h-full\">\n <Alert variant=\"destructive\" className=\"h-full\">\n <AlertDescription className=\"font-mono text-sm whitespace-pre-wrap break-words\">\n {error}\n </AlertDescription>\n </Alert>\n </div>\n ) : result ? (\n <ScrollArea className=\"h-full border rounded-md\">\n <pre className=\"p-3 text-sm font-mono whitespace-pre-wrap break-words min-w-0\">\n {formatResult(result)}\n </pre>\n </ScrollArea>\n ) : (\n <div className=\"h-full flex items-center justify-center border rounded-md\">\n <div className=\"text-center text-muted-foreground\">\n <Code className=\"h-8 w-8 mx-auto mb-2 opacity-50\" />\n <p>等待调用工具...</p>\n </div>\n </div>\n )}\n </div>\n </div>\n </div>\n\n {/* 底部操作按钮 */}\n <div className=\"flex justify-end gap-2 pt-4 border-t\">\n <Button\n variant=\"outline\"\n onClick={handleClear}\n disabled={loading}\n >\n <BrushCleaningIcon className=\"h-4 w-4\" />\n 清空\n </Button>\n <Button\n onClick={handleCallTool}\n disabled={\n loading ||\n // 只有有参数工具且在JSON模式时才检查JSON格式\n (() => {\n const hasNoParams =\n !tool?.inputSchema?.properties ||\n Object.keys(tool.inputSchema.properties).length === 0;\n return (\n !hasNoParams &&\n inputMode === \"json\" &&\n !validateJSON(jsonInput)\n );\n })()\n }\n >\n {loading ? (\n <>\n <Loader2 className=\"h-4 w-4 mr-2 animate-spin\" />\n 调用中...\n </>\n ) : (\n <>\n <PlayIcon className=\"h-4 w-4\" />\n {(() => {\n const hasNoParams =\n !tool?.inputSchema?.properties ||\n Object.keys(tool.inputSchema.properties).length === 0;\n return hasNoParams ? \"直接调用\" : \"调用工具\";\n })()} ({getShortcutText()})\n </>\n )}\n </Button>\n </div>\n </div>\n )}\n </DialogContent>\n </TooltipProvider>\n </Dialog>\n );\n}\n","import * as SwitchPrimitives from \"@radix-ui/react-switch\";\nimport * as React from \"react\";\n\nimport { cn } from \"@/lib/utils\";\n\nconst Switch = React.forwardRef<\n React.ElementRef<typeof SwitchPrimitives.Root>,\n React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>\n>(({ className, ...props }, ref) => (\n <SwitchPrimitives.Root\n className={cn(\n \"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input\",\n className\n )}\n {...props}\n ref={ref}\n >\n <SwitchPrimitives.Thumb\n className={cn(\n \"pointer-events-none block h-4 w-4 rounded-full bg-background shadow-lg ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0\"\n )}\n />\n </SwitchPrimitives.Root>\n));\nSwitch.displayName = SwitchPrimitives.Root.displayName;\n\nexport { Switch };\n","/**\n * 工具搜索 Hook\n *\n * 提供工具列表搜索和筛选功能,支持根据服务名、工具名、描述进行筛选\n */\n\nimport type { ToolRowData } from \"@/components/mcp-tool/mcp-tool-table\";\nimport { useCallback, useMemo, useState } from \"react\";\n\ninterface UseToolSearchResult {\n /** 搜索关键词 */\n searchValue: string;\n /** 设置搜索关键词 */\n setSearchValue: (value: string) => void;\n /** 筛选后的工具列表 */\n filteredTools: ToolRowData[];\n /** 清除搜索 */\n clearSearch: () => void;\n}\n\n/**\n * 工具搜索状态管理 Hook\n * 提供搜索功能,支持根据服务名、工具名、描述进行筛选\n */\nexport function useToolSearch(tools: ToolRowData[]): UseToolSearchResult {\n const [searchValue, setSearchValue] = useState(\"\");\n\n // 确保 tools 是有效数组\n const safeTools = Array.isArray(tools) ? tools : [];\n\n // 筛选匹配的工具\n const filteredTools = useMemo(() => {\n if (!searchValue.trim()) return safeTools;\n const keyword = searchValue.toLowerCase();\n return safeTools.filter(\n (tool) =>\n (tool?.serverName?.toLowerCase() || \"\").includes(keyword) ||\n (tool?.toolName?.toLowerCase() || \"\").includes(keyword) ||\n (tool?.description?.toLowerCase() || \"\").includes(keyword)\n );\n }, [safeTools, searchValue]);\n\n const clearSearch = useCallback(() => {\n setSearchValue(\"\");\n }, []);\n\n return {\n searchValue,\n setSearchValue,\n filteredTools,\n clearSearch,\n };\n}\n","\"use client\";\n\n/**\n * 工具排序配置持久化 Hook\n *\n * 自动将用户选择的工具排序方式保存到 localStorage,支持按名称、启用状态、使用次数、最后使用时间等字段排序\n */\n\nimport type { ToolSortConfig } from \"@/components/mcp-tool/tool-sort-selector\";\nimport type { ToolSortField } from \"@/components/mcp-tool/tool-sort-selector\";\nimport { useSortPersistence } from \"@/hooks/useSortPersistence\";\n\n/** 有效的排序字段列表 */\nconst VALID_SORT_FIELDS: ToolSortField[] = [\n \"name\",\n \"enabled\",\n \"usageCount\",\n \"lastUsedTime\",\n];\n\n/**\n * 工具排序配置持久化 Hook\n * 自动将用户选择的排序方式保存到 localStorage\n */\nexport function useToolSortPersistence() {\n return useSortPersistence<ToolSortConfig>({\n storageKey: \"mcp-tool-sort-config\",\n defaultConfig: { field: \"name\" },\n validFields: VALID_SORT_FIELDS,\n loggerName: \"useToolSortPersistence\",\n });\n}\n","\"use client\";\n\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@/components/ui/select\";\n\nexport type ToolSortField = \"name\" | \"enabled\" | \"usageCount\" | \"lastUsedTime\";\n\nexport interface ToolSortConfig {\n field: ToolSortField;\n}\n\ninterface ToolSortSelectorProps {\n value: ToolSortConfig;\n onChange: (config: ToolSortConfig) => void;\n}\n\nconst SORT_OPTIONS = [\n { value: \"name\", label: \"按名称排序\" },\n { value: \"enabled\", label: \"按状态排序\" },\n { value: \"usageCount\", label: \"按使用次数排序\" },\n { value: \"lastUsedTime\", label: \"按最近使用排序\" },\n];\n\nexport function ToolSortSelector({ value, onChange }: ToolSortSelectorProps) {\n return (\n <div className=\"flex items-center gap-2\">\n <Select\n value={value.field}\n onValueChange={(field) => onChange({ field: field as ToolSortField })}\n >\n <SelectTrigger id=\"sort-field\" className=\"w-40\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {SORT_OPTIONS.map((option) => (\n <SelectItem key={option.value} value={option.value}>\n {option.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n );\n}\n","\"use client\";\n\nimport { CollapsibleText } from \"@/components/common/collapsible-text\";\nimport { ToolDebugDialog } from \"@/components/tool-debug-dialog\";\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n} from \"@/components/ui/alert-dialog\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport { Switch } from \"@/components/ui/switch\";\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from \"@/components/ui/table\";\nimport { useToolPagination } from \"@/hooks/useToolPagination\";\nimport { useToolSearch } from \"@/hooks/useToolSearch\";\nimport { useToolSortPersistence } from \"@/hooks/useToolSortPersistence\";\nimport { cn } from \"@/lib/utils\";\nimport { apiClient } from \"@/services/api\";\nimport type { CustomMCPToolWithStats } from \"@xiaozhi-client/shared-types\";\nimport { CoffeeIcon, Loader2, ZapIcon } from \"lucide-react\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport { toast } from \"sonner\";\nimport { ToolPagination } from \"./tool-pagination\";\nimport { ToolSearchInput } from \"./tool-search-input\";\nimport { ToolSortSelector } from \"./tool-sort-selector\";\n\n// 服务名称常量\nconst UNKNOWN_SERVICE_NAME = \"未知服务\";\nconst CUSTOM_SERVICE_NAME = \"自定义服务\";\n\nexport interface ToolRowData {\n name: string;\n serverName: string;\n toolName: string;\n description: string;\n enabled: boolean;\n usageCount: number;\n lastUsedTime: string;\n inputSchema: any;\n}\n\ninterface McpToolTableProps {\n initialStatus?: \"enabled\" | \"disabled\" | \"all\";\n className?: string;\n}\n\n/**\n * 格式化时间显示\n */\nfunction formatTime(timeStr?: string): string {\n if (!timeStr) return \"-\";\n\n try {\n const date = new Date(timeStr);\n const now = new Date();\n const diffMs = now.getTime() - date.getTime();\n const diffMins = Math.floor(diffMs / 60000);\n\n if (diffMins < 1) return \"刚刚\";\n if (diffMins < 60) return `${diffMins}分钟前`;\n if (diffMins < 1440) return `${Math.floor(diffMins / 60)}小时前`;\n return `${Math.floor(diffMins / 1440)}天前`;\n } catch {\n return timeStr;\n }\n}\n\nexport function McpToolTable({\n initialStatus = \"all\",\n className,\n}: McpToolTableProps) {\n // 使用持久化 Hook,自动保存和恢复排序配置\n const { sortConfig, setSortConfig } = useToolSortPersistence();\n\n const [tools, setTools] = useState<ToolRowData[]>([]);\n\n // 使用搜索 Hook\n const { searchValue, setSearchValue, filteredTools, clearSearch } =\n useToolSearch(tools);\n\n // 使用分页 Hook\n const { currentPage, totalPages, paginatedTools, setPage, resetPage } =\n useToolPagination(filteredTools, 10);\n\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [, setRefreshing] = useState(false);\n\n // Coze 工具确认对话框状态\n const [cozeToolToRemove, setCozeToolToRemove] = useState<string | null>(null);\n\n // 工具调试对话框状态\n const [debugDialog, setDebugDialog] = useState<{\n open: boolean;\n tool?: {\n name: string;\n serverName: string;\n toolName: string;\n description?: string;\n inputSchema?: any;\n };\n }>({ open: false });\n\n // 格式化工具信息的辅助函数\n const formatTool = useCallback(\n (tool: CustomMCPToolWithStats, enabled: boolean): ToolRowData => {\n const { serviceName, toolName } = (() => {\n if (!tool || !tool.handler) {\n return {\n serviceName: UNKNOWN_SERVICE_NAME,\n toolName: tool?.name || UNKNOWN_SERVICE_NAME,\n };\n }\n\n if (tool.handler.type === \"mcp\") {\n return {\n serviceName:\n tool.handler.config?.serviceName || UNKNOWN_SERVICE_NAME,\n toolName: tool.handler.config?.toolName || tool.name,\n };\n }\n if (tool.handler.type === \"proxy\" && tool.handler.platform === \"coze\") {\n return {\n serviceName: \"customMCP\",\n toolName: tool.name,\n };\n }\n return {\n serviceName: CUSTOM_SERVICE_NAME,\n toolName: tool.name,\n };\n })();\n\n return {\n name: tool.name,\n serverName: serviceName,\n toolName,\n description: tool.description || \"\",\n enabled,\n usageCount: tool.usageCount || 0,\n lastUsedTime: tool.lastUsedTime || \"\",\n inputSchema: tool.inputSchema,\n };\n },\n []\n );\n\n // 加载工具数据的辅助函数\n const loadToolsData = useCallback(async () => {\n const toolsList = await apiClient.getToolsList(initialStatus, sortConfig);\n const formattedTools = toolsList.map((tool) =>\n formatTool(tool, tool.enabled ?? false)\n );\n setTools(formattedTools);\n }, [initialStatus, sortConfig, formatTool]);\n\n // 获取工具列表\n const fetchTools = useCallback(async () => {\n setLoading(true);\n setError(null);\n\n try {\n await loadToolsData();\n } catch (err) {\n console.error(\"获取工具列表失败:\", err);\n setError(err instanceof Error ? err.message : \"获取工具列表失败\");\n toast.error(\"获取工具列表失败\");\n } finally {\n setLoading(false);\n }\n }, [loadToolsData]);\n\n // 刷新工具列表(用于启用/禁用后更新)\n const refreshToolLists = useCallback(async () => {\n try {\n await loadToolsData();\n } catch (err) {\n console.error(\"刷新工具列表失败:\", err);\n toast.error(\"刷新工具列表失败\");\n }\n }, [loadToolsData]);\n\n // 手动刷新\n const handleRefresh = useCallback(async () => {\n setRefreshing(true);\n try {\n await fetchTools();\n toast.success(\"刷新成功\");\n } catch {\n toast.error(\"刷新失败\");\n } finally {\n setRefreshing(false);\n }\n }, [fetchTools]);\n\n // 启用/禁用工具\n const handleToggleTool = useCallback(\n async (name: string, currentEnable: boolean) => {\n try {\n const originalTool = tools.find((tool) => tool.name === name);\n\n if (!originalTool) {\n toast.error(\"找不到对应的工具信息\");\n return;\n }\n\n // 检查是否为 Coze 工作流工具\n if (originalTool.serverName === \"customMCP\") {\n if (currentEnable) {\n // Coze 工作流工具需要确认对话框\n setCozeToolToRemove(name);\n return;\n }\n // 添加 Coze 工作流工具\n await apiClient.addCustomTool(\n {\n workflow_id: \"\",\n workflow_name: name,\n description: originalTool.description || \"\",\n icon_url: \"\",\n app_id: \"\",\n },\n name,\n originalTool.description || \"\"\n );\n toast.success(`添加工具 ${name} 成功`);\n } else {\n // 普通 MCP 工具\n const action = currentEnable ? \"disable\" : \"enable\";\n await apiClient.manageMCPTool({\n action,\n serverName: originalTool.serverName,\n toolName: originalTool.toolName,\n description: originalTool.description,\n });\n toast.success(`${currentEnable ? \"禁用\" : \"启用\"}工具 ${name} 成功`);\n }\n\n await refreshToolLists();\n } catch (err) {\n console.error(\"切换工具状态失败:\", err);\n toast.error(err instanceof Error ? err.message : \"切换工具状态失败\");\n }\n },\n [tools, refreshToolLists]\n );\n\n // 确认移除 Coze 工具\n const handleConfirmRemoveCozeTool = useCallback(async () => {\n if (!cozeToolToRemove) return;\n\n try {\n await apiClient.removeCustomTool(cozeToolToRemove);\n toast.success(`删除工具 ${cozeToolToRemove} 成功`);\n await refreshToolLists();\n } catch (err) {\n console.error(\"删除 Coze 工具失败:\", err);\n toast.error(err instanceof Error ? err.message : \"删除 Coze 工具失败\");\n } finally {\n setCozeToolToRemove(null);\n }\n }, [cozeToolToRemove, refreshToolLists]);\n\n // 取消移除 Coze 工具\n const handleCancelRemoveCozeTool = useCallback(() => {\n setCozeToolToRemove(null);\n }, []);\n\n // 打开工具调试对话框\n const handleDebugTool = useCallback((tool: ToolRowData) => {\n setDebugDialog({\n open: true,\n tool: {\n name: tool.name,\n serverName: tool.serverName,\n toolName: tool.toolName,\n description: tool.description,\n inputSchema: tool.inputSchema,\n },\n });\n }, []);\n\n // 组件挂载时获取工具列表\n useEffect(() => {\n fetchTools();\n }, [fetchTools]);\n\n // 搜索条件变化时重置分页\n // biome-ignore lint/correctness/useExhaustiveDependencies: 需要在搜索值变化时重置分页\n useEffect(() => {\n resetPage();\n }, [searchValue, resetPage]);\n\n return (\n <div className={cn(\"flex flex-col gap-4\", className)}>\n {/* 排序选择器和搜索框 */}\n <div className=\"flex items-center justify-between gap-4\">\n <ToolSortSelector value={sortConfig} onChange={setSortConfig} />\n <ToolSearchInput\n value={searchValue}\n onChange={setSearchValue}\n placeholder=\"搜索服务名、工具名...\"\n />\n </div>\n\n {/* 搜索结果提示 */}\n {searchValue && (\n <div className=\"text-sm text-muted-foreground\">\n 找到 {filteredTools.length} 个结果\n {filteredTools.length > 0 && (\n <button\n type=\"button\"\n onClick={clearSearch}\n className=\"ml-2 text-primary hover:underline\"\n >\n 清除搜索\n </button>\n )}\n </div>\n )}\n\n {/* 表格容器 */}\n <div className=\"rounded-md border\">\n {loading ? (\n <div className=\"flex items-center justify-center py-12\">\n <div className=\"flex flex-col items-center gap-2\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n <span className=\"text-sm text-muted-foreground\">\n 加载工具列表中...\n </span>\n </div>\n </div>\n ) : error ? (\n <div className=\"flex flex-col items-center justify-center py-12 gap-4\">\n <div className=\"text-red-500 text-sm\">{error}</div>\n <Button variant=\"outline\" size=\"sm\" onClick={handleRefresh}>\n 重试\n </Button>\n </div>\n ) : filteredTools.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-12 gap-4\">\n <CoffeeIcon className=\"h-12 w-12 text-muted-foreground\" />\n <span className=\"text-sm text-muted-foreground\">\n {searchValue\n ? \"没有找到匹配的工具\"\n : initialStatus === \"enabled\"\n ? \"没有已启用的工具\"\n : initialStatus === \"disabled\"\n ? \"没有已禁用的工具\"\n : \"暂无可用工具\"}\n </span>\n {searchValue && (\n <Button variant=\"outline\" size=\"sm\" onClick={clearSearch}>\n 清除搜索\n </Button>\n )}\n </div>\n ) : (\n <>\n <Table size=\"compact\">\n <TableHeader>\n <TableRow>\n <TableHead>服务名</TableHead>\n <TableHead>工具名</TableHead>\n <TableHead>描述</TableHead>\n <TableHead>使用次数</TableHead>\n <TableHead>最近使用</TableHead>\n <TableHead>状态</TableHead>\n <TableHead>操作</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {paginatedTools.map((tool) => (\n <TableRow key={tool.name}>\n <TableCell>\n <Badge variant=\"secondary\" className=\"rounded-md\">\n {tool.serverName}\n </Badge>\n </TableCell>\n <TableCell className=\"font-medium\">\n {tool.toolName}\n </TableCell>\n <TableCell className=\"text-muted-foreground max-w-[300px]\">\n <CollapsibleText\n text={tool.description}\n maxLength={100}\n textSize=\"text-xs\"\n storageKey={`mcp-tool-desc-${tool.name}`}\n />\n </TableCell>\n <TableCell className=\"text-right\">\n {tool.usageCount}\n </TableCell>\n <TableCell className=\"text-right text-muted-foreground\">\n {formatTime(tool.lastUsedTime)}\n </TableCell>\n <TableCell>\n <div className=\"flex justify-end\">\n <Switch\n checked={tool.enabled}\n onCheckedChange={(checked) =>\n handleToggleTool(tool.name, !checked)\n }\n />\n </div>\n </TableCell>\n <TableCell>\n <div className=\"flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n className=\"flex items-center gap-1 hover:cursor-pointer hover:text-primary transition-all duration-100\"\n onClick={() => handleDebugTool(tool)}\n title=\"调试工具\"\n >\n <ZapIcon size={14} />\n <span>调试</span>\n </button>\n </div>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n\n {/* 分页控件 */}\n <ToolPagination\n currentPage={currentPage}\n totalPages={totalPages}\n setPage={setPage}\n />\n </>\n )}\n </div>\n\n {/* Coze 工具移除确认对话框 */}\n <AlertDialog\n open={cozeToolToRemove !== null}\n onOpenChange={(open) => !open && setCozeToolToRemove(null)}\n >\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>确认移除 Coze 工作流工具</AlertDialogTitle>\n <AlertDialogDescription>\n 移除后需要通过【工作流集成】重新添加并配置入参,确定要移除工具 \"\n {cozeToolToRemove}\" 吗?\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel onClick={handleCancelRemoveCozeTool}>\n 取消\n </AlertDialogCancel>\n <AlertDialogAction\n onClick={handleConfirmRemoveCozeTool}\n className=\"bg-destructive text-destructive-foreground hover:bg-destructive/90\"\n >\n 确认移除\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n\n {/* 工具调试对话框 */}\n <ToolDebugDialog\n open={debugDialog.open}\n onOpenChange={(open) => setDebugDialog((prev) => ({ ...prev, open }))}\n tool={debugDialog.tool || null}\n />\n </div>\n );\n}\n","import type { SVGProps } from \"react\";\n\n/**\n * QQ 图标组件\n *\n * 用于展示 QQ 联系方式的图标\n */\n\ninterface QQIconProps extends SVGProps<SVGSVGElement> {\n /** 图标大小(像素) */\n size?: number;\n /** 图标颜色 */\n color?: string;\n}\n\nexport const QQIcon = ({\n size = 24,\n color = \"currentColor\",\n ...props\n}: QQIconProps) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 1024 1024\"\n fill=\"none\"\n stroke={color}\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M980.79827 694.105946c-21.144216-122.796973-109.844757-203.250162-109.844757-203.250162 12.647784-111.477622-33.792-131.26573-33.792-131.26573C827.392 14.668108 530.985514 20.67373 524.730811 20.839784 518.476108 20.67373 222.01427 14.668108 212.300108 359.590054c0 0-46.467459 19.788108-33.819676 131.26573 0 0-88.700541 80.453189-109.817081 203.250162 0 0-11.291676 207.484541 101.403676 25.40627 0 0 25.350919 69.161514 71.790703 131.26573 0 0-83.082378 28.256865-75.997405 101.625081 0 0-2.87827 81.836973 177.401081 76.218811 0 0 126.699243-9.852541 164.753297-63.515676l16.605405 0 0.276757 0 16.633081 0c38.026378 53.635459 164.725622 63.515676 164.725622 63.515676 180.224 5.618162 177.401081-76.218811 177.401081-76.218811 7.029622-73.368216-75.997405-101.625081-75.997405-101.625081 46.439784-62.104216 71.790703-131.26573 71.790703-131.26573C992.034595 901.590486 980.79827 694.105946 980.79827 694.105946z\" />\n </svg>\n);\n","import type { SVGProps } from \"react\";\n\n/**\n * GitHub 图标组件\n *\n * 用于展示 GitHub 链接的图标\n */\n\ninterface GithubIconProps extends SVGProps<SVGSVGElement> {\n /** 图标大小(像素) */\n size?: number;\n /** 图标颜色 */\n color?: string;\n}\n\nexport const GithubIcon = ({\n size = 24,\n color = \"currentColor\",\n ...props\n}: GithubIconProps) => (\n <svg\n width={size}\n height={size}\n viewBox=\"0 0 1024 1024\"\n fill=\"none\"\n stroke={color}\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path\n d=\"M0 524.992q0 166.016 95.488 298.496t247.488 185.504q6.016 0.992 10.016 0.992t6.496-1.504 4-3.008 2.016-4.992 0.512-4.992v-100.512q-36.992 4-66.016-0.512t-45.504-14.016-28.992-23.488-16.992-25.504-8.992-24-5.504-14.496q-8.992-15.008-27.008-27.488t-27.008-20-2.016-14.496q50.016-26.016 112.992 66.016 34.016 51.008 119.008 30.016 10.016-40.992 40-70.016Q293.984 736 237.984 670.976t-56-158.016q0-87.008 55.008-151.008-22.016-64.992 6.016-136.992 28.992-2.016 64.992 11.488t50.496 23.008 25.504 17.504q56.992-16 128.512-16t129.504 16q12.992-8.992 28.992-19.008t48.992-21.504 60.992-9.504q27.008 71.008 7.008 135.008 56 64 56 151.008 0 92.992-56.992 158.496t-172 85.504q43.008 43.008 43.008 104v128.992q0 0.992 0.992 3.008 0 6.016 0.512 8.992t4.512 6.016 12 3.008q152.992-52 250.496-185.504t97.504-300.512q0-104-40.512-199.008t-108.992-163.488-163.488-108.992T512.032 12.96 313.024 53.472 149.536 162.464t-108.992 163.488-40.512 199.008z\"\n p-id=\"4674\"\n />\n </svg>\n);\n","/**\n * useNPMInstall Hook - NPM 安装日志实时推送\n *\n * 功能:\n * - 管理 NPM 安装状态\n * - 订阅 WebSocket 安装事件\n * - 提供安装日志实时更新\n * - 支持安装操作触发\n */\n\nimport { webSocketManager } from \"@/services/websocket\";\nimport { useCallback, useEffect, useState } from \"react\";\n\n/**\n * 安装日志接口\n */\nexport interface InstallLog {\n type: \"stdout\" | \"stderr\";\n message: string;\n timestamp: number;\n}\n\n/**\n * 安装状态接口\n */\nexport interface InstallStatus {\n status: \"idle\" | \"installing\" | \"completed\" | \"failed\";\n version?: string;\n installId?: string;\n logs: InstallLog[];\n error?: string;\n duration?: number;\n}\n\n/**\n * useNPMInstall Hook\n *\n * @returns 安装状态和操作方法\n */\nexport function useNPMInstall() {\n const [installStatus, setInstallStatus] = useState<InstallStatus>({\n status: \"idle\",\n logs: [],\n });\n\n useEffect(() => {\n // 订阅安装开始事件\n const unsubscribeStarted = webSocketManager.subscribe(\n \"data:npmInstallStarted\",\n (data) => {\n console.log(\"[useNPMInstall] 安装开始:\", data);\n setInstallStatus({\n status: \"installing\",\n version: data.version,\n installId: data.installId,\n logs: [],\n });\n }\n );\n\n // 订阅安装日志事件\n const unsubscribeLog = webSocketManager.subscribe(\n \"data:npmInstallLog\",\n (data) => {\n console.log(\"[useNPMInstall] 收到日志:\", data);\n setInstallStatus((prev) => {\n // 只处理当前安装任务的日志\n if (prev.installId === data.installId) {\n return {\n ...prev,\n logs: [\n ...prev.logs,\n {\n type: data.type,\n message: data.message,\n timestamp: data.timestamp,\n },\n ],\n };\n }\n return prev;\n });\n }\n );\n\n // 订阅安装完成事件\n const unsubscribeCompleted = webSocketManager.subscribe(\n \"data:npmInstallCompleted\",\n (data) => {\n console.log(\"[useNPMInstall] 安装完成:\", data);\n setInstallStatus((prev) => {\n // 只处理当前安装任务的完成事件\n if (prev.installId === data.installId) {\n return {\n ...prev,\n status: \"completed\",\n duration: data.duration,\n };\n }\n return prev;\n });\n }\n );\n\n // 订阅安装失败事件\n const unsubscribeFailed = webSocketManager.subscribe(\n \"data:npmInstallFailed\",\n (data) => {\n console.log(\"[useNPMInstall] 安装失败:\", data);\n setInstallStatus((prev) => {\n // 只处理当前安装任务的失败事件\n if (prev.installId === data.installId) {\n return {\n ...prev,\n status: \"failed\",\n error: data.error,\n duration: data.duration,\n };\n }\n return prev;\n });\n }\n );\n\n // 清理函数:取消所有事件订阅\n return () => {\n unsubscribeStarted();\n unsubscribeLog();\n unsubscribeCompleted();\n unsubscribeFailed();\n };\n }, []);\n\n /**\n * 开始安装指定版本\n *\n * @param version 要安装的版本号\n * @returns Promise<安装结果>\n */\n const startInstall = useCallback(async (version: string) => {\n try {\n console.log(\"[useNPMInstall] 开始安装版本:\", version);\n\n const response = await fetch(\"/api/update\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ version }),\n });\n\n const result = await response.json();\n\n if (!result.success) {\n throw new Error(result.error?.message || \"安装请求失败\");\n }\n\n console.log(\"[useNPMInstall] 安装请求已接受:\", result);\n return result;\n } catch (error) {\n console.error(\"[useNPMInstall] 安装请求失败:\", error);\n\n // 更新状态为失败\n setInstallStatus((prev) => ({\n ...prev,\n status: \"failed\",\n error: error instanceof Error ? error.message : \"未知错误\",\n }));\n\n throw error;\n }\n }, []);\n\n /**\n * 清除安装状态\n */\n const clearStatus = useCallback(() => {\n console.log(\"[useNPMInstall] 清除安装状态\");\n setInstallStatus({\n status: \"idle\",\n logs: [],\n });\n }, []);\n\n /**\n * 获取状态描述文本\n */\n const getStatusText = useCallback(() => {\n switch (installStatus.status) {\n case \"installing\":\n return `正在安装 xiaozhi-client@${installStatus.version}...`;\n case \"completed\":\n return \"安装完成!\";\n case \"failed\":\n return `安装失败: ${installStatus.error}`;\n default:\n return \"\";\n }\n }, [installStatus]);\n\n /**\n * 获取状态颜色类名\n */\n const getStatusColor = useCallback(() => {\n switch (installStatus.status) {\n case \"installing\":\n return \"text-blue-600\";\n case \"completed\":\n return \"text-green-600\";\n case \"failed\":\n return \"text-red-600\";\n default:\n return \"text-gray-600\";\n }\n }, [installStatus]);\n\n /**\n * 检查是否正在安装\n */\n const isInstalling = useCallback(() => {\n return installStatus.status === \"installing\";\n }, [installStatus.status]);\n\n /**\n * 检查是否可以关闭对话框\n */\n const canCloseDialog = useCallback(() => {\n return installStatus.status !== \"installing\";\n }, [installStatus.status]);\n\n return {\n installStatus,\n startInstall,\n clearStatus,\n getStatusText,\n getStatusColor,\n isInstalling,\n canCloseDialog,\n };\n}\n","'use strict'\n\n// Note: this is the semver.org version of the spec that it implements\n// Not necessarily the package version of this code.\nconst SEMVER_SPEC_VERSION = '2.0.0'\n\nconst MAX_LENGTH = 256\nconst MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER ||\n/* istanbul ignore next */ 9007199254740991\n\n// Max safe segment length for coercion.\nconst MAX_SAFE_COMPONENT_LENGTH = 16\n\n// Max safe length for a build identifier. The max length minus 6 characters for\n// the shortest version with a build 0.0.0+BUILD.\nconst MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6\n\nconst RELEASE_TYPES = [\n 'major',\n 'premajor',\n 'minor',\n 'preminor',\n 'patch',\n 'prepatch',\n 'prerelease',\n]\n\nmodule.exports = {\n MAX_LENGTH,\n MAX_SAFE_COMPONENT_LENGTH,\n MAX_SAFE_BUILD_LENGTH,\n MAX_SAFE_INTEGER,\n RELEASE_TYPES,\n SEMVER_SPEC_VERSION,\n FLAG_INCLUDE_PRERELEASE: 0b001,\n FLAG_LOOSE: 0b010,\n}\n","'use strict'\n\nconst debug = (\n typeof process === 'object' &&\n process.env &&\n process.env.NODE_DEBUG &&\n /\\bsemver\\b/i.test(process.env.NODE_DEBUG)\n) ? (...args) => console.error('SEMVER', ...args)\n : () => {}\n\nmodule.exports = debug\n","'use strict'\n\nconst {\n MAX_SAFE_COMPONENT_LENGTH,\n MAX_SAFE_BUILD_LENGTH,\n MAX_LENGTH,\n} = require('./constants')\nconst debug = require('./debug')\nexports = module.exports = {}\n\n// The actual regexps go on exports.re\nconst re = exports.re = []\nconst safeRe = exports.safeRe = []\nconst src = exports.src = []\nconst safeSrc = exports.safeSrc = []\nconst t = exports.t = {}\nlet R = 0\n\nconst LETTERDASHNUMBER = '[a-zA-Z0-9-]'\n\n// Replace some greedy regex tokens to prevent regex dos issues. These regex are\n// used internally via the safeRe object since all inputs in this library get\n// normalized first to trim and collapse all extra whitespace. The original\n// regexes are exported for userland consumption and lower level usage. A\n// future breaking change could export the safer regex only with a note that\n// all input should have extra whitespace removed.\nconst safeRegexReplacements = [\n ['\\\\s', 1],\n ['\\\\d', MAX_LENGTH],\n [LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH],\n]\n\nconst makeSafeRegex = (value) => {\n for (const [token, max] of safeRegexReplacements) {\n value = value\n .split(`${token}*`).join(`${token}{0,${max}}`)\n .split(`${token}+`).join(`${token}{1,${max}}`)\n }\n return value\n}\n\nconst createToken = (name, value, isGlobal) => {\n const safe = makeSafeRegex(value)\n const index = R++\n debug(name, index, value)\n t[name] = index\n src[index] = value\n safeSrc[index] = safe\n re[index] = new RegExp(value, isGlobal ? 'g' : undefined)\n safeRe[index] = new RegExp(safe, isGlobal ? 'g' : undefined)\n}\n\n// The following Regular Expressions can be used for tokenizing,\n// validating, and parsing SemVer version strings.\n\n// ## Numeric Identifier\n// A single `0`, or a non-zero digit followed by zero or more digits.\n\ncreateToken('NUMERICIDENTIFIER', '0|[1-9]\\\\d*')\ncreateToken('NUMERICIDENTIFIERLOOSE', '\\\\d+')\n\n// ## Non-numeric Identifier\n// Zero or more digits, followed by a letter or hyphen, and then zero or\n// more letters, digits, or hyphens.\n\ncreateToken('NONNUMERICIDENTIFIER', `\\\\d*[a-zA-Z-]${LETTERDASHNUMBER}*`)\n\n// ## Main Version\n// Three dot-separated numeric identifiers.\n\ncreateToken('MAINVERSION', `(${src[t.NUMERICIDENTIFIER]})\\\\.` +\n `(${src[t.NUMERICIDENTIFIER]})\\\\.` +\n `(${src[t.NUMERICIDENTIFIER]})`)\n\ncreateToken('MAINVERSIONLOOSE', `(${src[t.NUMERICIDENTIFIERLOOSE]})\\\\.` +\n `(${src[t.NUMERICIDENTIFIERLOOSE]})\\\\.` +\n `(${src[t.NUMERICIDENTIFIERLOOSE]})`)\n\n// ## Pre-release Version Identifier\n// A numeric identifier, or a non-numeric identifier.\n// Non-numberic identifiers include numberic identifiers but can be longer.\n// Therefore non-numberic identifiers must go first.\n\ncreateToken('PRERELEASEIDENTIFIER', `(?:${src[t.NONNUMERICIDENTIFIER]\n}|${src[t.NUMERICIDENTIFIER]})`)\n\ncreateToken('PRERELEASEIDENTIFIERLOOSE', `(?:${src[t.NONNUMERICIDENTIFIER]\n}|${src[t.NUMERICIDENTIFIERLOOSE]})`)\n\n// ## Pre-release Version\n// Hyphen, followed by one or more dot-separated pre-release version\n// identifiers.\n\ncreateToken('PRERELEASE', `(?:-(${src[t.PRERELEASEIDENTIFIER]\n}(?:\\\\.${src[t.PRERELEASEIDENTIFIER]})*))`)\n\ncreateToken('PRERELEASELOOSE', `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE]\n}(?:\\\\.${src[t.PRERELEASEIDENTIFIERLOOSE]})*))`)\n\n// ## Build Metadata Identifier\n// Any combination of digits, letters, or hyphens.\n\ncreateToken('BUILDIDENTIFIER', `${LETTERDASHNUMBER}+`)\n\n// ## Build Metadata\n// Plus sign, followed by one or more period-separated build metadata\n// identifiers.\n\ncreateToken('BUILD', `(?:\\\\+(${src[t.BUILDIDENTIFIER]\n}(?:\\\\.${src[t.BUILDIDENTIFIER]})*))`)\n\n// ## Full Version String\n// A main version, followed optionally by a pre-release version and\n// build metadata.\n\n// Note that the only major, minor, patch, and pre-release sections of\n// the version string are capturing groups. The build metadata is not a\n// capturing group, because it should not ever be used in version\n// comparison.\n\ncreateToken('FULLPLAIN', `v?${src[t.MAINVERSION]\n}${src[t.PRERELEASE]}?${\n src[t.BUILD]}?`)\n\ncreateToken('FULL', `^${src[t.FULLPLAIN]}$`)\n\n// like full, but allows v1.2.3 and =1.2.3, which people do sometimes.\n// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty\n// common in the npm registry.\ncreateToken('LOOSEPLAIN', `[v=\\\\s]*${src[t.MAINVERSIONLOOSE]\n}${src[t.PRERELEASELOOSE]}?${\n src[t.BUILD]}?`)\n\ncreateToken('LOOSE', `^${src[t.LOOSEPLAIN]}$`)\n\ncreateToken('GTLT', '((?:<|>)?=?)')\n\n// Something like \"2.*\" or \"1.2.x\".\n// Note that \"x.x\" is a valid xRange identifer, meaning \"any version\"\n// Only the first item is strictly required.\ncreateToken('XRANGEIDENTIFIERLOOSE', `${src[t.NUMERICIDENTIFIERLOOSE]}|x|X|\\\\*`)\ncreateToken('XRANGEIDENTIFIER', `${src[t.NUMERICIDENTIFIER]}|x|X|\\\\*`)\n\ncreateToken('XRANGEPLAIN', `[v=\\\\s]*(${src[t.XRANGEIDENTIFIER]})` +\n `(?:\\\\.(${src[t.XRANGEIDENTIFIER]})` +\n `(?:\\\\.(${src[t.XRANGEIDENTIFIER]})` +\n `(?:${src[t.PRERELEASE]})?${\n src[t.BUILD]}?` +\n `)?)?`)\n\ncreateToken('XRANGEPLAINLOOSE', `[v=\\\\s]*(${src[t.XRANGEIDENTIFIERLOOSE]})` +\n `(?:\\\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` +\n `(?:\\\\.(${src[t.XRANGEIDENTIFIERLOOSE]})` +\n `(?:${src[t.PRERELEASELOOSE]})?${\n src[t.BUILD]}?` +\n `)?)?`)\n\ncreateToken('XRANGE', `^${src[t.GTLT]}\\\\s*${src[t.XRANGEPLAIN]}$`)\ncreateToken('XRANGELOOSE', `^${src[t.GTLT]}\\\\s*${src[t.XRANGEPLAINLOOSE]}$`)\n\n// Coercion.\n// Extract anything that could conceivably be a part of a valid semver\ncreateToken('COERCEPLAIN', `${'(^|[^\\\\d])' +\n '(\\\\d{1,'}${MAX_SAFE_COMPONENT_LENGTH}})` +\n `(?:\\\\.(\\\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?` +\n `(?:\\\\.(\\\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?`)\ncreateToken('COERCE', `${src[t.COERCEPLAIN]}(?:$|[^\\\\d])`)\ncreateToken('COERCEFULL', src[t.COERCEPLAIN] +\n `(?:${src[t.PRERELEASE]})?` +\n `(?:${src[t.BUILD]})?` +\n `(?:$|[^\\\\d])`)\ncreateToken('COERCERTL', src[t.COERCE], true)\ncreateToken('COERCERTLFULL', src[t.COERCEFULL], true)\n\n// Tilde ranges.\n// Meaning is \"reasonably at or greater than\"\ncreateToken('LONETILDE', '(?:~>?)')\n\ncreateToken('TILDETRIM', `(\\\\s*)${src[t.LONETILDE]}\\\\s+`, true)\nexports.tildeTrimReplace = '$1~'\n\ncreateToken('TILDE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAIN]}$`)\ncreateToken('TILDELOOSE', `^${src[t.LONETILDE]}${src[t.XRANGEPLAINLOOSE]}$`)\n\n// Caret ranges.\n// Meaning is \"at least and backwards compatible with\"\ncreateToken('LONECARET', '(?:\\\\^)')\n\ncreateToken('CARETTRIM', `(\\\\s*)${src[t.LONECARET]}\\\\s+`, true)\nexports.caretTrimReplace = '$1^'\n\ncreateToken('CARET', `^${src[t.LONECARET]}${src[t.XRANGEPLAIN]}$`)\ncreateToken('CARETLOOSE', `^${src[t.LONECARET]}${src[t.XRANGEPLAINLOOSE]}$`)\n\n// A simple gt/lt/eq thing, or just \"\" to indicate \"any version\"\ncreateToken('COMPARATORLOOSE', `^${src[t.GTLT]}\\\\s*(${src[t.LOOSEPLAIN]})$|^$`)\ncreateToken('COMPARATOR', `^${src[t.GTLT]}\\\\s*(${src[t.FULLPLAIN]})$|^$`)\n\n// An expression to strip any whitespace between the gtlt and the thing\n// it modifies, so that `> 1.2.3` ==> `>1.2.3`\ncreateToken('COMPARATORTRIM', `(\\\\s*)${src[t.GTLT]\n}\\\\s*(${src[t.LOOSEPLAIN]}|${src[t.XRANGEPLAIN]})`, true)\nexports.comparatorTrimReplace = '$1$2$3'\n\n// Something like `1.2.3 - 1.2.4`\n// Note that these all use the loose form, because they'll be\n// checked against either the strict or loose comparator form\n// later.\ncreateToken('HYPHENRANGE', `^\\\\s*(${src[t.XRANGEPLAIN]})` +\n `\\\\s+-\\\\s+` +\n `(${src[t.XRANGEPLAIN]})` +\n `\\\\s*$`)\n\ncreateToken('HYPHENRANGELOOSE', `^\\\\s*(${src[t.XRANGEPLAINLOOSE]})` +\n `\\\\s+-\\\\s+` +\n `(${src[t.XRANGEPLAINLOOSE]})` +\n `\\\\s*$`)\n\n// Star ranges basically just allow anything at all.\ncreateToken('STAR', '(<|>)?=?\\\\s*\\\\*')\n// >=0.0.0 is like a star\ncreateToken('GTE0', '^\\\\s*>=\\\\s*0\\\\.0\\\\.0\\\\s*$')\ncreateToken('GTE0PRE', '^\\\\s*>=\\\\s*0\\\\.0\\\\.0-0\\\\s*$')\n","'use strict'\n\n// parse out just the options we care about\nconst looseOption = Object.freeze({ loose: true })\nconst emptyOpts = Object.freeze({ })\nconst parseOptions = options => {\n if (!options) {\n return emptyOpts\n }\n\n if (typeof options !== 'object') {\n return looseOption\n }\n\n return options\n}\nmodule.exports = parseOptions\n","'use strict'\n\nconst numeric = /^[0-9]+$/\nconst compareIdentifiers = (a, b) => {\n if (typeof a === 'number' && typeof b === 'number') {\n return a === b ? 0 : a < b ? -1 : 1\n }\n\n const anum = numeric.test(a)\n const bnum = numeric.test(b)\n\n if (anum && bnum) {\n a = +a\n b = +b\n }\n\n return a === b ? 0\n : (anum && !bnum) ? -1\n : (bnum && !anum) ? 1\n : a < b ? -1\n : 1\n}\n\nconst rcompareIdentifiers = (a, b) => compareIdentifiers(b, a)\n\nmodule.exports = {\n compareIdentifiers,\n rcompareIdentifiers,\n}\n","'use strict'\n\nconst debug = require('../internal/debug')\nconst { MAX_LENGTH, MAX_SAFE_INTEGER } = require('../internal/constants')\nconst { safeRe: re, t } = require('../internal/re')\n\nconst parseOptions = require('../internal/parse-options')\nconst { compareIdentifiers } = require('../internal/identifiers')\nclass SemVer {\n constructor (version, options) {\n options = parseOptions(options)\n\n if (version instanceof SemVer) {\n if (version.loose === !!options.loose &&\n version.includePrerelease === !!options.includePrerelease) {\n return version\n } else {\n version = version.version\n }\n } else if (typeof version !== 'string') {\n throw new TypeError(`Invalid version. Must be a string. Got type \"${typeof version}\".`)\n }\n\n if (version.length > MAX_LENGTH) {\n throw new TypeError(\n `version is longer than ${MAX_LENGTH} characters`\n )\n }\n\n debug('SemVer', version, options)\n this.options = options\n this.loose = !!options.loose\n // this isn't actually relevant for versions, but keep it so that we\n // don't run into trouble passing this.options around.\n this.includePrerelease = !!options.includePrerelease\n\n const m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL])\n\n if (!m) {\n throw new TypeError(`Invalid Version: ${version}`)\n }\n\n this.raw = version\n\n // these are actually numbers\n this.major = +m[1]\n this.minor = +m[2]\n this.patch = +m[3]\n\n if (this.major > MAX_SAFE_INTEGER || this.major < 0) {\n throw new TypeError('Invalid major version')\n }\n\n if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) {\n throw new TypeError('Invalid minor version')\n }\n\n if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) {\n throw new TypeError('Invalid patch version')\n }\n\n // numberify any prerelease numeric ids\n if (!m[4]) {\n this.prerelease = []\n } else {\n this.prerelease = m[4].split('.').map((id) => {\n if (/^[0-9]+$/.test(id)) {\n const num = +id\n if (num >= 0 && num < MAX_SAFE_INTEGER) {\n return num\n }\n }\n return id\n })\n }\n\n this.build = m[5] ? m[5].split('.') : []\n this.format()\n }\n\n format () {\n this.version = `${this.major}.${this.minor}.${this.patch}`\n if (this.prerelease.length) {\n this.version += `-${this.prerelease.join('.')}`\n }\n return this.version\n }\n\n toString () {\n return this.version\n }\n\n compare (other) {\n debug('SemVer.compare', this.version, this.options, other)\n if (!(other instanceof SemVer)) {\n if (typeof other === 'string' && other === this.version) {\n return 0\n }\n other = new SemVer(other, this.options)\n }\n\n if (other.version === this.version) {\n return 0\n }\n\n return this.compareMain(other) || this.comparePre(other)\n }\n\n compareMain (other) {\n if (!(other instanceof SemVer)) {\n other = new SemVer(other, this.options)\n }\n\n if (this.major < other.major) {\n return -1\n }\n if (this.major > other.major) {\n return 1\n }\n if (this.minor < other.minor) {\n return -1\n }\n if (this.minor > other.minor) {\n return 1\n }\n if (this.patch < other.patch) {\n return -1\n }\n if (this.patch > other.patch) {\n return 1\n }\n return 0\n }\n\n comparePre (other) {\n if (!(other instanceof SemVer)) {\n other = new SemVer(other, this.options)\n }\n\n // NOT having a prerelease is > having one\n if (this.prerelease.length && !other.prerelease.length) {\n return -1\n } else if (!this.prerelease.length && other.prerelease.length) {\n return 1\n } else if (!this.prerelease.length && !other.prerelease.length) {\n return 0\n }\n\n let i = 0\n do {\n const a = this.prerelease[i]\n const b = other.prerelease[i]\n debug('prerelease compare', i, a, b)\n if (a === undefined && b === undefined) {\n return 0\n } else if (b === undefined) {\n return 1\n } else if (a === undefined) {\n return -1\n } else if (a === b) {\n continue\n } else {\n return compareIdentifiers(a, b)\n }\n } while (++i)\n }\n\n compareBuild (other) {\n if (!(other instanceof SemVer)) {\n other = new SemVer(other, this.options)\n }\n\n let i = 0\n do {\n const a = this.build[i]\n const b = other.build[i]\n debug('build compare', i, a, b)\n if (a === undefined && b === undefined) {\n return 0\n } else if (b === undefined) {\n return 1\n } else if (a === undefined) {\n return -1\n } else if (a === b) {\n continue\n } else {\n return compareIdentifiers(a, b)\n }\n } while (++i)\n }\n\n // preminor will bump the version up to the next minor release, and immediately\n // down to pre-release. premajor and prepatch work the same way.\n inc (release, identifier, identifierBase) {\n if (release.startsWith('pre')) {\n if (!identifier && identifierBase === false) {\n throw new Error('invalid increment argument: identifier is empty')\n }\n // Avoid an invalid semver results\n if (identifier) {\n const match = `-${identifier}`.match(this.options.loose ? re[t.PRERELEASELOOSE] : re[t.PRERELEASE])\n if (!match || match[1] !== identifier) {\n throw new Error(`invalid identifier: ${identifier}`)\n }\n }\n }\n\n switch (release) {\n case 'premajor':\n this.prerelease.length = 0\n this.patch = 0\n this.minor = 0\n this.major++\n this.inc('pre', identifier, identifierBase)\n break\n case 'preminor':\n this.prerelease.length = 0\n this.patch = 0\n this.minor++\n this.inc('pre', identifier, identifierBase)\n break\n case 'prepatch':\n // If this is already a prerelease, it will bump to the next version\n // drop any prereleases that might already exist, since they are not\n // relevant at this point.\n this.prerelease.length = 0\n this.inc('patch', identifier, identifierBase)\n this.inc('pre', identifier, identifierBase)\n break\n // If the input is a non-prerelease version, this acts the same as\n // prepatch.\n case 'prerelease':\n if (this.prerelease.length === 0) {\n this.inc('patch', identifier, identifierBase)\n }\n this.inc('pre', identifier, identifierBase)\n break\n case 'release':\n if (this.prerelease.length === 0) {\n throw new Error(`version ${this.raw} is not a prerelease`)\n }\n this.prerelease.length = 0\n break\n\n case 'major':\n // If this is a pre-major version, bump up to the same major version.\n // Otherwise increment major.\n // 1.0.0-5 bumps to 1.0.0\n // 1.1.0 bumps to 2.0.0\n if (\n this.minor !== 0 ||\n this.patch !== 0 ||\n this.prerelease.length === 0\n ) {\n this.major++\n }\n this.minor = 0\n this.patch = 0\n this.prerelease = []\n break\n case 'minor':\n // If this is a pre-minor version, bump up to the same minor version.\n // Otherwise increment minor.\n // 1.2.0-5 bumps to 1.2.0\n // 1.2.1 bumps to 1.3.0\n if (this.patch !== 0 || this.prerelease.length === 0) {\n this.minor++\n }\n this.patch = 0\n this.prerelease = []\n break\n case 'patch':\n // If this is not a pre-release version, it will increment the patch.\n // If it is a pre-release it will bump up to the same patch version.\n // 1.2.0-5 patches to 1.2.0\n // 1.2.0 patches to 1.2.1\n if (this.prerelease.length === 0) {\n this.patch++\n }\n this.prerelease = []\n break\n // This probably shouldn't be used publicly.\n // 1.0.0 'pre' would become 1.0.0-0 which is the wrong direction.\n case 'pre': {\n const base = Number(identifierBase) ? 1 : 0\n\n if (this.prerelease.length === 0) {\n this.prerelease = [base]\n } else {\n let i = this.prerelease.length\n while (--i >= 0) {\n if (typeof this.prerelease[i] === 'number') {\n this.prerelease[i]++\n i = -2\n }\n }\n if (i === -1) {\n // didn't increment anything\n if (identifier === this.prerelease.join('.') && identifierBase === false) {\n throw new Error('invalid increment argument: identifier already exists')\n }\n this.prerelease.push(base)\n }\n }\n if (identifier) {\n // 1.2.0-beta.1 bumps to 1.2.0-beta.2,\n // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0\n let prerelease = [identifier, base]\n if (identifierBase === false) {\n prerelease = [identifier]\n }\n if (compareIdentifiers(this.prerelease[0], identifier) === 0) {\n if (isNaN(this.prerelease[1])) {\n this.prerelease = prerelease\n }\n } else {\n this.prerelease = prerelease\n }\n }\n break\n }\n default:\n throw new Error(`invalid increment argument: ${release}`)\n }\n this.raw = this.format()\n if (this.build.length) {\n this.raw += `+${this.build.join('.')}`\n }\n return this\n }\n}\n\nmodule.exports = SemVer\n","'use strict'\n\nconst SemVer = require('../classes/semver')\nconst parse = (version, options, throwErrors = false) => {\n if (version instanceof SemVer) {\n return version\n }\n try {\n return new SemVer(version, options)\n } catch (er) {\n if (!throwErrors) {\n return null\n }\n throw er\n }\n}\n\nmodule.exports = parse\n","'use strict'\n\nconst parse = require('./parse')\nconst valid = (version, options) => {\n const v = parse(version, options)\n return v ? v.version : null\n}\nmodule.exports = valid\n","'use strict'\n\nconst parse = require('./parse')\nconst clean = (version, options) => {\n const s = parse(version.trim().replace(/^[=v]+/, ''), options)\n return s ? s.version : null\n}\nmodule.exports = clean\n","'use strict'\n\nconst SemVer = require('../classes/semver')\n\nconst inc = (version, release, options, identifier, identifierBase) => {\n if (typeof (options) === 'string') {\n identifierBase = identifier\n identifier = options\n options = undefined\n }\n\n try {\n return new SemVer(\n version instanceof SemVer ? version.version : version,\n options\n ).inc(release, identifier, identifierBase).version\n } catch (er) {\n return null\n }\n}\nmodule.exports = inc\n","'use strict'\n\nconst parse = require('./parse.js')\n\nconst diff = (version1, version2) => {\n const v1 = parse(version1, null, true)\n const v2 = parse(version2, null, true)\n const comparison = v1.compare(v2)\n\n if (comparison === 0) {\n return null\n }\n\n const v1Higher = comparison > 0\n const highVersion = v1Higher ? v1 : v2\n const lowVersion = v1Higher ? v2 : v1\n const highHasPre = !!highVersion.prerelease.length\n const lowHasPre = !!lowVersion.prerelease.length\n\n if (lowHasPre && !highHasPre) {\n // Going from prerelease -> no prerelease requires some special casing\n\n // If the low version has only a major, then it will always be a major\n // Some examples:\n // 1.0.0-1 -> 1.0.0\n // 1.0.0-1 -> 1.1.1\n // 1.0.0-1 -> 2.0.0\n if (!lowVersion.patch && !lowVersion.minor) {\n return 'major'\n }\n\n // If the main part has no difference\n if (lowVersion.compareMain(highVersion) === 0) {\n if (lowVersion.minor && !lowVersion.patch) {\n return 'minor'\n }\n return 'patch'\n }\n }\n\n // add the `pre` prefix if we are going to a prerelease version\n const prefix = highHasPre ? 'pre' : ''\n\n if (v1.major !== v2.major) {\n return prefix + 'major'\n }\n\n if (v1.minor !== v2.minor) {\n return prefix + 'minor'\n }\n\n if (v1.patch !== v2.patch) {\n return prefix + 'patch'\n }\n\n // high and low are preleases\n return 'prerelease'\n}\n\nmodule.exports = diff\n","'use strict'\n\nconst SemVer = require('../classes/semver')\nconst major = (a, loose) => new SemVer(a, loose).major\nmodule.exports = major\n","'use strict'\n\nconst SemVer = require('../classes/semver')\nconst minor = (a, loose) => new SemVer(a, loose).minor\nmodule.exports = minor\n","'use strict'\n\nconst SemVer = require('../classes/semver')\nconst patch = (a, loose) => new SemVer(a, loose).patch\nmodule.exports = patch\n","'use strict'\n\nconst parse = require('./parse')\nconst prerelease = (version, options) => {\n const parsed = parse(version, options)\n return (parsed && parsed.prerelease.length) ? parsed.prerelease : null\n}\nmodule.exports = prerelease\n","'use strict'\n\nconst SemVer = require('../classes/semver')\nconst compare = (a, b, loose) =>\n new SemVer(a, loose).compare(new SemVer(b, loose))\n\nmodule.exports = compare\n","'use strict'\n\nconst compare = require('./compare')\nconst rcompare = (a, b, loose) => compare(b, a, loose)\nmodule.exports = rcompare\n","'use strict'\n\nconst compare = require('./compare')\nconst compareLoose = (a, b) => compare(a, b, true)\nmodule.exports = compareLoose\n","'use strict'\n\nconst SemVer = require('../classes/semver')\nconst compareBuild = (a, b, loose) => {\n const versionA = new SemVer(a, loose)\n const versionB = new SemVer(b, loose)\n return versionA.compare(versionB) || versionA.compareBuild(versionB)\n}\nmodule.exports = compareBuild\n","'use strict'\n\nconst compareBuild = require('./compare-build')\nconst sort = (list, loose) => list.sort((a, b) => compareBuild(a, b, loose))\nmodule.exports = sort\n","'use strict'\n\nconst compareBuild = require('./compare-build')\nconst rsort = (list, loose) => list.sort((a, b) => compareBuild(b, a, loose))\nmodule.exports = rsort\n","'use strict'\n\nconst compare = require('./compare')\nconst gt = (a, b, loose) => compare(a, b, loose) > 0\nmodule.exports = gt\n","'use strict'\n\nconst compare = require('./compare')\nconst lt = (a, b, loose) => compare(a, b, loose) < 0\nmodule.exports = lt\n","'use strict'\n\nconst compare = require('./compare')\nconst eq = (a, b, loose) => compare(a, b, loose) === 0\nmodule.exports = eq\n","'use strict'\n\nconst compare = require('./compare')\nconst neq = (a, b, loose) => compare(a, b, loose) !== 0\nmodule.exports = neq\n","'use strict'\n\nconst compare = require('./compare')\nconst gte = (a, b, loose) => compare(a, b, loose) >= 0\nmodule.exports = gte\n","'use strict'\n\nconst compare = require('./compare')\nconst lte = (a, b, loose) => compare(a, b, loose) <= 0\nmodule.exports = lte\n","'use strict'\n\nconst eq = require('./eq')\nconst neq = require('./neq')\nconst gt = require('./gt')\nconst gte = require('./gte')\nconst lt = require('./lt')\nconst lte = require('./lte')\n\nconst cmp = (a, op, b, loose) => {\n switch (op) {\n case '===':\n if (typeof a === 'object') {\n a = a.version\n }\n if (typeof b === 'object') {\n b = b.version\n }\n return a === b\n\n case '!==':\n if (typeof a === 'object') {\n a = a.version\n }\n if (typeof b === 'object') {\n b = b.version\n }\n return a !== b\n\n case '':\n case '=':\n case '==':\n return eq(a, b, loose)\n\n case '!=':\n return neq(a, b, loose)\n\n case '>':\n return gt(a, b, loose)\n\n case '>=':\n return gte(a, b, loose)\n\n case '<':\n return lt(a, b, loose)\n\n case '<=':\n return lte(a, b, loose)\n\n default:\n throw new TypeError(`Invalid operator: ${op}`)\n }\n}\nmodule.exports = cmp\n","'use strict'\n\nconst SemVer = require('../classes/semver')\nconst parse = require('./parse')\nconst { safeRe: re, t } = require('../internal/re')\n\nconst coerce = (version, options) => {\n if (version instanceof SemVer) {\n return version\n }\n\n if (typeof version === 'number') {\n version = String(version)\n }\n\n if (typeof version !== 'string') {\n return null\n }\n\n options = options || {}\n\n let match = null\n if (!options.rtl) {\n match = version.match(options.includePrerelease ? re[t.COERCEFULL] : re[t.COERCE])\n } else {\n // Find the right-most coercible string that does not share\n // a terminus with a more left-ward coercible string.\n // Eg, '1.2.3.4' wants to coerce '2.3.4', not '3.4' or '4'\n // With includePrerelease option set, '1.2.3.4-rc' wants to coerce '2.3.4-rc', not '2.3.4'\n //\n // Walk through the string checking with a /g regexp\n // Manually set the index so as to pick up overlapping matches.\n // Stop when we get a match that ends at the string end, since no\n // coercible string can be more right-ward without the same terminus.\n const coerceRtlRegex = options.includePrerelease ? re[t.COERCERTLFULL] : re[t.COERCERTL]\n let next\n while ((next = coerceRtlRegex.exec(version)) &&\n (!match || match.index + match[0].length !== version.length)\n ) {\n if (!match ||\n next.index + next[0].length !== match.index + match[0].length) {\n match = next\n }\n coerceRtlRegex.lastIndex = next.index + next[1].length + next[2].length\n }\n // leave it in a clean state\n coerceRtlRegex.lastIndex = -1\n }\n\n if (match === null) {\n return null\n }\n\n const major = match[2]\n const minor = match[3] || '0'\n const patch = match[4] || '0'\n const prerelease = options.includePrerelease && match[5] ? `-${match[5]}` : ''\n const build = options.includePrerelease && match[6] ? `+${match[6]}` : ''\n\n return parse(`${major}.${minor}.${patch}${prerelease}${build}`, options)\n}\nmodule.exports = coerce\n","'use strict'\n\nclass LRUCache {\n constructor () {\n this.max = 1000\n this.map = new Map()\n }\n\n get (key) {\n const value = this.map.get(key)\n if (value === undefined) {\n return undefined\n } else {\n // Remove the key from the map and add it to the end\n this.map.delete(key)\n this.map.set(key, value)\n return value\n }\n }\n\n delete (key) {\n return this.map.delete(key)\n }\n\n set (key, value) {\n const deleted = this.delete(key)\n\n if (!deleted && value !== undefined) {\n // If cache is full, delete the least recently used item\n if (this.map.size >= this.max) {\n const firstKey = this.map.keys().next().value\n this.delete(firstKey)\n }\n\n this.map.set(key, value)\n }\n\n return this\n }\n}\n\nmodule.exports = LRUCache\n","'use strict'\n\nconst SPACE_CHARACTERS = /\\s+/g\n\n// hoisted class for cyclic dependency\nclass Range {\n constructor (range, options) {\n options = parseOptions(options)\n\n if (range instanceof Range) {\n if (\n range.loose === !!options.loose &&\n range.includePrerelease === !!options.includePrerelease\n ) {\n return range\n } else {\n return new Range(range.raw, options)\n }\n }\n\n if (range instanceof Comparator) {\n // just put it in the set and return\n this.raw = range.value\n this.set = [[range]]\n this.formatted = undefined\n return this\n }\n\n this.options = options\n this.loose = !!options.loose\n this.includePrerelease = !!options.includePrerelease\n\n // First reduce all whitespace as much as possible so we do not have to rely\n // on potentially slow regexes like \\s*. This is then stored and used for\n // future error messages as well.\n this.raw = range.trim().replace(SPACE_CHARACTERS, ' ')\n\n // First, split on ||\n this.set = this.raw\n .split('||')\n // map the range to a 2d array of comparators\n .map(r => this.parseRange(r.trim()))\n // throw out any comparator lists that are empty\n // this generally means that it was not a valid range, which is allowed\n // in loose mode, but will still throw if the WHOLE range is invalid.\n .filter(c => c.length)\n\n if (!this.set.length) {\n throw new TypeError(`Invalid SemVer Range: ${this.raw}`)\n }\n\n // if we have any that are not the null set, throw out null sets.\n if (this.set.length > 1) {\n // keep the first one, in case they're all null sets\n const first = this.set[0]\n this.set = this.set.filter(c => !isNullSet(c[0]))\n if (this.set.length === 0) {\n this.set = [first]\n } else if (this.set.length > 1) {\n // if we have any that are *, then the range is just *\n for (const c of this.set) {\n if (c.length === 1 && isAny(c[0])) {\n this.set = [c]\n break\n }\n }\n }\n }\n\n this.formatted = undefined\n }\n\n get range () {\n if (this.formatted === undefined) {\n this.formatted = ''\n for (let i = 0; i < this.set.length; i++) {\n if (i > 0) {\n this.formatted += '||'\n }\n const comps = this.set[i]\n for (let k = 0; k < comps.length; k++) {\n if (k > 0) {\n this.formatted += ' '\n }\n this.formatted += comps[k].toString().trim()\n }\n }\n }\n return this.formatted\n }\n\n format () {\n return this.range\n }\n\n toString () {\n return this.range\n }\n\n parseRange (range) {\n // memoize range parsing for performance.\n // this is a very hot path, and fully deterministic.\n const memoOpts =\n (this.options.includePrerelease && FLAG_INCLUDE_PRERELEASE) |\n (this.options.loose && FLAG_LOOSE)\n const memoKey = memoOpts + ':' + range\n const cached = cache.get(memoKey)\n if (cached) {\n return cached\n }\n\n const loose = this.options.loose\n // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`\n const hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE]\n range = range.replace(hr, hyphenReplace(this.options.includePrerelease))\n debug('hyphen replace', range)\n\n // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`\n range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace)\n debug('comparator trim', range)\n\n // `~ 1.2.3` => `~1.2.3`\n range = range.replace(re[t.TILDETRIM], tildeTrimReplace)\n debug('tilde trim', range)\n\n // `^ 1.2.3` => `^1.2.3`\n range = range.replace(re[t.CARETTRIM], caretTrimReplace)\n debug('caret trim', range)\n\n // At this point, the range is completely trimmed and\n // ready to be split into comparators.\n\n let rangeList = range\n .split(' ')\n .map(comp => parseComparator(comp, this.options))\n .join(' ')\n .split(/\\s+/)\n // >=0.0.0 is equivalent to *\n .map(comp => replaceGTE0(comp, this.options))\n\n if (loose) {\n // in loose mode, throw out any that are not valid comparators\n rangeList = rangeList.filter(comp => {\n debug('loose invalid filter', comp, this.options)\n return !!comp.match(re[t.COMPARATORLOOSE])\n })\n }\n debug('range list', rangeList)\n\n // if any comparators are the null set, then replace with JUST null set\n // if more than one comparator, remove any * comparators\n // also, don't include the same comparator more than once\n const rangeMap = new Map()\n const comparators = rangeList.map(comp => new Comparator(comp, this.options))\n for (const comp of comparators) {\n if (isNullSet(comp)) {\n return [comp]\n }\n rangeMap.set(comp.value, comp)\n }\n if (rangeMap.size > 1 && rangeMap.has('')) {\n rangeMap.delete('')\n }\n\n const result = [...rangeMap.values()]\n cache.set(memoKey, result)\n return result\n }\n\n intersects (range, options) {\n if (!(range instanceof Range)) {\n throw new TypeError('a Range is required')\n }\n\n return this.set.some((thisComparators) => {\n return (\n isSatisfiable(thisComparators, options) &&\n range.set.some((rangeComparators) => {\n return (\n isSatisfiable(rangeComparators, options) &&\n thisComparators.every((thisComparator) => {\n return rangeComparators.every((rangeComparator) => {\n return thisComparator.intersects(rangeComparator, options)\n })\n })\n )\n })\n )\n })\n }\n\n // if ANY of the sets match ALL of its comparators, then pass\n test (version) {\n if (!version) {\n return false\n }\n\n if (typeof version === 'string') {\n try {\n version = new SemVer(version, this.options)\n } catch (er) {\n return false\n }\n }\n\n for (let i = 0; i < this.set.length; i++) {\n if (testSet(this.set[i], version, this.options)) {\n return true\n }\n }\n return false\n }\n}\n\nmodule.exports = Range\n\nconst LRU = require('../internal/lrucache')\nconst cache = new LRU()\n\nconst parseOptions = require('../internal/parse-options')\nconst Comparator = require('./comparator')\nconst debug = require('../internal/debug')\nconst SemVer = require('./semver')\nconst {\n safeRe: re,\n t,\n comparatorTrimReplace,\n tildeTrimReplace,\n caretTrimReplace,\n} = require('../internal/re')\nconst { FLAG_INCLUDE_PRERELEASE, FLAG_LOOSE } = require('../internal/constants')\n\nconst isNullSet = c => c.value === '<0.0.0-0'\nconst isAny = c => c.value === ''\n\n// take a set of comparators and determine whether there\n// exists a version which can satisfy it\nconst isSatisfiable = (comparators, options) => {\n let result = true\n const remainingComparators = comparators.slice()\n let testComparator = remainingComparators.pop()\n\n while (result && remainingComparators.length) {\n result = remainingComparators.every((otherComparator) => {\n return testComparator.intersects(otherComparator, options)\n })\n\n testComparator = remainingComparators.pop()\n }\n\n return result\n}\n\n// comprised of xranges, tildes, stars, and gtlt's at this point.\n// already replaced the hyphen ranges\n// turn into a set of JUST comparators.\nconst parseComparator = (comp, options) => {\n comp = comp.replace(re[t.BUILD], '')\n debug('comp', comp, options)\n comp = replaceCarets(comp, options)\n debug('caret', comp)\n comp = replaceTildes(comp, options)\n debug('tildes', comp)\n comp = replaceXRanges(comp, options)\n debug('xrange', comp)\n comp = replaceStars(comp, options)\n debug('stars', comp)\n return comp\n}\n\nconst isX = id => !id || id.toLowerCase() === 'x' || id === '*'\n\n// ~, ~> --> * (any, kinda silly)\n// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0-0\n// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0-0\n// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0-0\n// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0-0\n// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0-0\n// ~0.0.1 --> >=0.0.1 <0.1.0-0\nconst replaceTildes = (comp, options) => {\n return comp\n .trim()\n .split(/\\s+/)\n .map((c) => replaceTilde(c, options))\n .join(' ')\n}\n\nconst replaceTilde = (comp, options) => {\n const r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE]\n return comp.replace(r, (_, M, m, p, pr) => {\n debug('tilde', comp, _, M, m, p, pr)\n let ret\n\n if (isX(M)) {\n ret = ''\n } else if (isX(m)) {\n ret = `>=${M}.0.0 <${+M + 1}.0.0-0`\n } else if (isX(p)) {\n // ~1.2 == >=1.2.0 <1.3.0-0\n ret = `>=${M}.${m}.0 <${M}.${+m + 1}.0-0`\n } else if (pr) {\n debug('replaceTilde pr', pr)\n ret = `>=${M}.${m}.${p}-${pr\n } <${M}.${+m + 1}.0-0`\n } else {\n // ~1.2.3 == >=1.2.3 <1.3.0-0\n ret = `>=${M}.${m}.${p\n } <${M}.${+m + 1}.0-0`\n }\n\n debug('tilde return', ret)\n return ret\n })\n}\n\n// ^ --> * (any, kinda silly)\n// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0-0\n// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0-0\n// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0-0\n// ^1.2.3 --> >=1.2.3 <2.0.0-0\n// ^1.2.0 --> >=1.2.0 <2.0.0-0\n// ^0.0.1 --> >=0.0.1 <0.0.2-0\n// ^0.1.0 --> >=0.1.0 <0.2.0-0\nconst replaceCarets = (comp, options) => {\n return comp\n .trim()\n .split(/\\s+/)\n .map((c) => replaceCaret(c, options))\n .join(' ')\n}\n\nconst replaceCaret = (comp, options) => {\n debug('caret', comp, options)\n const r = options.loose ? re[t.CARETLOOSE] : re[t.CARET]\n const z = options.includePrerelease ? '-0' : ''\n return comp.replace(r, (_, M, m, p, pr) => {\n debug('caret', comp, _, M, m, p, pr)\n let ret\n\n if (isX(M)) {\n ret = ''\n } else if (isX(m)) {\n ret = `>=${M}.0.0${z} <${+M + 1}.0.0-0`\n } else if (isX(p)) {\n if (M === '0') {\n ret = `>=${M}.${m}.0${z} <${M}.${+m + 1}.0-0`\n } else {\n ret = `>=${M}.${m}.0${z} <${+M + 1}.0.0-0`\n }\n } else if (pr) {\n debug('replaceCaret pr', pr)\n if (M === '0') {\n if (m === '0') {\n ret = `>=${M}.${m}.${p}-${pr\n } <${M}.${m}.${+p + 1}-0`\n } else {\n ret = `>=${M}.${m}.${p}-${pr\n } <${M}.${+m + 1}.0-0`\n }\n } else {\n ret = `>=${M}.${m}.${p}-${pr\n } <${+M + 1}.0.0-0`\n }\n } else {\n debug('no pr')\n if (M === '0') {\n if (m === '0') {\n ret = `>=${M}.${m}.${p\n }${z} <${M}.${m}.${+p + 1}-0`\n } else {\n ret = `>=${M}.${m}.${p\n }${z} <${M}.${+m + 1}.0-0`\n }\n } else {\n ret = `>=${M}.${m}.${p\n } <${+M + 1}.0.0-0`\n }\n }\n\n debug('caret return', ret)\n return ret\n })\n}\n\nconst replaceXRanges = (comp, options) => {\n debug('replaceXRanges', comp, options)\n return comp\n .split(/\\s+/)\n .map((c) => replaceXRange(c, options))\n .join(' ')\n}\n\nconst replaceXRange = (comp, options) => {\n comp = comp.trim()\n const r = options.loose ? re[t.XRANGELOOSE] : re[t.XRANGE]\n return comp.replace(r, (ret, gtlt, M, m, p, pr) => {\n debug('xRange', comp, ret, gtlt, M, m, p, pr)\n const xM = isX(M)\n const xm = xM || isX(m)\n const xp = xm || isX(p)\n const anyX = xp\n\n if (gtlt === '=' && anyX) {\n gtlt = ''\n }\n\n // if we're including prereleases in the match, then we need\n // to fix this to -0, the lowest possible prerelease value\n pr = options.includePrerelease ? '-0' : ''\n\n if (xM) {\n if (gtlt === '>' || gtlt === '<') {\n // nothing is allowed\n ret = '<0.0.0-0'\n } else {\n // nothing is forbidden\n ret = '*'\n }\n } else if (gtlt && anyX) {\n // we know patch is an x, because we have any x at all.\n // replace X with 0\n if (xm) {\n m = 0\n }\n p = 0\n\n if (gtlt === '>') {\n // >1 => >=2.0.0\n // >1.2 => >=1.3.0\n gtlt = '>='\n if (xm) {\n M = +M + 1\n m = 0\n p = 0\n } else {\n m = +m + 1\n p = 0\n }\n } else if (gtlt === '<=') {\n // <=0.7.x is actually <0.8.0, since any 0.7.x should\n // pass. Similarly, <=7.x is actually <8.0.0, etc.\n gtlt = '<'\n if (xm) {\n M = +M + 1\n } else {\n m = +m + 1\n }\n }\n\n if (gtlt === '<') {\n pr = '-0'\n }\n\n ret = `${gtlt + M}.${m}.${p}${pr}`\n } else if (xm) {\n ret = `>=${M}.0.0${pr} <${+M + 1}.0.0-0`\n } else if (xp) {\n ret = `>=${M}.${m}.0${pr\n } <${M}.${+m + 1}.0-0`\n }\n\n debug('xRange return', ret)\n\n return ret\n })\n}\n\n// Because * is AND-ed with everything else in the comparator,\n// and '' means \"any version\", just remove the *s entirely.\nconst replaceStars = (comp, options) => {\n debug('replaceStars', comp, options)\n // Looseness is ignored here. star is always as loose as it gets!\n return comp\n .trim()\n .replace(re[t.STAR], '')\n}\n\nconst replaceGTE0 = (comp, options) => {\n debug('replaceGTE0', comp, options)\n return comp\n .trim()\n .replace(re[options.includePrerelease ? t.GTE0PRE : t.GTE0], '')\n}\n\n// This function is passed to string.replace(re[t.HYPHENRANGE])\n// M, m, patch, prerelease, build\n// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5\n// 1.2.3 - 3.4 => >=1.2.0 <3.5.0-0 Any 3.4.x will do\n// 1.2 - 3.4 => >=1.2.0 <3.5.0-0\n// TODO build?\nconst hyphenReplace = incPr => ($0,\n from, fM, fm, fp, fpr, fb,\n to, tM, tm, tp, tpr) => {\n if (isX(fM)) {\n from = ''\n } else if (isX(fm)) {\n from = `>=${fM}.0.0${incPr ? '-0' : ''}`\n } else if (isX(fp)) {\n from = `>=${fM}.${fm}.0${incPr ? '-0' : ''}`\n } else if (fpr) {\n from = `>=${from}`\n } else {\n from = `>=${from}${incPr ? '-0' : ''}`\n }\n\n if (isX(tM)) {\n to = ''\n } else if (isX(tm)) {\n to = `<${+tM + 1}.0.0-0`\n } else if (isX(tp)) {\n to = `<${tM}.${+tm + 1}.0-0`\n } else if (tpr) {\n to = `<=${tM}.${tm}.${tp}-${tpr}`\n } else if (incPr) {\n to = `<${tM}.${tm}.${+tp + 1}-0`\n } else {\n to = `<=${to}`\n }\n\n return `${from} ${to}`.trim()\n}\n\nconst testSet = (set, version, options) => {\n for (let i = 0; i < set.length; i++) {\n if (!set[i].test(version)) {\n return false\n }\n }\n\n if (version.prerelease.length && !options.includePrerelease) {\n // Find the set of versions that are allowed to have prereleases\n // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0\n // That should allow `1.2.3-pr.2` to pass.\n // However, `1.2.4-alpha.notready` should NOT be allowed,\n // even though it's within the range set by the comparators.\n for (let i = 0; i < set.length; i++) {\n debug(set[i].semver)\n if (set[i].semver === Comparator.ANY) {\n continue\n }\n\n if (set[i].semver.prerelease.length > 0) {\n const allowed = set[i].semver\n if (allowed.major === version.major &&\n allowed.minor === version.minor &&\n allowed.patch === version.patch) {\n return true\n }\n }\n }\n\n // Version has a -pre, but it's not one of the ones we like.\n return false\n }\n\n return true\n}\n","'use strict'\n\nconst ANY = Symbol('SemVer ANY')\n// hoisted class for cyclic dependency\nclass Comparator {\n static get ANY () {\n return ANY\n }\n\n constructor (comp, options) {\n options = parseOptions(options)\n\n if (comp instanceof Comparator) {\n if (comp.loose === !!options.loose) {\n return comp\n } else {\n comp = comp.value\n }\n }\n\n comp = comp.trim().split(/\\s+/).join(' ')\n debug('comparator', comp, options)\n this.options = options\n this.loose = !!options.loose\n this.parse(comp)\n\n if (this.semver === ANY) {\n this.value = ''\n } else {\n this.value = this.operator + this.semver.version\n }\n\n debug('comp', this)\n }\n\n parse (comp) {\n const r = this.options.loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR]\n const m = comp.match(r)\n\n if (!m) {\n throw new TypeError(`Invalid comparator: ${comp}`)\n }\n\n this.operator = m[1] !== undefined ? m[1] : ''\n if (this.operator === '=') {\n this.operator = ''\n }\n\n // if it literally is just '>' or '' then allow anything.\n if (!m[2]) {\n this.semver = ANY\n } else {\n this.semver = new SemVer(m[2], this.options.loose)\n }\n }\n\n toString () {\n return this.value\n }\n\n test (version) {\n debug('Comparator.test', version, this.options.loose)\n\n if (this.semver === ANY || version === ANY) {\n return true\n }\n\n if (typeof version === 'string') {\n try {\n version = new SemVer(version, this.options)\n } catch (er) {\n return false\n }\n }\n\n return cmp(version, this.operator, this.semver, this.options)\n }\n\n intersects (comp, options) {\n if (!(comp instanceof Comparator)) {\n throw new TypeError('a Comparator is required')\n }\n\n if (this.operator === '') {\n if (this.value === '') {\n return true\n }\n return new Range(comp.value, options).test(this.value)\n } else if (comp.operator === '') {\n if (comp.value === '') {\n return true\n }\n return new Range(this.value, options).test(comp.semver)\n }\n\n options = parseOptions(options)\n\n // Special cases where nothing can possibly be lower\n if (options.includePrerelease &&\n (this.value === '<0.0.0-0' || comp.value === '<0.0.0-0')) {\n return false\n }\n if (!options.includePrerelease &&\n (this.value.startsWith('<0.0.0') || comp.value.startsWith('<0.0.0'))) {\n return false\n }\n\n // Same direction increasing (> or >=)\n if (this.operator.startsWith('>') && comp.operator.startsWith('>')) {\n return true\n }\n // Same direction decreasing (< or <=)\n if (this.operator.startsWith('<') && comp.operator.startsWith('<')) {\n return true\n }\n // same SemVer and both sides are inclusive (<= or >=)\n if (\n (this.semver.version === comp.semver.version) &&\n this.operator.includes('=') && comp.operator.includes('=')) {\n return true\n }\n // opposite directions less than\n if (cmp(this.semver, '<', comp.semver, options) &&\n this.operator.startsWith('>') && comp.operator.startsWith('<')) {\n return true\n }\n // opposite directions greater than\n if (cmp(this.semver, '>', comp.semver, options) &&\n this.operator.startsWith('<') && comp.operator.startsWith('>')) {\n return true\n }\n return false\n }\n}\n\nmodule.exports = Comparator\n\nconst parseOptions = require('../internal/parse-options')\nconst { safeRe: re, t } = require('../internal/re')\nconst cmp = require('../functions/cmp')\nconst debug = require('../internal/debug')\nconst SemVer = require('./semver')\nconst Range = require('./range')\n","'use strict'\n\nconst Range = require('../classes/range')\nconst satisfies = (version, range, options) => {\n try {\n range = new Range(range, options)\n } catch (er) {\n return false\n }\n return range.test(version)\n}\nmodule.exports = satisfies\n","'use strict'\n\nconst Range = require('../classes/range')\n\n// Mostly just for testing and legacy API reasons\nconst toComparators = (range, options) =>\n new Range(range, options).set\n .map(comp => comp.map(c => c.value).join(' ').trim().split(' '))\n\nmodule.exports = toComparators\n","'use strict'\n\nconst SemVer = require('../classes/semver')\nconst Range = require('../classes/range')\n\nconst maxSatisfying = (versions, range, options) => {\n let max = null\n let maxSV = null\n let rangeObj = null\n try {\n rangeObj = new Range(range, options)\n } catch (er) {\n return null\n }\n versions.forEach((v) => {\n if (rangeObj.test(v)) {\n // satisfies(v, range, options)\n if (!max || maxSV.compare(v) === -1) {\n // compare(max, v, true)\n max = v\n maxSV = new SemVer(max, options)\n }\n }\n })\n return max\n}\nmodule.exports = maxSatisfying\n","'use strict'\n\nconst SemVer = require('../classes/semver')\nconst Range = require('../classes/range')\nconst minSatisfying = (versions, range, options) => {\n let min = null\n let minSV = null\n let rangeObj = null\n try {\n rangeObj = new Range(range, options)\n } catch (er) {\n return null\n }\n versions.forEach((v) => {\n if (rangeObj.test(v)) {\n // satisfies(v, range, options)\n if (!min || minSV.compare(v) === 1) {\n // compare(min, v, true)\n min = v\n minSV = new SemVer(min, options)\n }\n }\n })\n return min\n}\nmodule.exports = minSatisfying\n","'use strict'\n\nconst SemVer = require('../classes/semver')\nconst Range = require('../classes/range')\nconst gt = require('../functions/gt')\n\nconst minVersion = (range, loose) => {\n range = new Range(range, loose)\n\n let minver = new SemVer('0.0.0')\n if (range.test(minver)) {\n return minver\n }\n\n minver = new SemVer('0.0.0-0')\n if (range.test(minver)) {\n return minver\n }\n\n minver = null\n for (let i = 0; i < range.set.length; ++i) {\n const comparators = range.set[i]\n\n let setMin = null\n comparators.forEach((comparator) => {\n // Clone to avoid manipulating the comparator's semver object.\n const compver = new SemVer(comparator.semver.version)\n switch (comparator.operator) {\n case '>':\n if (compver.prerelease.length === 0) {\n compver.patch++\n } else {\n compver.prerelease.push(0)\n }\n compver.raw = compver.format()\n /* fallthrough */\n case '':\n case '>=':\n if (!setMin || gt(compver, setMin)) {\n setMin = compver\n }\n break\n case '<':\n case '<=':\n /* Ignore maximum versions */\n break\n /* istanbul ignore next */\n default:\n throw new Error(`Unexpected operation: ${comparator.operator}`)\n }\n })\n if (setMin && (!minver || gt(minver, setMin))) {\n minver = setMin\n }\n }\n\n if (minver && range.test(minver)) {\n return minver\n }\n\n return null\n}\nmodule.exports = minVersion\n","'use strict'\n\nconst Range = require('../classes/range')\nconst validRange = (range, options) => {\n try {\n // Return '*' instead of '' so that truthiness works.\n // This will throw if it's invalid anyway\n return new Range(range, options).range || '*'\n } catch (er) {\n return null\n }\n}\nmodule.exports = validRange\n","'use strict'\n\nconst SemVer = require('../classes/semver')\nconst Comparator = require('../classes/comparator')\nconst { ANY } = Comparator\nconst Range = require('../classes/range')\nconst satisfies = require('../functions/satisfies')\nconst gt = require('../functions/gt')\nconst lt = require('../functions/lt')\nconst lte = require('../functions/lte')\nconst gte = require('../functions/gte')\n\nconst outside = (version, range, hilo, options) => {\n version = new SemVer(version, options)\n range = new Range(range, options)\n\n let gtfn, ltefn, ltfn, comp, ecomp\n switch (hilo) {\n case '>':\n gtfn = gt\n ltefn = lte\n ltfn = lt\n comp = '>'\n ecomp = '>='\n break\n case '<':\n gtfn = lt\n ltefn = gte\n ltfn = gt\n comp = '<'\n ecomp = '<='\n break\n default:\n throw new TypeError('Must provide a hilo val of \"<\" or \">\"')\n }\n\n // If it satisfies the range it is not outside\n if (satisfies(version, range, options)) {\n return false\n }\n\n // From now on, variable terms are as if we're in \"gtr\" mode.\n // but note that everything is flipped for the \"ltr\" function.\n\n for (let i = 0; i < range.set.length; ++i) {\n const comparators = range.set[i]\n\n let high = null\n let low = null\n\n comparators.forEach((comparator) => {\n if (comparator.semver === ANY) {\n comparator = new Comparator('>=0.0.0')\n }\n high = high || comparator\n low = low || comparator\n if (gtfn(comparator.semver, high.semver, options)) {\n high = comparator\n } else if (ltfn(comparator.semver, low.semver, options)) {\n low = comparator\n }\n })\n\n // If the edge version comparator has a operator then our version\n // isn't outside it\n if (high.operator === comp || high.operator === ecomp) {\n return false\n }\n\n // If the lowest version comparator has an operator and our version\n // is less than it then it isn't higher than the range\n if ((!low.operator || low.operator === comp) &&\n ltefn(version, low.semver)) {\n return false\n } else if (low.operator === ecomp && ltfn(version, low.semver)) {\n return false\n }\n }\n return true\n}\n\nmodule.exports = outside\n","'use strict'\n\n// Determine if version is greater than all the versions possible in the range.\nconst outside = require('./outside')\nconst gtr = (version, range, options) => outside(version, range, '>', options)\nmodule.exports = gtr\n","'use strict'\n\nconst outside = require('./outside')\n// Determine if version is less than all the versions possible in the range\nconst ltr = (version, range, options) => outside(version, range, '<', options)\nmodule.exports = ltr\n","'use strict'\n\nconst Range = require('../classes/range')\nconst intersects = (r1, r2, options) => {\n r1 = new Range(r1, options)\n r2 = new Range(r2, options)\n return r1.intersects(r2, options)\n}\nmodule.exports = intersects\n","'use strict'\n\n// given a set of versions and a range, create a \"simplified\" range\n// that includes the same versions that the original range does\n// If the original range is shorter than the simplified one, return that.\nconst satisfies = require('../functions/satisfies.js')\nconst compare = require('../functions/compare.js')\nmodule.exports = (versions, range, options) => {\n const set = []\n let first = null\n let prev = null\n const v = versions.sort((a, b) => compare(a, b, options))\n for (const version of v) {\n const included = satisfies(version, range, options)\n if (included) {\n prev = version\n if (!first) {\n first = version\n }\n } else {\n if (prev) {\n set.push([first, prev])\n }\n prev = null\n first = null\n }\n }\n if (first) {\n set.push([first, null])\n }\n\n const ranges = []\n for (const [min, max] of set) {\n if (min === max) {\n ranges.push(min)\n } else if (!max && min === v[0]) {\n ranges.push('*')\n } else if (!max) {\n ranges.push(`>=${min}`)\n } else if (min === v[0]) {\n ranges.push(`<=${max}`)\n } else {\n ranges.push(`${min} - ${max}`)\n }\n }\n const simplified = ranges.join(' || ')\n const original = typeof range.raw === 'string' ? range.raw : String(range)\n return simplified.length < original.length ? simplified : range\n}\n","'use strict'\n\nconst Range = require('../classes/range.js')\nconst Comparator = require('../classes/comparator.js')\nconst { ANY } = Comparator\nconst satisfies = require('../functions/satisfies.js')\nconst compare = require('../functions/compare.js')\n\n// Complex range `r1 || r2 || ...` is a subset of `R1 || R2 || ...` iff:\n// - Every simple range `r1, r2, ...` is a null set, OR\n// - Every simple range `r1, r2, ...` which is not a null set is a subset of\n// some `R1, R2, ...`\n//\n// Simple range `c1 c2 ...` is a subset of simple range `C1 C2 ...` iff:\n// - If c is only the ANY comparator\n// - If C is only the ANY comparator, return true\n// - Else if in prerelease mode, return false\n// - else replace c with `[>=0.0.0]`\n// - If C is only the ANY comparator\n// - if in prerelease mode, return true\n// - else replace C with `[>=0.0.0]`\n// - Let EQ be the set of = comparators in c\n// - If EQ is more than one, return true (null set)\n// - Let GT be the highest > or >= comparator in c\n// - Let LT be the lowest < or <= comparator in c\n// - If GT and LT, and GT.semver > LT.semver, return true (null set)\n// - If any C is a = range, and GT or LT are set, return false\n// - If EQ\n// - If GT, and EQ does not satisfy GT, return true (null set)\n// - If LT, and EQ does not satisfy LT, return true (null set)\n// - If EQ satisfies every C, return true\n// - Else return false\n// - If GT\n// - If GT.semver is lower than any > or >= comp in C, return false\n// - If GT is >=, and GT.semver does not satisfy every C, return false\n// - If GT.semver has a prerelease, and not in prerelease mode\n// - If no C has a prerelease and the GT.semver tuple, return false\n// - If LT\n// - If LT.semver is greater than any < or <= comp in C, return false\n// - If LT is <=, and LT.semver does not satisfy every C, return false\n// - If GT.semver has a prerelease, and not in prerelease mode\n// - If no C has a prerelease and the LT.semver tuple, return false\n// - Else return true\n\nconst subset = (sub, dom, options = {}) => {\n if (sub === dom) {\n return true\n }\n\n sub = new Range(sub, options)\n dom = new Range(dom, options)\n let sawNonNull = false\n\n OUTER: for (const simpleSub of sub.set) {\n for (const simpleDom of dom.set) {\n const isSub = simpleSubset(simpleSub, simpleDom, options)\n sawNonNull = sawNonNull || isSub !== null\n if (isSub) {\n continue OUTER\n }\n }\n // the null set is a subset of everything, but null simple ranges in\n // a complex range should be ignored. so if we saw a non-null range,\n // then we know this isn't a subset, but if EVERY simple range was null,\n // then it is a subset.\n if (sawNonNull) {\n return false\n }\n }\n return true\n}\n\nconst minimumVersionWithPreRelease = [new Comparator('>=0.0.0-0')]\nconst minimumVersion = [new Comparator('>=0.0.0')]\n\nconst simpleSubset = (sub, dom, options) => {\n if (sub === dom) {\n return true\n }\n\n if (sub.length === 1 && sub[0].semver === ANY) {\n if (dom.length === 1 && dom[0].semver === ANY) {\n return true\n } else if (options.includePrerelease) {\n sub = minimumVersionWithPreRelease\n } else {\n sub = minimumVersion\n }\n }\n\n if (dom.length === 1 && dom[0].semver === ANY) {\n if (options.includePrerelease) {\n return true\n } else {\n dom = minimumVersion\n }\n }\n\n const eqSet = new Set()\n let gt, lt\n for (const c of sub) {\n if (c.operator === '>' || c.operator === '>=') {\n gt = higherGT(gt, c, options)\n } else if (c.operator === '<' || c.operator === '<=') {\n lt = lowerLT(lt, c, options)\n } else {\n eqSet.add(c.semver)\n }\n }\n\n if (eqSet.size > 1) {\n return null\n }\n\n let gtltComp\n if (gt && lt) {\n gtltComp = compare(gt.semver, lt.semver, options)\n if (gtltComp > 0) {\n return null\n } else if (gtltComp === 0 && (gt.operator !== '>=' || lt.operator !== '<=')) {\n return null\n }\n }\n\n // will iterate one or zero times\n for (const eq of eqSet) {\n if (gt && !satisfies(eq, String(gt), options)) {\n return null\n }\n\n if (lt && !satisfies(eq, String(lt), options)) {\n return null\n }\n\n for (const c of dom) {\n if (!satisfies(eq, String(c), options)) {\n return false\n }\n }\n\n return true\n }\n\n let higher, lower\n let hasDomLT, hasDomGT\n // if the subset has a prerelease, we need a comparator in the superset\n // with the same tuple and a prerelease, or it's not a subset\n let needDomLTPre = lt &&\n !options.includePrerelease &&\n lt.semver.prerelease.length ? lt.semver : false\n let needDomGTPre = gt &&\n !options.includePrerelease &&\n gt.semver.prerelease.length ? gt.semver : false\n // exception: <1.2.3-0 is the same as <1.2.3\n if (needDomLTPre && needDomLTPre.prerelease.length === 1 &&\n lt.operator === '<' && needDomLTPre.prerelease[0] === 0) {\n needDomLTPre = false\n }\n\n for (const c of dom) {\n hasDomGT = hasDomGT || c.operator === '>' || c.operator === '>='\n hasDomLT = hasDomLT || c.operator === '<' || c.operator === '<='\n if (gt) {\n if (needDomGTPre) {\n if (c.semver.prerelease && c.semver.prerelease.length &&\n c.semver.major === needDomGTPre.major &&\n c.semver.minor === needDomGTPre.minor &&\n c.semver.patch === needDomGTPre.patch) {\n needDomGTPre = false\n }\n }\n if (c.operator === '>' || c.operator === '>=') {\n higher = higherGT(gt, c, options)\n if (higher === c && higher !== gt) {\n return false\n }\n } else if (gt.operator === '>=' && !satisfies(gt.semver, String(c), options)) {\n return false\n }\n }\n if (lt) {\n if (needDomLTPre) {\n if (c.semver.prerelease && c.semver.prerelease.length &&\n c.semver.major === needDomLTPre.major &&\n c.semver.minor === needDomLTPre.minor &&\n c.semver.patch === needDomLTPre.patch) {\n needDomLTPre = false\n }\n }\n if (c.operator === '<' || c.operator === '<=') {\n lower = lowerLT(lt, c, options)\n if (lower === c && lower !== lt) {\n return false\n }\n } else if (lt.operator === '<=' && !satisfies(lt.semver, String(c), options)) {\n return false\n }\n }\n if (!c.operator && (lt || gt) && gtltComp !== 0) {\n return false\n }\n }\n\n // if there was a < or >, and nothing in the dom, then must be false\n // UNLESS it was limited by another range in the other direction.\n // Eg, >1.0.0 <1.0.1 is still a subset of <2.0.0\n if (gt && hasDomLT && !lt && gtltComp !== 0) {\n return false\n }\n\n if (lt && hasDomGT && !gt && gtltComp !== 0) {\n return false\n }\n\n // we needed a prerelease range in a specific tuple, but didn't get one\n // then this isn't a subset. eg >=1.2.3-pre is not a subset of >=1.0.0,\n // because it includes prereleases in the 1.2.3 tuple\n if (needDomGTPre || needDomLTPre) {\n return false\n }\n\n return true\n}\n\n// >=1.2.3 is lower than >1.2.3\nconst higherGT = (a, b, options) => {\n if (!a) {\n return b\n }\n const comp = compare(a.semver, b.semver, options)\n return comp > 0 ? a\n : comp < 0 ? b\n : b.operator === '>' && a.operator === '>=' ? b\n : a\n}\n\n// <=1.2.3 is higher than <1.2.3\nconst lowerLT = (a, b, options) => {\n if (!a) {\n return b\n }\n const comp = compare(a.semver, b.semver, options)\n return comp < 0 ? a\n : comp > 0 ? b\n : b.operator === '<' && a.operator === '<=' ? b\n : a\n}\n\nmodule.exports = subset\n","'use strict'\n\n// just pre-load all the stuff that index.js lazily exports\nconst internalRe = require('./internal/re')\nconst constants = require('./internal/constants')\nconst SemVer = require('./classes/semver')\nconst identifiers = require('./internal/identifiers')\nconst parse = require('./functions/parse')\nconst valid = require('./functions/valid')\nconst clean = require('./functions/clean')\nconst inc = require('./functions/inc')\nconst diff = require('./functions/diff')\nconst major = require('./functions/major')\nconst minor = require('./functions/minor')\nconst patch = require('./functions/patch')\nconst prerelease = require('./functions/prerelease')\nconst compare = require('./functions/compare')\nconst rcompare = require('./functions/rcompare')\nconst compareLoose = require('./functions/compare-loose')\nconst compareBuild = require('./functions/compare-build')\nconst sort = require('./functions/sort')\nconst rsort = require('./functions/rsort')\nconst gt = require('./functions/gt')\nconst lt = require('./functions/lt')\nconst eq = require('./functions/eq')\nconst neq = require('./functions/neq')\nconst gte = require('./functions/gte')\nconst lte = require('./functions/lte')\nconst cmp = require('./functions/cmp')\nconst coerce = require('./functions/coerce')\nconst Comparator = require('./classes/comparator')\nconst Range = require('./classes/range')\nconst satisfies = require('./functions/satisfies')\nconst toComparators = require('./ranges/to-comparators')\nconst maxSatisfying = require('./ranges/max-satisfying')\nconst minSatisfying = require('./ranges/min-satisfying')\nconst minVersion = require('./ranges/min-version')\nconst validRange = require('./ranges/valid')\nconst outside = require('./ranges/outside')\nconst gtr = require('./ranges/gtr')\nconst ltr = require('./ranges/ltr')\nconst intersects = require('./ranges/intersects')\nconst simplifyRange = require('./ranges/simplify')\nconst subset = require('./ranges/subset')\nmodule.exports = {\n parse,\n valid,\n clean,\n inc,\n diff,\n major,\n minor,\n patch,\n prerelease,\n compare,\n rcompare,\n compareLoose,\n compareBuild,\n sort,\n rsort,\n gt,\n lt,\n eq,\n neq,\n gte,\n lte,\n cmp,\n coerce,\n Comparator,\n Range,\n satisfies,\n toComparators,\n maxSatisfying,\n minSatisfying,\n minVersion,\n validRange,\n outside,\n gtr,\n ltr,\n intersects,\n simplifyRange,\n subset,\n SemVer,\n re: internalRe.re,\n src: internalRe.src,\n tokens: internalRe.t,\n SEMVER_SPEC_VERSION: constants.SEMVER_SPEC_VERSION,\n RELEASE_TYPES: constants.RELEASE_TYPES,\n compareIdentifiers: identifiers.compareIdentifiers,\n rcompareIdentifiers: identifiers.rcompareIdentifiers,\n}\n","import { cn } from \"@/lib/utils\";\nimport * as React from \"react\";\n\ninterface ProgressProps extends React.HTMLAttributes<HTMLDivElement> {\n value?: number;\n status?: \"installing\" | \"completed\" | \"failed\" | \"idle\";\n}\n\nconst Progress = React.forwardRef<HTMLDivElement, ProgressProps>(\n ({ className, value, status = \"idle\", ...props }, ref) => {\n const getProgressColor = () => {\n switch (status) {\n case \"installing\":\n return \"bg-blue-500\";\n case \"completed\":\n return \"bg-green-500\";\n case \"failed\":\n return \"bg-red-500\";\n default:\n return \"bg-gray-300\";\n }\n };\n\n return (\n <div\n ref={ref}\n className={cn(\n \"relative h-4 w-full overflow-hidden rounded-full bg-gray-200 dark:bg-gray-800\",\n className\n )}\n {...props}\n >\n <div\n className={cn(\n \"h-full transition-all duration-300 ease-in-out\",\n getProgressColor()\n )}\n style={{ width: `${value || 0}%` }}\n />\n </div>\n );\n }\n);\nProgress.displayName = \"Progress\";\n\nexport { Progress };\n","/**\n * InstallLogDialog 组件 - NPM 安装日志实时显示对话框\n *\n * 功能:\n * - 实时显示 NPM 安装日志\n * - 显示安装状态和进度\n * - 支持自动滚动到最新日志\n * - 安装完成后提供操作选项\n */\n\nimport { Alert, AlertDescription } from \"@/components/ui/alert\";\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n Dialog,\n DialogContent,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from \"@/components/ui/dialog\";\nimport { Progress } from \"@/components/ui/progress\";\nimport { ScrollArea } from \"@/components/ui/scroll-area\";\nimport { useNPMInstall } from \"@/hooks/useNPMInstall\";\nimport {\n CheckCircleIcon,\n ChevronDownIcon,\n ChevronUpIcon,\n TerminalIcon,\n XCircleIcon,\n} from \"lucide-react\";\nimport type React from \"react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { toast } from \"sonner\";\n\n// 单个正则表达式匹配所有 ANSI 颜色转义序列,避免每次渲染时创建多个正则表达式\nconst ANSI_PATTERN = /\\[(0|31|32|33|34|35|36|37|90|91|92|93|94|95|96|97)m/g;\n\ninterface InstallLogDialogProps {\n isOpen: boolean;\n onClose: () => void;\n version: string;\n}\n\nexport function InstallLogDialog({\n isOpen,\n onClose,\n version,\n}: InstallLogDialogProps) {\n const {\n installStatus,\n startInstall,\n clearStatus,\n isInstalling,\n canCloseDialog,\n } = useNPMInstall();\n\n const logContainerRef = useRef<HTMLDivElement>(null);\n const [showDetails, setShowDetails] = useState(false);\n\n // 对话框打开时开始安装\n useEffect(() => {\n if (isOpen && version) {\n console.log(\"[InstallLogDialog] 对话框打开,开始安装版本:\", version);\n clearStatus();\n startInstall(version).catch((error) => {\n console.error(\"[InstallLogDialog] 启动安装失败:\", error);\n });\n }\n }, [isOpen, version, startInstall, clearStatus]);\n\n // 自动滚动到最新日志\n useEffect(() => {\n const timer = setTimeout(() => {\n if (logContainerRef.current) {\n logContainerRef.current.scrollTop =\n logContainerRef.current.scrollHeight;\n }\n }, 100);\n return () => clearTimeout(timer);\n });\n\n // 计算进度条进度\n const getProgressValue = () => {\n switch (installStatus.status) {\n case \"idle\":\n return 0;\n case \"installing\":\n // 基于日志数量估算进度,最多到90%\n return Math.min(90, installStatus.logs.length * 2);\n case \"completed\":\n return 100;\n case \"failed\":\n return 100;\n default:\n return 0;\n }\n };\n\n // 获取简洁的状态描述\n const getSimpleStatusText = () => {\n switch (installStatus.status) {\n case \"installing\":\n return \"正在安装...\";\n case \"completed\":\n return \"安装完成\";\n case \"failed\":\n return \"安装失败\";\n default:\n return \"准备安装\";\n }\n };\n\n // 获取状态图标\n const getStatusIcon = () => {\n switch (installStatus.status) {\n case \"installing\":\n return <TerminalIcon className=\"h-4 w-4 animate-pulse\" />;\n case \"completed\":\n return <CheckCircleIcon className=\"h-4 w-4 text-green-600\" />;\n case \"failed\":\n return <XCircleIcon className=\"h-4 w-4 text-red-600\" />;\n default:\n return null;\n }\n };\n\n // 获取状态徽章样式\n const getStatusBadgeVariant = () => {\n switch (installStatus.status) {\n case \"installing\":\n return \"default\";\n case \"completed\":\n return \"secondary\";\n case \"failed\":\n return \"destructive\";\n default:\n return \"outline\";\n }\n };\n\n // 格式化日志消息\n const formatLogMessage = (message: string) => {\n // 使用单个正则表达式清理所有 ANSI 转义序列\n const cleanedMessage = message.replace(ANSI_PATTERN, \"\");\n\n // 分割成行并去除空行\n return cleanedMessage\n .split(\"\\n\")\n .filter((line) => line.trim())\n .map((line, index) => (\n <div key={`${line.slice(0, 20)}-${index}`} className=\"leading-relaxed\">\n {line}\n </div>\n ));\n };\n\n // 处理关闭操作\n const handleClose = () => {\n if (canCloseDialog()) {\n clearStatus();\n onClose();\n } else {\n toast.error(\"安装过程中无法关闭对话框,请等待安装完成\");\n }\n };\n\n // 处理键盘事件\n const handleKeyDown = (event: React.KeyboardEvent) => {\n if (event.key === \"Escape\" && canCloseDialog()) {\n handleClose();\n }\n };\n\n // 切换详情显示\n const toggleDetails = () => {\n setShowDetails(!showDetails);\n };\n\n return (\n <Dialog open={isOpen} onOpenChange={handleClose}>\n <DialogContent\n className=\"max-w-2xl max-h-[80vh] flex flex-col\"\n onKeyDown={handleKeyDown}\n >\n <DialogHeader className=\"flex flex-row items-center justify-between space-y-0 pb-4\">\n <div className=\"flex items-center gap-3\">\n <DialogTitle className=\"text-lg font-semibold\">\n 正在安装\n </DialogTitle>\n {installStatus.status !== \"idle\" && (\n <Badge\n variant={getStatusBadgeVariant()}\n className=\"flex items-center gap-1\"\n >\n {getStatusIcon()}\n {getSimpleStatusText()}\n </Badge>\n )}\n </div>\n </DialogHeader>\n\n {/* 简洁的进度显示 */}\n <div className=\"space-y-4\">\n {/* 进度条 */}\n <div className=\"space-y-2\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"font-medium\">安装进度</span>\n {installStatus.version && (\n <Badge variant=\"outline\" className=\"text-xs\">\n v{installStatus.version}\n </Badge>\n )}\n </div>\n <Progress\n value={getProgressValue()}\n status={installStatus.status}\n className=\"w-full h-2\"\n />\n <div className=\"flex items-center justify-between text-xs text-muted-foreground\">\n <span>{getSimpleStatusText()}</span>\n {installStatus.duration && (\n <span>耗时: {(installStatus.duration / 1000).toFixed(1)}s</span>\n )}\n </div>\n </div>\n\n {/* 状态描述 */}\n {installStatus.status === \"failed\" && (\n <Alert variant=\"destructive\">\n <AlertDescription>\n 安装失败,请查看详细日志了解具体原因。\n </AlertDescription>\n </Alert>\n )}\n\n {/* 详细日志区域(可折叠) */}\n <div>\n <button\n type=\"button\"\n onClick={toggleDetails}\n className=\"flex w-full items-center justify-between h-auto p-0 gap-0 hover:bg-none bg-none text-sm mb-2\"\n >\n <h4 className=\"font-medium\">安装日志</h4>\n <div className=\"flex items-center gap-1\">\n {showDetails ? (\n <>\n 收起 <ChevronUpIcon className=\"h-4 w-4\" />\n </>\n ) : (\n <>\n 展开 <ChevronDownIcon className=\"h-4 w-4\" />\n </>\n )}\n </div>\n </button>\n {showDetails && (\n <ScrollArea\n ref={logContainerRef}\n className=\"h-[300px] w-full rounded-md border bg-background\"\n >\n <div className=\"p-4 font-mono text-xs\">\n {installStatus.logs.length === 0 ? (\n <div className=\"text-muted-foreground flex items-center gap-2\">\n <TerminalIcon className=\"h-4 w-4 animate-pulse\" />\n 等待日志输出...\n </div>\n ) : (\n <div className=\"space-y-1\">\n {installStatus.logs.map((log) => (\n <div\n key={`${log.timestamp}-${log.message.slice(0, 50)}`}\n className={`${\n log.type === \"stderr\"\n ? \"text-orange-600\"\n : \"text-foreground\"\n } break-words`}\n >\n {formatLogMessage(log.message)}\n </div>\n ))}\n </div>\n )}\n </div>\n </ScrollArea>\n )}\n </div>\n </div>\n\n {/* 底部操作栏 */}\n <DialogFooter className=\"flex items-center justify-between pt-4 border-t\">\n <div className=\"flex gap-2\">\n {installStatus.status === \"completed\" && (\n <Button\n variant=\"outline\"\n onClick={() => window.location.reload()}\n className=\"flex items-center gap-2\"\n >\n <CheckCircleIcon className=\"h-4 w-4\" />\n 重启应用\n </Button>\n )}\n\n <Button\n onClick={handleClose}\n disabled={!canCloseDialog()}\n variant={\n installStatus.status === \"failed\" ? \"destructive\" : \"default\"\n }\n >\n {isInstalling()\n ? \"安装中...\"\n : installStatus.status === \"completed\"\n ? \"完成\"\n : installStatus.status === \"failed\"\n ? \"关闭\"\n : \"关闭\"}\n </Button>\n </div>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n","/**\n * VersionUpgradeDialog 组件 - 版本升级选择对话框\n *\n * 功能:\n * - 提供版本选择下拉菜单\n * - 触发版本安装\n * - 集成安装日志显示\n */\n\nimport { Alert, AlertDescription, AlertTitle } from \"@/components/ui/alert\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from \"@/components/ui/dialog\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"@/components/ui/select\";\nimport { useNPMInstall } from \"@/hooks/useNPMInstall\";\nimport { apiClient } from \"@/services/api\";\nimport { DownloadIcon, ShieldAlertIcon } from \"lucide-react\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport semver from \"semver\";\nimport { InstallLogDialog } from \"./install-log-dialog\";\n\ninterface VersionUpgradeDialogProps {\n children?: React.ReactNode;\n defaultSelectedVersion?: string;\n}\n\n// 版本类型选项\nconst VERSION_TYPES = [\n { value: \"stable\", label: \"正式版\" },\n { value: \"rc\", label: \"预览版\" },\n { value: \"beta\", label: \"测试版\" },\n { value: \"all\", label: \"全部版本\" },\n] as const;\n\ntype VersionType = (typeof VERSION_TYPES)[number][\"value\"];\n\nexport function VersionUpgradeDialog({\n children,\n defaultSelectedVersion,\n}: VersionUpgradeDialogProps) {\n const [isOpen, setIsOpen] = useState(false);\n const [selectedVersion, setSelectedVersion] = useState<string>(\"\");\n const [selectedVersionType, setSelectedVersionType] =\n useState<VersionType>(\"stable\");\n const [showInstallDialog, setShowInstallDialog] = useState(false);\n const [availableVersions, setAvailableVersions] = useState<\n { value: string; label: string }[]\n >([]);\n const [isLoadingVersions, setIsLoadingVersions] = useState(false);\n\n const { startInstall } = useNPMInstall();\n\n // 获取可用版本列表\n const fetchAvailableVersions = useCallback(\n async (type: VersionType) => {\n try {\n setIsLoadingVersions(true);\n const response = await apiClient.getAvailableVersions(type);\n const versions = response.versions.map((version) => ({\n value: version,\n label: `v${version}`,\n }));\n setAvailableVersions(versions);\n console.log(\n `[VersionUpgradeDialog] 获取到 ${response.total} 个${type}版本`\n );\n if (\n defaultSelectedVersion &&\n response.versions.includes(defaultSelectedVersion || \"\")\n ) {\n setSelectedVersion(defaultSelectedVersion || \"\");\n }\n } catch (error) {\n console.error(\"[VersionUpgradeDialog] 获取版本列表失败:\", error);\n // 如果获取失败,使用默认版本列表\n const defaultVersions = [] as { value: string; label: string }[];\n setAvailableVersions(defaultVersions);\n } finally {\n setIsLoadingVersions(false);\n }\n },\n [defaultSelectedVersion]\n );\n\n // 当对话框打开时获取版本列表\n useEffect(() => {\n if (isOpen) {\n fetchAvailableVersions(selectedVersionType);\n }\n }, [isOpen, selectedVersionType, fetchAvailableVersions]);\n\n // 处理版本类型选择\n const handleVersionTypeSelect = (value: VersionType) => {\n setSelectedVersionType(value);\n setSelectedVersion(\"\"); // 清空之前选择的版本\n };\n\n // 处理版本选择\n const handleVersionSelect = (value: string) => {\n setSelectedVersion(value);\n };\n\n // 处理确认安装\n const handleConfirmInstall = async () => {\n if (!selectedVersion) {\n return;\n }\n\n try {\n console.log(\"[VersionUpgradeDialog] 开始安装版本:\", selectedVersion);\n\n // 关闭版本选择对话框\n setIsOpen(false);\n\n // 显示安装日志对话框\n setShowInstallDialog(true);\n\n // 开始安装\n await startInstall(selectedVersion);\n } catch (error) {\n console.error(\"[VersionUpgradeDialog] 安装失败:\", error);\n // 如果安装失败,关闭安装日志对话框\n setShowInstallDialog(false);\n }\n };\n\n // 处理安装对话框关闭\n const handleInstallDialogClose = () => {\n setShowInstallDialog(false);\n setSelectedVersion(\"\");\n };\n\n // 处理对话框关闭\n const handleDialogClose = (open: boolean) => {\n if (!open) {\n setSelectedVersion(\"\");\n setSelectedVersionType(\"stable\"); // 重置为默认选择\n }\n setIsOpen(open);\n };\n\n return (\n <>\n <Dialog open={isOpen} onOpenChange={handleDialogClose}>\n <DialogTrigger asChild>\n {children || (\n <Button className=\"flex items-center gap-2\">\n <DownloadIcon className=\"h-4 w-4\" />\n 升级版本\n </Button>\n )}\n </DialogTrigger>\n <DialogContent className=\"sm:max-w-md\">\n <DialogHeader>\n <DialogTitle>选择安装版本</DialogTitle>\n <DialogDescription>\n 请选择要安装的 xiaozhi-client 版本\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-4 py-4\">\n {/* 版本选择 */}\n <div className=\"space-y-2\">\n <label htmlFor=\"version-select\" className=\"text-sm font-medium\">\n 版本选择\n </label>\n <div className=\"flex items-center gap-2\">\n <Select\n value={selectedVersionType}\n onValueChange={handleVersionTypeSelect}\n >\n <SelectTrigger id=\"version-type-select\" className=\"w-[150px]\">\n <SelectValue placeholder=\"请选择版本类型\" />\n </SelectTrigger>\n <SelectContent>\n {VERSION_TYPES.map((type) => (\n <SelectItem key={type.value} value={type.value}>\n {type.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Select\n value={selectedVersion}\n onValueChange={handleVersionSelect}\n disabled={isLoadingVersions}\n >\n <SelectTrigger id=\"version-select\">\n <SelectValue\n placeholder={\n isLoadingVersions ? \"正在获取版本列表...\" : \"请选择版本\"\n }\n />\n </SelectTrigger>\n <SelectContent>\n {isLoadingVersions ? (\n <SelectItem value=\"loading\" disabled>\n 正在获取版本列表...\n </SelectItem>\n ) : availableVersions.length === 0 ? (\n <SelectItem value=\"empty\" disabled>\n 暂无可用版本\n </SelectItem>\n ) : (\n availableVersions.map((version) => (\n <SelectItem key={version.value} value={version.value}>\n {version.label}\n </SelectItem>\n ))\n )}\n </SelectContent>\n </Select>\n </div>\n {!isLoadingVersions && availableVersions.length === 0 && (\n <p className=\"text-xs text-muted-foreground\">\n 当前版本类型暂无可用版本\n </p>\n )}\n {selectedVersion && semver.lt(selectedVersion, \"1.8.0\") && (\n <Alert variant=\"destructive\">\n <ShieldAlertIcon size={18} />\n <AlertTitle>重要提醒</AlertTitle>\n <AlertDescription>\n 指定版本低于1.8.0,安装后无法再使用Web界面重装,需手动通过命令操作,请谨慎操作!\n </AlertDescription>\n </Alert>\n )}\n </div>\n </div>\n\n <div className=\"flex justify-end gap-2\">\n <Button variant=\"outline\" onClick={() => setIsOpen(false)}>\n 取消\n </Button>\n <Button\n onClick={handleConfirmInstall}\n disabled={!selectedVersion || isLoadingVersions}\n >\n 确定安装\n </Button>\n </div>\n </DialogContent>\n </Dialog>\n\n {/* 安装日志对话框 */}\n <InstallLogDialog\n isOpen={showInstallDialog}\n onClose={handleInstallDialogClose}\n version={selectedVersion}\n />\n </>\n );\n}\n","/**\n * VersionDisplay 组件 - 版本信息显示和更新检查\n *\n * 功能:\n * - 显示当前应用版本信息\n * - 检查是否有新版本可用\n * - 提供版本信息复制功能\n * - 显示更新提示和升级对话框\n * - 支持版本切换\n */\n\nimport { Badge } from \"@/components/ui/badge\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n Tooltip,\n TooltipContent,\n TooltipProvider,\n TooltipTrigger,\n} from \"@/components/ui/tooltip\";\nimport type { VersionInfo } from \"@/services/api\";\nimport { apiClient } from \"@/services/api\";\nimport { CopyIcon, InfoIcon, RocketIcon } from \"lucide-react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { VersionUpgradeDialog } from \"./version-upgrade-dialog\";\n\ninterface VersionDisplayProps {\n className?: string;\n}\n\ninterface LatestVersionInfo {\n currentVersion: string;\n latestVersion: string | null;\n hasUpdate: boolean;\n error?: string;\n}\n\nexport function VersionDisplay({ className }: VersionDisplayProps) {\n const [versionInfo, setVersionInfo] = useState<VersionInfo | null>(null);\n const [latestVersionInfo, setLatestVersionInfo] =\n useState<LatestVersionInfo | null>(null);\n const [loading, setLoading] = useState(true);\n const [checkingUpdate, setCheckingUpdate] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [copied, setCopied] = useState(false);\n const copiedTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n const fetchVersion = async () => {\n try {\n setLoading(true);\n setError(null);\n const info = await apiClient.getVersion();\n setVersionInfo(info);\n } catch (err) {\n setError(err instanceof Error ? err.message : \"获取版本信息失败\");\n console.error(\"获取版本信息失败:\", err);\n } finally {\n setLoading(false);\n }\n };\n\n fetchVersion();\n }, []);\n\n useEffect(() => {\n return () => {\n if (copiedTimerRef.current) {\n clearTimeout(copiedTimerRef.current);\n }\n };\n }, []);\n\n useEffect(() => {\n const checkForUpdates = async () => {\n try {\n setCheckingUpdate(true);\n const updateInfo = await apiClient.getLatestVersion();\n setLatestVersionInfo(updateInfo);\n } catch (err) {\n console.error(\"检查更新失败:\", err);\n // 设置默认值,不显示错误给用户\n setLatestVersionInfo({\n currentVersion: versionInfo?.version || \"unknown\",\n latestVersion: null,\n hasUpdate: false,\n error: err instanceof Error ? err.message : \"检查更新失败\",\n });\n } finally {\n setCheckingUpdate(false);\n }\n };\n\n // 只有在获取到版本信息后才检查更新\n if (versionInfo) {\n checkForUpdates();\n }\n }, [versionInfo]);\n\n const handleCopyVersion = async () => {\n if (versionInfo?.version) {\n try {\n await navigator.clipboard.writeText(versionInfo.version);\n setCopied(true);\n if (copiedTimerRef.current) {\n clearTimeout(copiedTimerRef.current);\n }\n copiedTimerRef.current = setTimeout(() => setCopied(false), 2000);\n } catch (err) {\n console.error(\"复制版本号失败:\", err);\n }\n }\n };\n\n if (loading) {\n return (\n <Badge variant=\"outline\" className={className}>\n <span className=\"text-xs\">加载中...</span>\n </Badge>\n );\n }\n\n if (error || !versionInfo) {\n return (\n <Badge variant=\"outline\" className={className}>\n <span className=\"text-xs text-muted-foreground\">版本未知</span>\n </Badge>\n );\n }\n\n const tooltipContent = (\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <InfoIcon className=\"h-3 w-3\" />\n <span className=\"font-semibold\">版本详情</span>\n </div>\n <div className=\"text-xs space-y-0.5\">\n <div>\n <strong>名称:</strong> {versionInfo.name}\n </div>\n <div>\n <strong>版本:</strong> {versionInfo.version}\n </div>\n {latestVersionInfo && (\n <>\n <div>\n <strong>最新版本:</strong>{\" \"}\n {latestVersionInfo.latestVersion || \"未知\"}\n </div>\n <div>\n <strong>状态:</strong>\n {checkingUpdate\n ? \"检查中...\"\n : latestVersionInfo.hasUpdate\n ? \"有新版本\"\n : \"已是最新\"}\n </div>\n </>\n )}\n <div>\n <strong>描述:</strong> {versionInfo.description}\n </div>\n <div>\n <strong>作者:</strong> {versionInfo.author}\n </div>\n </div>\n <div className=\"pt-1 border-t\">\n <button\n type=\"button\"\n onClick={handleCopyVersion}\n className=\"text-xs text-primary hover:underline flex items-center gap-1\"\n >\n <CopyIcon className=\"h-3 w-3\" />\n {copied ? \"已复制!\" : \"复制版本号\"}\n </button>\n </div>\n </div>\n );\n\n // 决定显示的按钮文案和图标\n const getUpgradeButton = () => {\n if (checkingUpdate) return null;\n\n if (latestVersionInfo?.hasUpdate && latestVersionInfo.latestVersion) {\n return (\n <VersionUpgradeDialog\n defaultSelectedVersion={latestVersionInfo.latestVersion}\n >\n <Button variant=\"link\" className=\"p-0 gap-1\">\n <RocketIcon />\n 升级版本\n </Button>\n </VersionUpgradeDialog>\n );\n }\n\n return (\n <VersionUpgradeDialog defaultSelectedVersion={versionInfo.version}>\n <Button variant=\"link\" className=\"p-0 gap-1\">\n 切换版本\n </Button>\n </VersionUpgradeDialog>\n );\n };\n\n return (\n <TooltipProvider>\n <div className=\"flex items-center gap-2\">\n {getUpgradeButton()}\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"text-sm cursor-help\">v{versionInfo.version}</span>\n </TooltipTrigger>\n <TooltipContent>{tooltipContent}</TooltipContent>\n </Tooltip>\n </div>\n </TooltipProvider>\n );\n}\n","import { GithubIcon, QQIcon } from \"@/components/icons\";\nimport { VersionDisplay } from \"./version-display\";\n\nexport function SiteHeader({ title }: { title: string }) {\n return (\n <header className=\"flex h-12 shrink-0 items-center gap-2 border-b px-4\">\n <h1 className=\"text-base font-medium\">{title}</h1>\n <div className=\"flex flex-1 justify-end items-center gap-4\">\n <VersionDisplay />\n <a\n href=\"https://qun.qq.com/universal-share/share?ac=1&authKey=c08PvS2zvAF1NN%2F%2BuaOi0ze1AElTIsvFBLwbWUMFc2ixjaZYxqZTUQHzipwd8Kka&busi_data=eyJncm91cENvZGUiOiIxMDU0ODg4NDczIiwidG9rZW4iOiJuSmJUN2cyUEVkNEQ5WXovM3RQbFVNcDluMGVibUNZTUQvL1RuQnFJRjBkZmRZQnRBRTdwU0szL3V2Y0dLc1ZmIiwidWluIjoiMzkxMTcyMDYwMCJ9&data=9cH6_zEC-sN3xYlwzKEWiYF71RLY9CId5taN-gy6XZo7axSlSWDpd1Ojui5hYMQKIgEJYSPw59XYgF5vH2wLog&svctype=4&tempid=h5_group_info\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n <QQIcon size={24} className=\"text-slate-800\" fill=\"currentColor\" />\n </a>\n <a\n href=\"https://github.com/shenjingnan/xiaozhi-client\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n <GithubIcon\n size={24}\n className=\"text-slate-800\"\n fill=\"currentColor\"\n />\n </a>\n </div>\n </header>\n );\n}\n","import { DashboardStatusCard } from \"@/components/dashboard-status-card\";\nimport { McpToolTable } from \"@/components/mcp-tool/mcp-tool-table\";\nimport { SiteHeader } from \"@/components/site-header\";\n\nexport default function DashboardPage() {\n return (\n <div className=\"flex h-screen flex-col\">\n <SiteHeader title=\"看板\" />\n <div className=\"flex flex-1 flex-col\">\n <div className=\"@container/main flex flex-1 flex-col gap-2\">\n <div className=\"flex flex-col gap-4 py-4 md:gap-6 md:py-6\">\n <DashboardStatusCard />\n <div className=\"flex flex-col gap-4 px-4 lg:px-6\">\n <McpToolTable initialStatus=\"all\" />\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n}\n","import { Toaster } from \"@/components/ui/sonner\";\nimport { RestartNotificationProvider } from \"@/hooks/useRestartNotifications\";\nimport DashboardPage from \"@/pages/DashboardPage\";\nimport { WebSocketProvider } from \"@/providers/WebSocketProvider\";\nimport { Navigate, Route, Routes } from \"react-router-dom\";\n\nfunction App() {\n return (\n <WebSocketProvider>\n {/* 重启通知管理器 - 全局监听重启状态变化 */}\n <RestartNotificationProvider />\n\n <Routes>\n <Route path=\"/\" element={<Navigate to=\"/dashboard\" />} />\n <Route path=\"/dashboard\" element={<DashboardPage />} />\n <Route path=\"*\" element={<Navigate to=\"/dashboard\" replace />} />\n </Routes>\n\n {/* Toast 通知容器 */}\n <Toaster\n richColors\n closeButton={true} // 启用关闭按钮\n swipeDirections={[]} // 禁用所有方向的滑动手势,只允许点击关闭按钮关闭\n toastOptions={{\n classNames: {\n description: \"group-[.toast]:text-muted-foreground\",\n actionButton:\n \"group-[.toast]:bg-primary group-[.toast]:text-primary-foreground\",\n cancelButton: \"group-[.toast]:bg-white group-[.toast]:text-black\",\n error:\n \"group toast group-[.toaster]:bg-red group-[.toaster]:text-red-600 dark:group-[.toaster]:text-foreground group-[.toaster]:shadow-lg\",\n success:\n \"group toast group-[.toaster]:bg-green group-[.toaster]:text-green-600 dark:group-[.toaster]:text-foreground group-[.toaster]:shadow-lg\",\n warning:\n \"group toast group-[.toaster]:bg-yellow group-[.toaster]:text-yellow-600 dark:group-[.toaster]:text-foreground group-[.toaster]:shadow-lg\",\n info: \"group toast group-[.toaster]:bg-blue group-[.toaster]:text-blue-600 dark:group-[.toaster]:text-foreground group-[.toaster]:shadow-lg\",\n },\n }}\n />\n </WebSocketProvider>\n );\n}\n\nexport default App;\n","import React from \"react\";\nimport ReactDOM from \"react-dom/client\";\nimport { BrowserRouter } from \"react-router-dom\";\nimport App from \"./App\";\nimport \"./index.css\";\n\nReactDOM.createRoot(document.getElementById(\"root\")!).render(\n <React.StrictMode>\n <BrowserRouter>\n <App />\n </BrowserRouter>\n </React.StrictMode>\n);\n"],"x_google_ignoreList":[76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120],"mappings":"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"}
|