@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.
- package/.env.desktop +7 -0
- package/.github/scripts/pr-release-body.js +3 -0
- package/CHANGELOG.md +50 -0
- package/changelog/v1.json +18 -0
- package/package.json +15 -7
- package/packages/electron-client-ipc/src/events/file.ts +5 -0
- package/packages/electron-client-ipc/src/events/index.ts +19 -1
- package/packages/electron-client-ipc/src/events/shortcut.ts +4 -0
- package/packages/electron-client-ipc/src/events/system.ts +5 -0
- package/packages/electron-client-ipc/src/events/update.ts +20 -0
- package/packages/electron-client-ipc/src/events/windows.ts +9 -0
- package/packages/electron-client-ipc/src/index.ts +1 -0
- package/packages/electron-client-ipc/src/types/file.ts +14 -0
- package/packages/electron-client-ipc/src/types/index.ts +4 -0
- package/packages/electron-client-ipc/src/types/route.ts +46 -0
- package/packages/electron-client-ipc/src/types/shortcut.ts +11 -0
- package/packages/electron-client-ipc/src/types/update.ts +23 -0
- package/packages/electron-client-ipc/src/useWatchBroadcast.ts +37 -0
- package/packages/electron-server-ipc/src/events/database.ts +4 -0
- package/packages/electron-server-ipc/src/events/file.ts +6 -0
- package/packages/electron-server-ipc/src/events/index.ts +29 -0
- package/packages/electron-server-ipc/src/events/storagePath.ts +4 -0
- package/packages/electron-server-ipc/src/index.ts +1 -0
- package/packages/electron-server-ipc/src/ipcClient.test.ts +3 -4
- package/packages/electron-server-ipc/src/ipcClient.ts +52 -24
- package/packages/electron-server-ipc/src/ipcServer.ts +6 -14
- package/packages/electron-server-ipc/src/types/file.ts +4 -0
- package/packages/electron-server-ipc/src/types/index.ts +14 -1
- package/scripts/electronWorkflow/buildElectron.ts +52 -0
- package/scripts/electronWorkflow/moveNextStandalone.ts +69 -0
- package/scripts/electronWorkflow/setDesktopVersion.ts +96 -0
- package/scripts/prebuild.mts +77 -0
- package/src/config/aiModels/xai.ts +79 -18
- package/src/libs/agent-runtime/azureai/index.ts +3 -1
- package/src/prompts/files/file.ts +6 -4
- package/src/prompts/files/image.ts +6 -3
- package/src/prompts/files/index.test.ts +50 -0
- package/src/prompts/files/index.ts +4 -2
- package/src/services/chat.ts +4 -2
- package/src/store/global/actions/general.ts +13 -6
- 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
|
+
[](#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
|
+
[](#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.
|
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
|
-
"
|
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.
|
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.
|
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.
|
265
|
-
"@next/eslint-plugin-next": "15.
|
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",
|
@@ -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,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
|
*/
|
@@ -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,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,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
|
+
};
|
@@ -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(
|
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(
|
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(
|
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 {
|
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
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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(
|
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:
|
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}
|
149
|
-
},
|
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) => {
|