@lobehub/chat 1.79.2 → 1.79.4

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.
Files changed (41) hide show
  1. package/.env.desktop +7 -0
  2. package/.github/scripts/pr-release-body.js +3 -0
  3. package/CHANGELOG.md +50 -0
  4. package/changelog/v1.json +18 -0
  5. package/package.json +15 -7
  6. package/packages/electron-client-ipc/src/events/file.ts +5 -0
  7. package/packages/electron-client-ipc/src/events/index.ts +19 -1
  8. package/packages/electron-client-ipc/src/events/shortcut.ts +4 -0
  9. package/packages/electron-client-ipc/src/events/system.ts +5 -0
  10. package/packages/electron-client-ipc/src/events/update.ts +20 -0
  11. package/packages/electron-client-ipc/src/events/windows.ts +9 -0
  12. package/packages/electron-client-ipc/src/index.ts +1 -0
  13. package/packages/electron-client-ipc/src/types/file.ts +14 -0
  14. package/packages/electron-client-ipc/src/types/index.ts +4 -0
  15. package/packages/electron-client-ipc/src/types/route.ts +46 -0
  16. package/packages/electron-client-ipc/src/types/shortcut.ts +11 -0
  17. package/packages/electron-client-ipc/src/types/update.ts +23 -0
  18. package/packages/electron-client-ipc/src/useWatchBroadcast.ts +37 -0
  19. package/packages/electron-server-ipc/src/events/database.ts +4 -0
  20. package/packages/electron-server-ipc/src/events/file.ts +6 -0
  21. package/packages/electron-server-ipc/src/events/index.ts +29 -0
  22. package/packages/electron-server-ipc/src/events/storagePath.ts +4 -0
  23. package/packages/electron-server-ipc/src/index.ts +1 -0
  24. package/packages/electron-server-ipc/src/ipcClient.test.ts +3 -4
  25. package/packages/electron-server-ipc/src/ipcClient.ts +52 -24
  26. package/packages/electron-server-ipc/src/ipcServer.ts +6 -14
  27. package/packages/electron-server-ipc/src/types/file.ts +4 -0
  28. package/packages/electron-server-ipc/src/types/index.ts +14 -1
  29. package/scripts/electronWorkflow/buildElectron.ts +52 -0
  30. package/scripts/electronWorkflow/moveNextStandalone.ts +69 -0
  31. package/scripts/electronWorkflow/setDesktopVersion.ts +96 -0
  32. package/scripts/prebuild.mts +77 -0
  33. package/src/config/aiModels/xai.ts +79 -18
  34. package/src/libs/agent-runtime/azureai/index.ts +3 -1
  35. package/src/prompts/files/file.ts +6 -4
  36. package/src/prompts/files/image.ts +6 -3
  37. package/src/prompts/files/index.test.ts +50 -0
  38. package/src/prompts/files/index.ts +4 -2
  39. package/src/services/chat.ts +4 -2
  40. package/src/store/global/actions/general.ts +13 -6
  41. package/packages/electron-server-ipc/src/types/event.ts +0 -18
package/.env.desktop ADDED
@@ -0,0 +1,7 @@
1
+ # copy this file to .env when you want to develop the desktop app or you will fail
2
+ APP_URL=http://localhost:3015
3
+ FEATURE_FLAGS=+pin_list
4
+ KEY_VAULTS_SECRET=oLXWIiR/AKF+rWaqy9lHkrYgzpATbW3CtJp3UfkVgpE=
5
+ DATABASE_URL=postgresql://postgres@localhost:5432/postgres
6
+ DEFAULT_AGENT_CONFIG="model=qwen2.5;provider=ollama;chatConfig.searchFCModel.provider=ollama;chatConfig.searchFCModel.model=qwen2.5"
7
+ SYSTEM_AGENT="default=ollama/qwen2.5"
@@ -9,8 +9,10 @@ module.exports = ({ version, prNumber, branch }) => {
9
9
  ## PR Build Information
10
10
 
11
11
  **Version**: \`${version}\`
12
+ **Release Time**: \`${new Date().toISOString()}\`
12
13
  **PR**: [#${prNumber}](${prLink})
13
14
 
15
+
14
16
  ## ⚠️ Important Notice
15
17
 
16
18
  This is a **development build** specifically created for testing purposes. Please note:
@@ -35,6 +37,7 @@ Please report any issues found in this build directly in the PR discussion.
35
37
  ## PR 构建信息
36
38
 
37
39
  **版本**: \`${version}\`
40
+ **发布时间**: \`${new Date().toISOString()}\`
38
41
  **PR**: [#${prNumber}](${prLink})
39
42
 
40
43
  ## ⚠️ 重要提示
package/CHANGELOG.md CHANGED
@@ -2,6 +2,56 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 1.79.4](https://github.com/lobehub/lobe-chat/compare/v1.79.3...v1.79.4)
6
+
7
+ <sup>Released on **2025-04-10**</sup>
8
+
9
+ #### 💄 Styles
10
+
11
+ - **misc**: Update Grok 3 models.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### Styles
19
+
20
+ - **misc**: Update Grok 3 models, closes [#7360](https://github.com/lobehub/lobe-chat/issues/7360) ([d2b9120](https://github.com/lobehub/lobe-chat/commit/d2b9120))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
30
+ ### [Version 1.79.3](https://github.com/lobehub/lobe-chat/compare/v1.79.2...v1.79.3)
31
+
32
+ <sup>Released on **2025-04-10**</sup>
33
+
34
+ #### 🐛 Bug Fixes
35
+
36
+ - **misc**: Remove Azure AI o3-mini unsupported parameters.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### What's fixed
44
+
45
+ - **misc**: Remove Azure AI o3-mini unsupported parameters, closes [#7355](https://github.com/lobehub/lobe-chat/issues/7355) ([fe0711f](https://github.com/lobehub/lobe-chat/commit/fe0711f))
46
+
47
+ </details>
48
+
49
+ <div align="right">
50
+
51
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
52
+
53
+ </div>
54
+
5
55
  ### [Version 1.79.2](https://github.com/lobehub/lobe-chat/compare/v1.79.1...v1.79.2)
6
56
 
7
57
  <sup>Released on **2025-04-09**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,22 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "improvements": [
5
+ "Update Grok 3 models."
6
+ ]
7
+ },
8
+ "date": "2025-04-10",
9
+ "version": "1.79.4"
10
+ },
11
+ {
12
+ "children": {
13
+ "fixes": [
14
+ "Remove Azure AI o3-mini unsupported parameters."
15
+ ]
16
+ },
17
+ "date": "2025-04-10",
18
+ "version": "1.79.3"
19
+ },
2
20
  {
3
21
  "children": {
4
22
  "fixes": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.79.2",
3
+ "version": "1.79.4",
4
4
  "description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
5
5
  "keywords": [
6
6
  "framework",
@@ -35,7 +35,8 @@
35
35
  "build-sitemap": "tsx ./scripts/buildSitemapIndex/index.ts",
36
36
  "build:analyze": "ANALYZE=true next build",
37
37
  "build:docker": "DOCKER=true next build && npm run build-sitemap",
38
- "build:electron": "NODE_OPTIONS=--max-old-space-size=6144 NEXT_PUBLIC_IS_DESKTOP_APP=1 next build ",
38
+ "prebuild:electron": "cross-env NEXT_PUBLIC_IS_DESKTOP_APP=1 tsx scripts/prebuild.mts",
39
+ "build:electron": "cross-env NODE_OPTIONS=--max-old-space-size=6144 NEXT_PUBLIC_IS_DESKTOP_APP=1 NEXT_PUBLIC_SERVICE_MODE=server next build",
39
40
  "db:generate": "drizzle-kit generate && npm run db:generate-client && npm run workflow:dbml",
40
41
  "db:generate-client": "tsx ./scripts/migrateClientDB/compile-migrations.ts",
41
42
  "db:migrate": "MIGRATION_DB=1 tsx ./scripts/migrateServerDB/index.ts",
@@ -44,7 +45,12 @@
44
45
  "db:studio": "drizzle-kit studio",
45
46
  "db:visualize": "dbdocs build docs/development/database-schema.dbml --project lobe-chat",
46
47
  "db:z-pull": "drizzle-kit introspect",
48
+ "desktop:build": "npm run desktop:build-next && npm run desktop:prepare-dist && npm run desktop:build-electron",
49
+ "desktop:build-electron": "tsx scripts/electronWorkflow/buildElectron.ts",
50
+ "desktop:build-next": "npm run build:electron",
51
+ "desktop:prepare-dist": "tsx scripts/electronWorkflow/moveNextStandalone.ts",
47
52
  "dev": "next dev --turbopack -p 3010",
53
+ "dev:desktop": "next dev --turbopack -p 3015",
48
54
  "docs:i18n": "lobe-i18n md && npm run lint:md && npm run lint:mdx",
49
55
  "docs:seo": "lobe-seo && npm run lint:mdx",
50
56
  "i18n": "npm run workflow:i18n && lobe-i18n",
@@ -78,7 +84,8 @@
78
84
  "workflow:docs": "tsx ./scripts/docsWorkflow/index.ts",
79
85
  "workflow:i18n": "tsx ./scripts/i18nWorkflow/index.ts",
80
86
  "workflow:mdx": "tsx ./scripts/mdxWorkflow/index.ts",
81
- "workflow:readme": "tsx ./scripts/readmeWorkflow/index.ts"
87
+ "workflow:readme": "tsx ./scripts/readmeWorkflow/index.ts",
88
+ "workflow:set-desktop-version": "tsx ./scripts/electronWorkflow/setDesktopVersion.ts"
82
89
  },
83
90
  "lint-staged": {
84
91
  "*.md": [
@@ -140,7 +147,7 @@
140
147
  "@lobehub/tts": "^1.28.3",
141
148
  "@lobehub/ui": "^1.170.8",
142
149
  "@neondatabase/serverless": "^1.0.0",
143
- "@next/third-parties": "15.2.3",
150
+ "@next/third-parties": "^15.3.0",
144
151
  "@react-spring/web": "^9.7.5",
145
152
  "@sentry/nextjs": "^7.120.3",
146
153
  "@serwist/next": "^9.0.12",
@@ -190,7 +197,7 @@
190
197
  "mdast-util-to-markdown": "^2.1.2",
191
198
  "modern-screenshot": "^4.6.0",
192
199
  "nanoid": "^5.1.5",
193
- "next": "15.2.3",
200
+ "next": "^15.3.0",
194
201
  "next-auth": "beta",
195
202
  "next-mdx-remote": "^5.0.0",
196
203
  "nextjs-toploader": "^3.8.16",
@@ -261,8 +268,8 @@
261
268
  "@lobehub/i18n-cli": "^1.20.3",
262
269
  "@lobehub/lint": "^1.26.1",
263
270
  "@lobehub/seo-cli": "^1.4.3",
264
- "@next/bundle-analyzer": "15.2.3",
265
- "@next/eslint-plugin-next": "15.2.3",
271
+ "@next/bundle-analyzer": "^15.3.0",
272
+ "@next/eslint-plugin-next": "^15.3.0",
266
273
  "@peculiar/webcrypto": "^1.5.0",
267
274
  "@semantic-release/exec": "^6.0.3",
268
275
  "@testing-library/jest-dom": "^6.6.3",
@@ -293,6 +300,7 @@
293
300
  "ajv-keywords": "^5.1.0",
294
301
  "commitlint": "^19.8.0",
295
302
  "consola": "^3.4.2",
303
+ "cross-env": "^7.0.3",
296
304
  "crypto-js": "^4.2.0",
297
305
  "dbdocs": "^0.14.3",
298
306
  "dotenv": "^16.4.7",
@@ -0,0 +1,5 @@
1
+ import { FileMetadata, UploadFileParams } from '../types';
2
+
3
+ export interface FilesDispatchEvents {
4
+ createFile: (params: UploadFileParams) => { metadata: FileMetadata; success: boolean };
5
+ }
@@ -1,6 +1,9 @@
1
+ import { FilesDispatchEvents } from './file';
1
2
  import { MenuDispatchEvents } from './menu';
2
3
  import { FilesSearchDispatchEvents } from './search';
4
+ import { ShortcutDispatchEvents } from './shortcut';
3
5
  import { SystemDispatchEvents } from './system';
6
+ import { AutoUpdateBroadcastEvents, AutoUpdateDispatchEvents } from './update';
4
7
  import { WindowsDispatchEvents } from './windows';
5
8
 
6
9
  /**
@@ -11,10 +14,25 @@ export interface ClientDispatchEvents
11
14
  extends WindowsDispatchEvents,
12
15
  FilesSearchDispatchEvents,
13
16
  SystemDispatchEvents,
14
- MenuDispatchEvents {}
17
+ MenuDispatchEvents,
18
+ FilesDispatchEvents,
19
+ AutoUpdateDispatchEvents,
20
+ ShortcutDispatchEvents {}
15
21
 
16
22
  export type ClientDispatchEventKey = keyof ClientDispatchEvents;
17
23
 
18
24
  export type ClientEventReturnType<T extends ClientDispatchEventKey> = ReturnType<
19
25
  ClientDispatchEvents[T]
20
26
  >;
27
+
28
+ /**
29
+ * main -> render broadcast events
30
+ */
31
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
32
+ export interface MainBroadcastEvents extends AutoUpdateBroadcastEvents {}
33
+
34
+ export type MainBroadcastEventKey = keyof MainBroadcastEvents;
35
+
36
+ export type MainBroadcastParams<T extends MainBroadcastEventKey> = Parameters<
37
+ MainBroadcastEvents[T]
38
+ >[0];
@@ -0,0 +1,4 @@
1
+ export interface ShortcutDispatchEvents {
2
+ getShortcutsConfig: () => Record<string, string>;
3
+ updateShortcutConfig: (id: string, accelerator: string) => boolean;
4
+ }
@@ -1,3 +1,8 @@
1
1
  export interface SystemDispatchEvents {
2
2
  checkSystemAccessibility: () => boolean | undefined;
3
+ /**
4
+ * 更新应用语言设置
5
+ * @param locale 语言设置
6
+ */
7
+ updateLocale: (locale: string) => { success: boolean };
3
8
  }
@@ -0,0 +1,20 @@
1
+ import { ProgressInfo, UpdateInfo } from '../types';
2
+
3
+ export interface AutoUpdateDispatchEvents {
4
+ checkUpdate: () => void;
5
+ downloadUpdate: () => void;
6
+ installLater: () => void;
7
+ installNow: () => void;
8
+ installUpdate: () => void;
9
+ }
10
+
11
+ export interface AutoUpdateBroadcastEvents {
12
+ updateAvailable: (info: UpdateInfo) => void;
13
+ updateCheckStart: () => void;
14
+ updateDownloadProgress: (progress: ProgressInfo) => void;
15
+ updateDownloadStart: () => void;
16
+ updateDownloaded: (info: UpdateInfo) => void;
17
+ updateError: (message: string) => void;
18
+ updateNotAvailable: (info: UpdateInfo) => void;
19
+ updateWillInstallLater: () => void;
20
+ }
@@ -1,4 +1,13 @@
1
+ import { InterceptRouteParams, InterceptRouteResponse } from '../types/route';
2
+
1
3
  export interface WindowsDispatchEvents {
4
+ /**
5
+ * 拦截客户端路由导航请求
6
+ * @param params 包含路径和来源信息的参数对象
7
+ * @returns 路由拦截结果
8
+ */
9
+ interceptRoute: (params: InterceptRouteParams) => InterceptRouteResponse;
10
+
2
11
  /**
3
12
  * open the LobeHub Devtools
4
13
  */
@@ -1,3 +1,4 @@
1
1
  export * from './dispatch';
2
2
  export * from './events';
3
3
  export * from './types';
4
+ export * from './useWatchBroadcast';
@@ -0,0 +1,14 @@
1
+ export interface UploadFileParams {
2
+ content: ArrayBuffer;
3
+ filename: string;
4
+ hash: string;
5
+ path: string;
6
+ type: string;
7
+ }
8
+
9
+ export interface FileMetadata {
10
+ date: string;
11
+ dirname: string;
12
+ filename: string;
13
+ path: string;
14
+ }
@@ -1 +1,5 @@
1
1
  export * from './dispatch';
2
+ export * from './file';
3
+ export * from './route';
4
+ export * from './shortcut';
5
+ export * from './update';
@@ -0,0 +1,46 @@
1
+ export interface InterceptRouteParams {
2
+ /**
3
+ * 请求路径
4
+ */
5
+ path: string;
6
+ /**
7
+ * 来源类型:'link-click', 'push-state', 'replace-state'
8
+ */
9
+ source: 'link-click' | 'push-state' | 'replace-state';
10
+ /**
11
+ * 完整URL
12
+ */
13
+ url: string;
14
+ }
15
+
16
+ export interface InterceptRouteResponse {
17
+ /**
18
+ * 错误信息 (如果有)
19
+ */
20
+ error?: string;
21
+
22
+ /**
23
+ * 是否已拦截
24
+ */
25
+ intercepted: boolean;
26
+
27
+ /**
28
+ * 原始路径
29
+ */
30
+ path: string;
31
+
32
+ /**
33
+ * 原始来源
34
+ */
35
+ source: string;
36
+
37
+ /**
38
+ * 子路径 (如果有)
39
+ */
40
+ subPath?: string;
41
+
42
+ /**
43
+ * 目标窗口标识符
44
+ */
45
+ targetWindow?: string;
46
+ }
@@ -0,0 +1,11 @@
1
+ export interface ShortcutConfig {
2
+ /**
3
+ * 快捷键加速器(如 CommandOrControl+E)
4
+ */
5
+ accelerator: string;
6
+ /**
7
+ * 快捷键 ID
8
+ */
9
+ id: string;
10
+ }
11
+ export type ShortcutActionType = Record<string, any>;
@@ -0,0 +1,23 @@
1
+ export interface ReleaseNoteInfo {
2
+ /**
3
+ * The note.
4
+ */
5
+ note: string | null;
6
+ /**
7
+ * The version.
8
+ */
9
+ version: string;
10
+ }
11
+
12
+ export interface ProgressInfo {
13
+ bytesPerSecond: number;
14
+ percent: number;
15
+ total: number;
16
+ transferred: number;
17
+ }
18
+
19
+ export interface UpdateInfo {
20
+ releaseDate: string;
21
+ releaseNotes?: string | ReleaseNoteInfo[];
22
+ version: string;
23
+ }
@@ -0,0 +1,37 @@
1
+ 'use client';
2
+
3
+ import { useEffect } from 'react';
4
+
5
+ import { MainBroadcastEventKey, MainBroadcastParams } from './events';
6
+
7
+ interface ElectronAPI {
8
+ ipcRenderer: {
9
+ on: (event: MainBroadcastEventKey, listener: (e: any, data: any) => void) => void;
10
+ removeListener: (event: MainBroadcastEventKey, listener: (e: any, data: any) => void) => void;
11
+ };
12
+ }
13
+
14
+ declare global {
15
+ interface Window {
16
+ electron: ElectronAPI;
17
+ }
18
+ }
19
+
20
+ export const useWatchBroadcast = <T extends MainBroadcastEventKey>(
21
+ event: T,
22
+ handler: (data: MainBroadcastParams<T>) => void,
23
+ ) => {
24
+ useEffect(() => {
25
+ if (!window.electron) return;
26
+
27
+ const listener = (e: any, data: MainBroadcastParams<T>) => {
28
+ handler(data);
29
+ };
30
+
31
+ window.electron.ipcRenderer.on(event, listener);
32
+
33
+ return () => {
34
+ window.electron.ipcRenderer.removeListener(event, listener);
35
+ };
36
+ }, []);
37
+ };
@@ -0,0 +1,4 @@
1
+ export interface DatabaseDispatchEvents {
2
+ getDatabaseSchemaHash: () => string | undefined;
3
+ setDatabaseSchemaHash: (hash: string) => void;
4
+ }
@@ -0,0 +1,6 @@
1
+ import { DeleteFilesResponse } from '../types/file';
2
+
3
+ export interface FileDispatchEvents {
4
+ deleteFiles: (paths: string[]) => DeleteFilesResponse;
5
+ getStaticFilePath: (id: string) => string;
6
+ }
@@ -0,0 +1,29 @@
1
+ /* eslint-disable typescript-sort-keys/interface, sort-keys-fix/sort-keys-fix */
2
+ import { DatabaseDispatchEvents } from './database';
3
+ import { FileDispatchEvents } from './file';
4
+ import { StoragePathDispatchEvents } from './storagePath';
5
+
6
+ /**
7
+ * next server -> main dispatch events
8
+ */
9
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
10
+ export interface ServerDispatchEvents
11
+ extends StoragePathDispatchEvents,
12
+ DatabaseDispatchEvents,
13
+ FileDispatchEvents {}
14
+
15
+ export type ServerDispatchEventKey = keyof ServerDispatchEvents;
16
+
17
+ export type ServerEventReturnType<T extends ServerDispatchEventKey> = ReturnType<
18
+ ServerDispatchEvents[T]
19
+ >;
20
+
21
+ export type ServerEventParams<T extends ServerDispatchEventKey> = Parameters<
22
+ ServerDispatchEvents[T]
23
+ >[0];
24
+
25
+ export type IPCServerEventHandler = {
26
+ [key in ServerDispatchEventKey]: (
27
+ params: ServerEventParams<key>,
28
+ ) => Promise<ServerEventReturnType<key>>;
29
+ };
@@ -0,0 +1,4 @@
1
+ export interface StoragePathDispatchEvents {
2
+ getDatabasePath: () => string;
3
+ getUserDataPath: () => string;
4
+ }
@@ -1,3 +1,4 @@
1
+ export * from './events';
1
2
  export * from './ipcClient';
2
3
  export * from './ipcServer';
3
4
  export * from './types';
@@ -5,7 +5,6 @@ import path from 'node:path';
5
5
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
6
6
 
7
7
  import { ElectronIpcClient } from './ipcClient';
8
- import { ElectronIPCMethods } from './types';
9
8
 
10
9
  // Mock node modules
11
10
  vi.mock('node:fs');
@@ -124,7 +123,7 @@ describe('ElectronIpcClient', () => {
124
123
 
125
124
  it('should handle connection errors', async () => {
126
125
  // Start request - but don't await it yet
127
- const requestPromise = client.sendRequest(ElectronIPCMethods.getDatabasePath);
126
+ const requestPromise = client.sendRequest('getDatabasePath');
128
127
 
129
128
  // Find the error event handler
130
129
  const errorCallArgs = mockSocket.on.mock.calls.find((call) => call[0] === 'error');
@@ -154,7 +153,7 @@ describe('ElectronIpcClient', () => {
154
153
  });
155
154
 
156
155
  // Start request
157
- const requestPromise = client.sendRequest(ElectronIPCMethods.getDatabasePath);
156
+ const requestPromise = client.sendRequest('getDatabasePath');
158
157
 
159
158
  // Simulate connection established
160
159
  if (connectionCallback) connectionCallback();
@@ -188,7 +187,7 @@ describe('ElectronIpcClient', () => {
188
187
  });
189
188
 
190
189
  // Start a request to establish connection (but don't wait for it)
191
- const requestPromise = client.sendRequest(ElectronIPCMethods.getDatabasePath).catch(() => {}); // Ignore any errors
190
+ const requestPromise = client.sendRequest('getDatabasePath').catch(() => {}); // Ignore any errors
192
191
 
193
192
  // Simulate connection
194
193
  if (connectionCallback) connectionCallback();
@@ -4,7 +4,7 @@ import os from 'node:os';
4
4
  import path from 'node:path';
5
5
 
6
6
  import { SOCK_FILE, SOCK_INFO_FILE, WINDOW_PIPE_FILE } from './const';
7
- import { IElectronIPCMethods } from './types';
7
+ import { ServerDispatchEventKey } from './events';
8
8
 
9
9
  export class ElectronIpcClient {
10
10
  private socketPath: string | null = null;
@@ -16,6 +16,7 @@ export class ElectronIpcClient {
16
16
  private reconnectTimeout: NodeJS.Timeout | null = null;
17
17
  private connectionAttempts: number = 0;
18
18
  private maxConnectionAttempts: number = 5;
19
+ private dataBuffer: string = '';
19
20
 
20
21
  constructor() {
21
22
  this.initialize();
@@ -53,43 +54,64 @@ export class ElectronIpcClient {
53
54
  this.socket = net.createConnection(this.socketPath!, () => {
54
55
  this.connected = true;
55
56
  this.connectionAttempts = 0;
56
- console.log('Connected to Electron IPC server');
57
+ console.log('[ElectronIpcClient] Connected to Electron IPC server');
57
58
  resolve();
58
59
  });
59
60
 
60
61
  this.socket.on('data', (data) => {
61
- try {
62
- const response = JSON.parse(data.toString());
63
- const { id, result, error } = response;
64
-
65
- const pending = this.requestQueue.get(id);
66
- if (pending) {
67
- this.requestQueue.delete(id);
68
- if (error) {
69
- pending.reject(new Error(error));
70
- } else {
71
- pending.resolve(result);
62
+ const dataStr = data.toString();
63
+ console.log('output:', dataStr);
64
+
65
+ // 将新数据添加到缓冲区
66
+ this.dataBuffer += dataStr;
67
+
68
+ // 按换行符分割消息
69
+ const messages = this.dataBuffer.split('\n');
70
+
71
+ // 最后一个元素可能是不完整的消息,保留在缓冲区
72
+ this.dataBuffer = messages.pop() || '';
73
+
74
+ for (const message of messages) {
75
+ if (!message.trim()) continue; // 跳过空消息
76
+
77
+ try {
78
+ const response = JSON.parse(message);
79
+ const { id, result, error } = response;
80
+
81
+ const pending = this.requestQueue.get(id);
82
+ if (pending) {
83
+ this.requestQueue.delete(id);
84
+ if (error) {
85
+ pending.reject(new Error(error));
86
+ } else {
87
+ pending.resolve(result);
88
+ }
72
89
  }
90
+ } catch (err) {
91
+ console.error(
92
+ '[ElectronIpcClient] Failed to parse response:',
93
+ err,
94
+ 'message:',
95
+ message,
96
+ );
73
97
  }
74
- } catch (err) {
75
- console.error('Failed to parse response:', err);
76
98
  }
77
99
  });
78
100
 
79
101
  this.socket.on('error', (err) => {
80
- console.error('Socket error:', err);
102
+ console.error('[ElectronIpcClient] Socket error:', err);
81
103
  this.connected = false;
82
104
  this.handleDisconnect();
83
105
  reject(err);
84
106
  });
85
107
 
86
108
  this.socket.on('close', () => {
87
- console.log('Socket closed');
109
+ console.log('[ElectronIpcClient] Socket closed');
88
110
  this.connected = false;
89
111
  this.handleDisconnect();
90
112
  });
91
113
  } catch (err) {
92
- console.error('Failed to connect to IPC server:', err);
114
+ console.error('[ElectronIpcClient] Failed to connect to IPC server:', err);
93
115
  this.handleDisconnect();
94
116
  reject(err);
95
117
  }
@@ -104,9 +126,12 @@ export class ElectronIpcClient {
104
126
  this.reconnectTimeout = null;
105
127
  }
106
128
 
129
+ // 清空数据缓冲区
130
+ this.dataBuffer = '';
131
+
107
132
  // 拒绝所有待处理的请求
108
133
  for (const [, { reject }] of this.requestQueue) {
109
- reject(new Error('Connection to Electron IPC server lost'));
134
+ reject(new Error('[ElectronIpcClient] Connection to Electron IPC server lost'));
110
135
  }
111
136
  this.requestQueue.clear();
112
137
 
@@ -117,16 +142,19 @@ export class ElectronIpcClient {
117
142
 
118
143
  this.reconnectTimeout = setTimeout(() => {
119
144
  this.connect().catch((err) => {
120
- console.error(`Reconnection attempt ${this.connectionAttempts} failed:`, err);
145
+ console.error(
146
+ `[ElectronIpcClient] Reconnection attempt ${this.connectionAttempts} failed:`,
147
+ err,
148
+ );
121
149
  });
122
150
  }, delay);
123
151
  }
124
152
  }
125
153
 
126
154
  // 发送请求到 Electron IPC 服务器
127
- public async sendRequest<T>(method: IElectronIPCMethods, params: any = {}): Promise<T> {
155
+ public async sendRequest<T>(method: ServerDispatchEventKey, params: any = {}): Promise<T> {
128
156
  if (!this.socketPath) {
129
- throw new Error('Electron IPC connection not available');
157
+ throw new Error('[ElectronIpcClient] Electron IPC connection not available');
130
158
  }
131
159
 
132
160
  // 如果未连接,先连接
@@ -145,8 +173,8 @@ export class ElectronIpcClient {
145
173
  // 设置超时
146
174
  const timeout = setTimeout(() => {
147
175
  this.requestQueue.delete(id);
148
- reject(new Error(`Request ${method} timed out`));
149
- }, 10_000);
176
+ reject(new Error(`[ElectronIpcClient] Request timed out, method: ${method}`));
177
+ }, 5000);
150
178
 
151
179
  // 发送请求
152
180
  this.socket!.write(JSON.stringify(request), (err) => {