@lobehub/chat 1.80.4 → 1.81.0

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 (59) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/package.json +1 -1
  4. package/packages/electron-client-ipc/src/events/index.ts +6 -2
  5. package/packages/electron-client-ipc/src/events/remoteServer.ts +28 -0
  6. package/packages/electron-client-ipc/src/types/index.ts +1 -0
  7. package/packages/electron-client-ipc/src/types/remoteServer.ts +8 -0
  8. package/packages/electron-server-ipc/package.json +7 -1
  9. package/packages/electron-server-ipc/src/ipcClient.ts +54 -20
  10. package/packages/electron-server-ipc/src/ipcServer.ts +42 -9
  11. package/packages/web-crawler/src/crawImpl/__tests__/search1api.test.ts +33 -39
  12. package/packages/web-crawler/src/crawImpl/search1api.ts +1 -7
  13. package/packages/web-crawler/src/index.ts +1 -0
  14. package/packages/web-crawler/src/urlRules.ts +3 -1
  15. package/src/config/aiModels/ai21.ts +10 -6
  16. package/src/config/aiModels/ai360.ts +36 -2
  17. package/src/config/aiModels/stepfun.ts +1 -0
  18. package/src/config/aiModels/taichu.ts +61 -0
  19. package/src/config/aiModels/volcengine.ts +0 -1
  20. package/src/config/modelProviders/ai21.ts +1 -1
  21. package/src/config/tools.ts +2 -0
  22. package/src/database/repositories/aiInfra/index.test.ts +3 -3
  23. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/Debug.tsx +9 -3
  24. package/src/features/Conversation/Messages/Assistant/Tool/Inspector/PluginState.tsx +21 -0
  25. package/src/features/Conversation/Messages/Assistant/Tool/Render/Arguments.tsx +1 -1
  26. package/src/locales/default/plugin.ts +1 -0
  27. package/src/server/routers/tools/{__test__/search.test.ts → search.test.ts} +27 -5
  28. package/src/server/routers/tools/search.ts +3 -44
  29. package/src/server/services/search/impls/index.ts +30 -0
  30. package/src/server/services/search/impls/search1api/index.ts +154 -0
  31. package/src/server/services/search/impls/search1api/type.ts +81 -0
  32. package/src/server/{modules/SearXNG.ts → services/search/impls/searxng/client.ts} +32 -2
  33. package/src/server/{routers/tools/__tests__ → services/search/impls/searxng}/fixtures/searXNG.ts +2 -2
  34. package/src/server/services/search/impls/searxng/index.test.ts +26 -0
  35. package/src/server/services/search/impls/searxng/index.ts +62 -0
  36. package/src/server/services/search/impls/type.ts +11 -0
  37. package/src/server/services/search/index.ts +59 -0
  38. package/src/store/chat/slices/builtinTool/actions/index.ts +1 -1
  39. package/src/store/chat/slices/builtinTool/actions/{searXNG.test.ts → search.test.ts} +30 -55
  40. package/src/store/chat/slices/builtinTool/actions/{searXNG.ts → search.ts} +25 -32
  41. package/src/tools/web-browsing/Portal/Search/Footer.tsx +1 -1
  42. package/src/tools/web-browsing/Portal/Search/ResultList/SearchItem/TitleExtra.tsx +2 -2
  43. package/src/tools/web-browsing/Portal/Search/ResultList/SearchItem/Video.tsx +9 -7
  44. package/src/tools/web-browsing/Portal/Search/ResultList/SearchItem/index.tsx +2 -2
  45. package/src/tools/web-browsing/Portal/Search/ResultList/index.tsx +3 -3
  46. package/src/tools/web-browsing/Portal/Search/index.tsx +4 -4
  47. package/src/tools/web-browsing/Portal/index.tsx +3 -1
  48. package/src/tools/web-browsing/Render/Search/SearchQuery/SearchView.tsx +4 -2
  49. package/src/tools/web-browsing/Render/Search/SearchQuery/index.tsx +6 -13
  50. package/src/tools/web-browsing/Render/Search/SearchResult/SearchResultItem.tsx +2 -2
  51. package/src/tools/web-browsing/Render/Search/SearchResult/index.tsx +5 -5
  52. package/src/tools/web-browsing/Render/Search/index.tsx +2 -2
  53. package/src/tools/web-browsing/Render/index.tsx +4 -3
  54. package/src/tools/web-browsing/components/SearchBar.tsx +4 -6
  55. package/src/tools/web-browsing/index.ts +54 -60
  56. package/src/tools/web-browsing/systemRole.ts +22 -13
  57. package/src/types/tool/search/index.ts +44 -0
  58. package/src/server/routers/tools/__tests__/search.test.ts +0 -48
  59. package/src/types/tool/search.ts +0 -48
package/CHANGELOG.md CHANGED
@@ -2,6 +2,56 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ## [Version 1.81.0](https://github.com/lobehub/lobe-chat/compare/v1.80.5...v1.81.0)
6
+
7
+ <sup>Released on **2025-04-17**</sup>
8
+
9
+ #### ✨ Features
10
+
11
+ - **misc**: Support search1api as search provider.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's improved
19
+
20
+ - **misc**: Support search1api as search provider, closes [#7449](https://github.com/lobehub/lobe-chat/issues/7449) ([2738cac](https://github.com/lobehub/lobe-chat/commit/2738cac))
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.80.5](https://github.com/lobehub/lobe-chat/compare/v1.80.4...v1.80.5)
31
+
32
+ <sup>Released on **2025-04-17**</sup>
33
+
34
+ #### 💄 Styles
35
+
36
+ - **misc**: Update 360 AI & Taichu & AI21 model list.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### Styles
44
+
45
+ - **misc**: Update 360 AI & Taichu & AI21 model list, closes [#7443](https://github.com/lobehub/lobe-chat/issues/7443) ([60c9ae2](https://github.com/lobehub/lobe-chat/commit/60c9ae2))
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.80.4](https://github.com/lobehub/lobe-chat/compare/v1.80.3...v1.80.4)
6
56
 
7
57
  <sup>Released on **2025-04-17**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,22 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "features": [
5
+ "Support search1api as search provider."
6
+ ]
7
+ },
8
+ "date": "2025-04-17",
9
+ "version": "1.81.0"
10
+ },
11
+ {
12
+ "children": {
13
+ "improvements": [
14
+ "Update 360 AI & Taichu & AI21 model list."
15
+ ]
16
+ },
17
+ "date": "2025-04-17",
18
+ "version": "1.80.5"
19
+ },
2
20
  {
3
21
  "children": {
4
22
  "improvements": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.80.4",
3
+ "version": "1.81.0",
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",
@@ -1,5 +1,6 @@
1
1
  import { FilesDispatchEvents } from './file';
2
2
  import { MenuDispatchEvents } from './menu';
3
+ import { RemoteServerBroadcastEvents, RemoteServerDispatchEvents } from './remoteServer';
3
4
  import { FilesSearchDispatchEvents } from './search';
4
5
  import { ShortcutDispatchEvents } from './shortcut';
5
6
  import { SystemDispatchEvents } from './system';
@@ -17,7 +18,8 @@ export interface ClientDispatchEvents
17
18
  MenuDispatchEvents,
18
19
  FilesDispatchEvents,
19
20
  AutoUpdateDispatchEvents,
20
- ShortcutDispatchEvents {}
21
+ ShortcutDispatchEvents,
22
+ RemoteServerDispatchEvents {}
21
23
 
22
24
  export type ClientDispatchEventKey = keyof ClientDispatchEvents;
23
25
 
@@ -29,7 +31,9 @@ export type ClientEventReturnType<T extends ClientDispatchEventKey> = ReturnType
29
31
  * main -> render broadcast events
30
32
  */
31
33
  // eslint-disable-next-line @typescript-eslint/no-empty-interface
32
- export interface MainBroadcastEvents extends AutoUpdateBroadcastEvents {}
34
+ export interface MainBroadcastEvents
35
+ extends AutoUpdateBroadcastEvents,
36
+ RemoteServerBroadcastEvents {}
33
37
 
34
38
  export type MainBroadcastEventKey = keyof MainBroadcastEvents;
35
39
 
@@ -0,0 +1,28 @@
1
+ import { RemoteServerConfig } from '../types/remoteServer';
2
+
3
+ /**
4
+ * 远程服务器配置相关的事件
5
+ */
6
+ export interface RemoteServerDispatchEvents {
7
+ clearRemoteServerConfig: () => boolean;
8
+ getRemoteServerConfig: () => RemoteServerConfig;
9
+ refreshAccessToken: () => {
10
+ error?: string;
11
+ success: boolean;
12
+ };
13
+ requestAuthorization: (serverUrl: string) => {
14
+ error?: string;
15
+ success: boolean;
16
+ };
17
+ setRemoteServerConfig: (config: RemoteServerConfig) => boolean;
18
+ }
19
+
20
+ /**
21
+ * 从主进程广播的远程服务器相关事件
22
+ */
23
+ export interface RemoteServerBroadcastEvents {
24
+ authorizationFailed: (params: { error: string }) => void;
25
+ authorizationRequired: (params: void) => void;
26
+ authorizationSuccessful: (params: void) => void;
27
+ tokenRefreshed: (params: void) => void;
28
+ }
@@ -1,5 +1,6 @@
1
1
  export * from './dispatch';
2
2
  export * from './file';
3
+ export * from './remoteServer';
3
4
  export * from './route';
4
5
  export * from './shortcut';
5
6
  export * from './update';
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 远程服务器配置相关的事件
3
+ */
4
+ export interface RemoteServerConfig {
5
+ active: boolean;
6
+ isSelfHosted: boolean;
7
+ remoteServerUrl?: string;
8
+ }
@@ -3,5 +3,11 @@
3
3
  "version": "1.0.0",
4
4
  "private": true,
5
5
  "main": "src/index.ts",
6
- "types": "src/index.ts"
6
+ "types": "src/index.ts",
7
+ "dependencies": {
8
+ "debug": "^4.3.4"
9
+ },
10
+ "devDependencies": {
11
+ "@types/debug": "^4.1.7"
12
+ }
7
13
  }
@@ -1,3 +1,4 @@
1
+ import debug from 'debug';
1
2
  import fs from 'node:fs';
2
3
  import net from 'node:net';
3
4
  import os from 'node:os';
@@ -6,6 +7,8 @@ import path from 'node:path';
6
7
  import { SOCK_FILE, SOCK_INFO_FILE, WINDOW_PIPE_FILE } from './const';
7
8
  import { ServerDispatchEventKey } from './events';
8
9
 
10
+ const log = debug('electron-server-ipc:client');
11
+
9
12
  export class ElectronIpcClient {
10
13
  private socketPath: string | null = null;
11
14
  private connected: boolean = false;
@@ -19,6 +22,7 @@ export class ElectronIpcClient {
19
22
  private dataBuffer: string = '';
20
23
 
21
24
  constructor() {
25
+ log('Initializing client');
22
26
  this.initialize();
23
27
  }
24
28
 
@@ -29,13 +33,16 @@ export class ElectronIpcClient {
29
33
  const tempDir = os.tmpdir();
30
34
  const socketInfoPath = path.join(tempDir, SOCK_INFO_FILE);
31
35
 
36
+ log('Looking for socket info at: %s', socketInfoPath);
32
37
  if (fs.existsSync(socketInfoPath)) {
33
38
  const socketInfo = JSON.parse(fs.readFileSync(socketInfoPath, 'utf8'));
34
39
  this.socketPath = socketInfo.socketPath;
40
+ log('Found socket path: %s', this.socketPath);
35
41
  } else {
36
42
  // 如果找不到套接字信息,使用默认路径
37
43
  this.socketPath =
38
44
  process.platform === 'win32' ? WINDOW_PIPE_FILE : path.join(os.tmpdir(), SOCK_FILE);
45
+ log('Socket info not found, using default path: %s', this.socketPath);
39
46
  }
40
47
  } catch (err) {
41
48
  console.error('Failed to initialize IPC client:', err);
@@ -46,21 +53,23 @@ export class ElectronIpcClient {
46
53
  // 连接到 Electron IPC 服务器
47
54
  private connect(): Promise<void> {
48
55
  if (this.connected || !this.socketPath) {
56
+ log('Connection skipped: Connected=%s, SocketPath=%s', this.connected, !!this.socketPath);
49
57
  return Promise.resolve();
50
58
  }
51
59
 
60
+ log('Attempting to connect to socket: %s', this.socketPath);
52
61
  return new Promise((resolve, reject) => {
53
62
  try {
54
63
  this.socket = net.createConnection(this.socketPath!, () => {
55
64
  this.connected = true;
56
65
  this.connectionAttempts = 0;
57
- console.log('[ElectronIpcClient] Connected to Electron IPC server');
66
+ log('Connected to Electron IPC server');
58
67
  resolve();
59
68
  });
60
69
 
61
70
  this.socket.on('data', (data) => {
62
71
  const dataStr = data.toString();
63
- console.log('output:', dataStr);
72
+ log('Received data: %s', dataStr.length > 100 ? `${dataStr.slice(0, 100)}...` : dataStr);
64
73
 
65
74
  // 将新数据添加到缓冲区
66
75
  this.dataBuffer += dataStr;
@@ -70,6 +79,7 @@ export class ElectronIpcClient {
70
79
 
71
80
  // 最后一个元素可能是不完整的消息,保留在缓冲区
72
81
  this.dataBuffer = messages.pop() || '';
82
+ log('Buffer remainder: %d bytes', this.dataBuffer.length);
73
83
 
74
84
  for (const message of messages) {
75
85
  if (!message.trim()) continue; // 跳过空消息
@@ -77,41 +87,42 @@ export class ElectronIpcClient {
77
87
  try {
78
88
  const response = JSON.parse(message);
79
89
  const { id, result, error } = response;
90
+ log('Parsed response for request ID: %s, has error: %s', id, !!error);
80
91
 
81
92
  const pending = this.requestQueue.get(id);
82
93
  if (pending) {
94
+ log('Found pending request for ID: %s', id);
83
95
  this.requestQueue.delete(id);
84
96
  if (error) {
97
+ console.error('Error in response for ID %s: %s', id, error);
85
98
  pending.reject(new Error(error));
86
99
  } else {
100
+ log('Resolving request ID: %s', id);
87
101
  pending.resolve(result);
88
102
  }
103
+ } else {
104
+ log('No pending request found for ID: %s', id);
89
105
  }
90
106
  } catch (err) {
91
- console.error(
92
- '[ElectronIpcClient] Failed to parse response:',
93
- err,
94
- 'message:',
95
- message,
96
- );
107
+ console.error('Failed to parse response: %o, message: %s', err, message);
97
108
  }
98
109
  }
99
110
  });
100
111
 
101
112
  this.socket.on('error', (err) => {
102
- console.error('[ElectronIpcClient] Socket error:', err);
113
+ console.error('Socket error: %o', err);
103
114
  this.connected = false;
104
115
  this.handleDisconnect();
105
116
  reject(err);
106
117
  });
107
118
 
108
119
  this.socket.on('close', () => {
109
- console.log('[ElectronIpcClient] Socket closed');
120
+ log('Socket closed');
110
121
  this.connected = false;
111
122
  this.handleDisconnect();
112
123
  });
113
124
  } catch (err) {
114
- console.error('[ElectronIpcClient] Failed to connect to IPC server:', err);
125
+ console.error('Failed to connect to IPC server: %o', err);
115
126
  this.handleDisconnect();
116
127
  reject(err);
117
128
  }
@@ -120,18 +131,23 @@ export class ElectronIpcClient {
120
131
 
121
132
  // 处理断开连接
122
133
  private handleDisconnect() {
134
+ log('Handling disconnect, connection attempts: %d', this.connectionAttempts);
123
135
  // 清除重连定时器
124
136
  if (this.reconnectTimeout) {
125
137
  clearTimeout(this.reconnectTimeout);
126
138
  this.reconnectTimeout = null;
139
+ log('Cleared reconnect timeout');
127
140
  }
128
141
 
129
142
  // 清空数据缓冲区
130
143
  this.dataBuffer = '';
131
144
 
132
145
  // 拒绝所有待处理的请求
133
- for (const [, { reject }] of this.requestQueue) {
134
- reject(new Error('[ElectronIpcClient] Connection to Electron IPC server lost'));
146
+ const pendingCount = this.requestQueue.size;
147
+ log('Rejecting %d pending requests', pendingCount);
148
+ for (const [id, { reject }] of this.requestQueue) {
149
+ log('Rejecting request ID: %s', id);
150
+ reject(new Error('Connection to Electron IPC server lost'));
135
151
  }
136
152
  this.requestQueue.clear();
137
153
 
@@ -139,52 +155,66 @@ export class ElectronIpcClient {
139
155
  if (this.connectionAttempts < this.maxConnectionAttempts) {
140
156
  this.connectionAttempts++;
141
157
  const delay = Math.min(1000 * Math.pow(2, this.connectionAttempts - 1), 30_000);
158
+ log('Scheduling reconnection attempt %d in %dms', this.connectionAttempts, delay);
142
159
 
143
160
  this.reconnectTimeout = setTimeout(() => {
161
+ log('Attempting reconnection #%d', this.connectionAttempts);
144
162
  this.connect().catch((err) => {
145
- console.error(
146
- `[ElectronIpcClient] Reconnection attempt ${this.connectionAttempts} failed:`,
147
- err,
148
- );
163
+ console.error('Reconnection attempt %d failed: %o', this.connectionAttempts, err);
149
164
  });
150
165
  }, delay);
166
+ } else {
167
+ log('Reached maximum connection attempts, giving up');
151
168
  }
152
169
  }
153
170
 
154
171
  // 发送请求到 Electron IPC 服务器
155
172
  public async sendRequest<T>(method: ServerDispatchEventKey, params: any = {}): Promise<T> {
156
173
  if (!this.socketPath) {
157
- throw new Error('[ElectronIpcClient] Electron IPC connection not available');
174
+ console.error('Cannot send request: Electron IPC connection not available');
175
+ throw new Error('Electron IPC connection not available');
158
176
  }
159
177
 
160
178
  // 如果未连接,先连接
161
179
  if (!this.connected) {
180
+ log('Not connected, connecting before sending request');
162
181
  await this.connect();
163
182
  }
164
183
 
184
+ log('Sending request: %s %o', method, params);
165
185
  return new Promise<T>((resolve, reject) => {
166
186
  try {
167
187
  const id = Math.random().toString(36).slice(2, 15);
168
188
  const request = { id, method, params };
189
+ log('Created request with ID: %s', id);
169
190
 
170
191
  // 将请求添加到队列
171
192
  this.requestQueue.set(id, { reject, resolve });
193
+ log('Added request to queue, current queue size: %d', this.requestQueue.size);
172
194
 
173
195
  // 设置超时
174
196
  const timeout = setTimeout(() => {
175
197
  this.requestQueue.delete(id);
176
- reject(new Error(`[ElectronIpcClient] Request timed out, method: ${method}`));
198
+ const errorMsg = `Request timed out, method: ${method}`;
199
+ console.error('Request timed out, ID: %s, method: %s', id, method);
200
+ reject(new Error(errorMsg));
177
201
  }, 5000);
178
202
 
179
203
  // 发送请求
180
- this.socket!.write(JSON.stringify(request), (err) => {
204
+ const requestJson = JSON.stringify(request);
205
+ log('Writing request to socket, size: %d bytes', requestJson.length);
206
+ this.socket!.write(requestJson, (err) => {
181
207
  if (err) {
182
208
  clearTimeout(timeout);
183
209
  this.requestQueue.delete(id);
210
+ console.error('Failed to write request to socket: %o', err);
184
211
  reject(err);
212
+ } else {
213
+ log('Request successfully written to socket, ID: %s', id);
185
214
  }
186
215
  });
187
216
  } catch (err) {
217
+ console.error('Error sending request: %o', err);
188
218
  reject(err);
189
219
  }
190
220
  });
@@ -192,16 +222,20 @@ export class ElectronIpcClient {
192
222
 
193
223
  // 关闭连接
194
224
  public close() {
225
+ log('Closing client connection');
195
226
  if (this.reconnectTimeout) {
196
227
  clearTimeout(this.reconnectTimeout);
197
228
  this.reconnectTimeout = null;
229
+ log('Cleared reconnect timeout');
198
230
  }
199
231
 
200
232
  if (this.socket) {
233
+ log('Ending socket connection');
201
234
  this.socket.end();
202
235
  this.socket = null;
203
236
  }
204
237
 
205
238
  this.connected = false;
239
+ log('Client connection closed');
206
240
  }
207
241
  }
@@ -1,3 +1,4 @@
1
+ import debug from 'debug';
1
2
  import fs from 'node:fs';
2
3
  import net from 'node:net';
3
4
  import os from 'node:os';
@@ -7,6 +8,8 @@ import { SOCK_FILE, SOCK_INFO_FILE, WINDOW_PIPE_FILE } from './const';
7
8
  import { ServerDispatchEventKey } from './events';
8
9
  import { ElectronIPCEventHandler } from './types';
9
10
 
11
+ const log = debug('electron-server-ipc:server');
12
+
10
13
  export class ElectronIPCServer {
11
14
  private server: net.Server;
12
15
  private socketPath: string;
@@ -19,10 +22,12 @@ export class ElectronIPCServer {
19
22
 
20
23
  // 如果是 Unix 套接字,确保文件不存在
21
24
  if (!isWindows && fs.existsSync(this.socketPath)) {
25
+ log('Removing existing socket file at: %s', this.socketPath);
22
26
  fs.unlinkSync(this.socketPath);
23
27
  }
24
28
 
25
29
  // 创建服务器
30
+ log('Creating IPC server');
26
31
  this.server = net.createServer(this.handleConnection.bind(this));
27
32
 
28
33
  this.eventHandler = eventHandler;
@@ -30,18 +35,20 @@ export class ElectronIPCServer {
30
35
 
31
36
  // 启动服务器
32
37
  public start(): Promise<void> {
38
+ log('Starting IPC server');
33
39
  return new Promise((resolve, reject) => {
34
40
  this.server.on('error', (err) => {
35
- console.error('IPC Server error:', err);
41
+ console.error('IPC Server error: %o', err);
36
42
  reject(err);
37
43
  });
38
44
 
39
45
  this.server.listen(this.socketPath, () => {
40
- console.log(`[ElectronIPCServer] Electron IPC server listening on ${this.socketPath}`);
46
+ log('Electron IPC server listening on %s', this.socketPath);
41
47
 
42
48
  // 将套接字路径写入临时文件,供 Next.js 服务端读取
43
49
  const tempDir = os.tmpdir();
44
50
  const socketInfoPath = path.join(tempDir, SOCK_INFO_FILE);
51
+ log('Writing socket info to: %s', socketInfoPath);
45
52
  fs.writeFileSync(socketInfoPath, JSON.stringify({ socketPath: this.socketPath }), 'utf8');
46
53
 
47
54
  resolve();
@@ -52,62 +59,88 @@ export class ElectronIPCServer {
52
59
  // 处理客户端连接
53
60
  private handleConnection(socket: net.Socket): void {
54
61
  let dataBuffer = '';
62
+ log('New client connection established');
55
63
 
56
64
  socket.on('data', (data) => {
57
- dataBuffer += data.toString();
65
+ const chunk = data.toString();
66
+ log('Received data chunk, size: %d bytes', chunk.length);
67
+ dataBuffer += chunk;
58
68
 
59
69
  try {
60
70
  // 尝试解析 JSON 消息
61
71
  const message = JSON.parse(dataBuffer);
72
+ log('Successfully parsed JSON message: %o', {
73
+ id: message.id,
74
+ method: message.method,
75
+ });
62
76
  dataBuffer = ''; // 重置缓冲区
63
77
 
64
78
  // 处理请求
65
79
  this.handleRequest(socket, message);
66
80
  } catch {
67
81
  // 如果不是有效的 JSON,可能是消息不完整,继续等待
82
+ log('Incomplete or invalid JSON, buffering for more data');
68
83
  }
69
84
  });
70
85
 
71
86
  socket.on('error', (err) => {
72
- console.error('Socket error:', err);
87
+ console.error('Socket error: %o', err);
88
+ });
89
+
90
+ socket.on('close', () => {
91
+ log('Client connection closed');
73
92
  });
74
93
  }
75
94
 
76
95
  // 处理客户端请求
77
96
  private handleRequest = async (socket: net.Socket, request: any) => {
78
97
  const { id, method, params } = request;
98
+ log('Handling request: %s (ID: %s)', method, id);
79
99
 
80
100
  // 根据请求方法执行相应的操作
81
101
  const eventHandler = this.eventHandler[method as ServerDispatchEventKey];
82
- if (!eventHandler) return;
102
+ if (!eventHandler) {
103
+ console.error('No handler found for method: %s', method);
104
+ return;
105
+ }
83
106
 
84
107
  try {
108
+ log('Executing handler for method: %s with params: %o', method, params);
85
109
  const data = await eventHandler(params, { id, method, socket });
110
+ log('Handler execution successful for method: %s', method);
86
111
 
87
112
  this.sendResult(socket, id, data);
88
113
  } catch (err) {
89
- this.sendError(socket, id, `Failed to handle method(${method}): ${(err as Error).message}`);
114
+ const errorMsg = `Failed to handle method(${method}): ${(err as Error).message}`;
115
+ console.error('Error handling request: %s', errorMsg);
116
+ this.sendError(socket, id, errorMsg);
90
117
  }
91
118
  };
92
119
 
93
120
  // 发送结果
94
121
  private sendResult(socket: net.Socket, id: string, result: any): void {
95
- socket.write(JSON.stringify({ id, result }) + '\n');
122
+ const response = JSON.stringify({ id, result }) + '\n';
123
+ log('Sending success response for ID: %s, size: %d bytes', id, response.length);
124
+ socket.write(response);
96
125
  }
97
126
 
98
127
  // 发送错误
99
128
  private sendError(socket: net.Socket, id: string, error: string): void {
100
- socket.write(JSON.stringify({ error, id }) + '\n');
129
+ const response = JSON.stringify({ error, id }) + '\n';
130
+ log('Sending error response for ID: %s: %s', id, error);
131
+ socket.write(response);
101
132
  }
102
133
 
103
134
  // 关闭服务器
104
135
  public close(): Promise<void> {
136
+ log('Closing IPC server');
105
137
  return new Promise((resolve) => {
106
138
  this.server.close(() => {
107
- console.log('Electron IPC server closed');
139
+ log('Electron IPC server closed');
108
140
 
109
141
  // 删除套接字文件(Unix 平台)
110
142
  if (process.platform !== 'win32' && fs.existsSync(this.socketPath)) {
143
+ log('Removing socket file: %s', this.socketPath);
111
144
  fs.unlinkSync(this.socketPath);
112
145
  }
113
146