@lobehub/chat 1.85.6 → 1.85.8

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 (74) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/apps/desktop/electron-builder.js +10 -13
  3. package/changelog/v1.json +18 -0
  4. package/locales/ar/models.json +3 -0
  5. package/locales/ar/plugin.json +1 -1
  6. package/locales/bg-BG/models.json +3 -0
  7. package/locales/bg-BG/plugin.json +1 -1
  8. package/locales/de-DE/models.json +3 -0
  9. package/locales/de-DE/plugin.json +1 -1
  10. package/locales/en-US/models.json +3 -0
  11. package/locales/en-US/plugin.json +1 -1
  12. package/locales/es-ES/models.json +3 -0
  13. package/locales/es-ES/plugin.json +1 -1
  14. package/locales/fa-IR/models.json +3 -0
  15. package/locales/fa-IR/plugin.json +1 -1
  16. package/locales/fr-FR/models.json +3 -0
  17. package/locales/fr-FR/plugin.json +1 -1
  18. package/locales/it-IT/models.json +3 -0
  19. package/locales/it-IT/plugin.json +1 -1
  20. package/locales/ja-JP/models.json +3 -0
  21. package/locales/ja-JP/plugin.json +1 -1
  22. package/locales/ko-KR/models.json +3 -0
  23. package/locales/ko-KR/plugin.json +1 -1
  24. package/locales/nl-NL/models.json +3 -0
  25. package/locales/nl-NL/plugin.json +1 -1
  26. package/locales/pl-PL/models.json +3 -0
  27. package/locales/pl-PL/plugin.json +1 -1
  28. package/locales/pt-BR/models.json +3 -0
  29. package/locales/pt-BR/plugin.json +1 -1
  30. package/locales/ru-RU/models.json +3 -0
  31. package/locales/ru-RU/plugin.json +1 -1
  32. package/locales/tr-TR/models.json +3 -0
  33. package/locales/tr-TR/plugin.json +1 -1
  34. package/locales/vi-VN/models.json +3 -0
  35. package/locales/vi-VN/plugin.json +1 -1
  36. package/locales/zh-CN/models.json +3 -0
  37. package/locales/zh-CN/plugin.json +1 -1
  38. package/locales/zh-TW/models.json +3 -0
  39. package/locales/zh-TW/plugin.json +1 -1
  40. package/package.json +2 -2
  41. package/packages/file-loaders/package.json +1 -1
  42. package/packages/file-loaders/test/setup.ts +1 -1
  43. package/src/features/DataImporter/index.tsx +0 -1
  44. package/src/features/PluginDevModal/MCPManifestForm/index.tsx +11 -1
  45. package/src/locales/default/plugin.ts +1 -1
  46. package/src/server/routers/lambda/__tests__/importer.test.ts +3 -0
  47. package/src/server/routers/lambda/importer.ts +10 -2
  48. package/src/services/__tests__/upload.test.ts +4 -6
  49. package/src/services/import/server.ts +20 -71
  50. package/src/services/ragEval.ts +1 -1
  51. package/src/services/upload.ts +52 -18
  52. package/src/store/chat/slices/builtinTool/actions/dalle.test.ts +2 -0
  53. package/src/store/chat/slices/builtinTool/actions/localFile.ts +1 -1
  54. package/src/store/file/slices/upload/action.ts +14 -27
  55. package/src/tools/index.ts +3 -3
  56. package/src/tools/{local-files → local-system}/Render/ListFiles/Result.tsx +1 -1
  57. package/src/tools/{local-files → local-system}/Render/ListFiles/index.tsx +1 -1
  58. package/src/tools/{local-files → local-system}/Render/ReadLocalFile/index.tsx +1 -1
  59. package/src/tools/{local-files → local-system}/Render/RenameLocalFile/index.tsx +1 -1
  60. package/src/tools/{local-files → local-system}/Render/RunCommand/index.tsx +1 -1
  61. package/src/tools/{local-files → local-system}/Render/SearchFiles/Result.tsx +2 -2
  62. package/src/tools/{local-files → local-system}/Render/SearchFiles/SearchQuery/index.tsx +1 -1
  63. package/src/tools/{local-files → local-system}/Render/SearchFiles/index.tsx +1 -1
  64. package/src/tools/{local-files → local-system}/Render/index.tsx +6 -6
  65. package/src/tools/{local-files → local-system}/index.ts +10 -10
  66. package/src/tools/renders.ts +3 -3
  67. /package/src/tools/{local-files → local-system}/Render/ReadLocalFile/ReadFileSkeleton.tsx +0 -0
  68. /package/src/tools/{local-files → local-system}/Render/ReadLocalFile/ReadFileView.tsx +0 -0
  69. /package/src/tools/{local-files → local-system}/Render/ReadLocalFile/style.ts +0 -0
  70. /package/src/tools/{local-files → local-system}/Render/SearchFiles/SearchQuery/SearchView.tsx +0 -0
  71. /package/src/tools/{local-files → local-system}/Render/WriteFile/index.tsx +0 -0
  72. /package/src/tools/{local-files → local-system}/components/FileItem.tsx +0 -0
  73. /package/src/tools/{local-files → local-system}/systemRole.ts +0 -0
  74. /package/src/tools/{local-files → local-system}/type.ts +0 -0
@@ -8,6 +8,7 @@ import { ImportResultData } from '@/types/importer';
8
8
  import { importerRouter } from '../importer';
9
9
 
10
10
  const mockGetFileContent = vi.fn();
11
+ const mockDeleteFile = vi.fn();
11
12
  const mockImportData = vi.fn();
12
13
  const mockImportPgData = vi.fn();
13
14
 
@@ -21,6 +22,7 @@ vi.mock('@/database/repositories/dataImporter', () => ({
21
22
  vi.mock('@/server/services/file', () => ({
22
23
  FileService: vi.fn().mockImplementation(() => ({
23
24
  getFileContent: mockGetFileContent,
25
+ deleteFile: mockDeleteFile,
24
26
  })),
25
27
  }));
26
28
 
@@ -74,6 +76,7 @@ describe('importerRouter', () => {
74
76
  expect(result).toEqual(mockImportResult);
75
77
  expect(mockGetFileContent).toHaveBeenCalledWith('test.json');
76
78
  expect(mockImportData).toHaveBeenCalledWith(JSON.parse(mockFileContent));
79
+ expect(mockDeleteFile).toHaveBeenCalledWith('test.json');
77
80
  });
78
81
 
79
82
  it('should handle PG data import', async () => {
@@ -39,11 +39,19 @@ export const importerRouter = router({
39
39
  });
40
40
  }
41
41
 
42
+ let result: ImportResultData;
42
43
  if ('schemaHash' in data) {
43
- return ctx.dataImporterService.importPgData(data as unknown as ImportPgDataStructure);
44
+ result = await ctx.dataImporterService.importPgData(
45
+ data as unknown as ImportPgDataStructure,
46
+ );
47
+ } else {
48
+ result = await ctx.dataImporterService.importData(data);
44
49
  }
45
50
 
46
- return ctx.dataImporterService.importData(data);
51
+ // clean file after upload
52
+ await ctx.fileService.deleteFile(input.pathname);
53
+
54
+ return result;
47
55
  }),
48
56
 
49
57
  importByPost: importProcedure
@@ -69,7 +69,7 @@ describe('UploadService', () => {
69
69
  }
70
70
  });
71
71
 
72
- const result = await uploadService.uploadWithProgress(mockFile, { onProgress });
72
+ const result = await uploadService.uploadToServerS3(mockFile, { onProgress });
73
73
 
74
74
  expect(result).toEqual({
75
75
  date: '1',
@@ -91,9 +91,7 @@ describe('UploadService', () => {
91
91
  }
92
92
  });
93
93
 
94
- await expect(uploadService.uploadWithProgress(mockFile, {})).rejects.toBe(
95
- UPLOAD_NETWORK_ERROR,
96
- );
94
+ await expect(uploadService.uploadToServerS3(mockFile, {})).rejects.toBe(UPLOAD_NETWORK_ERROR);
97
95
  });
98
96
 
99
97
  it('should handle upload error', async () => {
@@ -109,7 +107,7 @@ describe('UploadService', () => {
109
107
  }
110
108
  });
111
109
 
112
- await expect(uploadService.uploadWithProgress(mockFile, {})).rejects.toBe('Bad Request');
110
+ await expect(uploadService.uploadToServerS3(mockFile, {})).rejects.toBe('Bad Request');
113
111
  });
114
112
  });
115
113
 
@@ -125,7 +123,7 @@ describe('UploadService', () => {
125
123
 
126
124
  (clientS3Storage.putObject as any).mockResolvedValue(undefined);
127
125
 
128
- const result = await uploadService.uploadToClientS3(hash, mockFile);
126
+ const result = await uploadService['uploadToClientS3'](hash, mockFile);
129
127
 
130
128
  expect(clientS3Storage.putObject).toHaveBeenCalledWith(hash, mockFile);
131
129
  expect(result).toEqual(expectedResult);
@@ -1,6 +1,7 @@
1
1
  import { DefaultErrorShape } from '@trpc/server/unstable-core-do-not-import';
2
2
 
3
- import { edgeClient, lambdaClient } from '@/libs/trpc/client';
3
+ import { lambdaClient } from '@/libs/trpc/client';
4
+ import { uploadService } from '@/services/upload';
4
5
  import { useUserStore } from '@/store/user';
5
6
  import { ImportPgDataStructure } from '@/types/export';
6
7
  import { ImportStage, OnImportCallbacks } from '@/types/importer';
@@ -48,30 +49,7 @@ export class ServerService implements IImportService {
48
49
  return;
49
50
  }
50
51
 
51
- // if the data is too large, upload it to S3 and upload by file
52
- const filename = `${uuid()}.json`;
53
-
54
- const pathname = `import_config/${filename}`;
55
-
56
- const url = await edgeClient.upload.createS3PreSignedUrl.mutate({ pathname });
57
-
58
- try {
59
- callbacks?.onStageChange?.(ImportStage.Uploading);
60
- await this.uploadWithProgress(url, data, callbacks?.onFileUploading);
61
- } catch {
62
- throw new Error('Upload Error');
63
- }
64
-
65
- callbacks?.onStageChange?.(ImportStage.Importing);
66
- const time = Date.now();
67
- try {
68
- const result = await lambdaClient.importer.importByFile.mutate({ pathname });
69
- const duration = Date.now() - time;
70
- callbacks?.onStageChange?.(ImportStage.Success);
71
- callbacks?.onSuccess?.(result.results, duration);
72
- } catch (e) {
73
- handleError(e);
74
- }
52
+ await this.uploadData(data, { callbacks, handleError });
75
53
  };
76
54
 
77
55
  importPgData: IImportService['importPgData'] = async (
@@ -115,16 +93,28 @@ export class ServerService implements IImportService {
115
93
  return;
116
94
  }
117
95
 
96
+ await this.uploadData(data, { callbacks, handleError });
97
+ };
98
+
99
+ private uploadData = async (
100
+ data: object,
101
+ { callbacks, handleError }: { callbacks?: OnImportCallbacks; handleError: (e: unknown) => any },
102
+ ) => {
118
103
  // if the data is too large, upload it to S3 and upload by file
119
104
  const filename = `${uuid()}.json`;
120
105
 
121
- const pathname = `import_config/${filename}`;
122
-
123
- const url = await edgeClient.upload.createS3PreSignedUrl.mutate({ pathname });
124
-
106
+ let pathname;
125
107
  try {
126
108
  callbacks?.onStageChange?.(ImportStage.Uploading);
127
- await this.uploadWithProgress(url, data, callbacks?.onFileUploading);
109
+ const result = await uploadService.uploadDataToS3(data, {
110
+ filename,
111
+ onProgress: (status, state) => {
112
+ callbacks?.onFileUploading?.(state);
113
+ },
114
+ pathname: `import_config/${filename}`,
115
+ });
116
+ pathname = result.data.path;
117
+ console.log(pathname);
128
118
  } catch {
129
119
  throw new Error('Upload Error');
130
120
  }
@@ -140,45 +130,4 @@ export class ServerService implements IImportService {
140
130
  handleError(e);
141
131
  }
142
132
  };
143
-
144
- private uploadWithProgress = async (
145
- url: string,
146
- data: object,
147
- onProgress: OnImportCallbacks['onFileUploading'],
148
- ) => {
149
- const xhr = new XMLHttpRequest();
150
-
151
- let startTime = Date.now();
152
- xhr.upload.addEventListener('progress', (event) => {
153
- if (event.lengthComputable) {
154
- const progress = Number(((event.loaded / event.total) * 100).toFixed(1));
155
-
156
- const speedInByte = event.loaded / ((Date.now() - startTime) / 1000);
157
-
158
- onProgress?.({
159
- // if the progress is 100, it means the file is uploaded
160
- // but the server is still processing it
161
- // so make it as 99.5 and let users think it's still uploading
162
- progress: progress === 100 ? 99.5 : progress,
163
- restTime: (event.total - event.loaded) / speedInByte,
164
- speed: speedInByte / 1024,
165
- });
166
- }
167
- });
168
-
169
- xhr.open('PUT', url);
170
- xhr.setRequestHeader('Content-Type', 'application/json');
171
-
172
- return new Promise((resolve, reject) => {
173
- xhr.addEventListener('load', () => {
174
- if (xhr.status >= 200 && xhr.status < 300) {
175
- resolve(xhr.response);
176
- } else {
177
- reject(xhr.statusText);
178
- }
179
- });
180
- xhr.addEventListener('error', () => reject(xhr.statusText));
181
- xhr.send(JSON.stringify(data));
182
- });
183
- };
184
133
  }
@@ -40,7 +40,7 @@ class RAGEvalService {
40
40
  };
41
41
 
42
42
  importDatasetRecords = async (datasetId: number, file: File): Promise<void> => {
43
- const { path } = await uploadService.uploadWithProgress(file, { directory: 'ragEval' });
43
+ const { path } = await uploadService.uploadToServerS3(file, { directory: 'ragEval' });
44
44
 
45
45
  await lambdaClient.ragEval.importDatasetRecords.mutate({ datasetId, pathname: path });
46
46
  };
@@ -2,7 +2,7 @@ import dayjs from 'dayjs';
2
2
  import { sha256 } from 'js-sha256';
3
3
 
4
4
  import { fileEnv } from '@/config/file';
5
- import { isServerMode } from '@/const/version';
5
+ import { isDesktop, isServerMode } from '@/const/version';
6
6
  import { parseDataUri } from '@/libs/agent-runtime/utils/uriParser';
7
7
  import { edgeClient } from '@/libs/trpc/client';
8
8
  import { API_ENDPOINTS } from '@/services/_url';
@@ -16,7 +16,10 @@ export const UPLOAD_NETWORK_ERROR = 'NetWorkError';
16
16
  interface UploadFileToS3Options {
17
17
  directory?: string;
18
18
  filename?: string;
19
+ onNotSupported?: () => void;
19
20
  onProgress?: (status: FileUploadStatus, state: FileUploadState) => void;
21
+ pathname?: string;
22
+ skipCheckFileType?: boolean;
20
23
  }
21
24
 
22
25
  class UploadService {
@@ -25,20 +28,43 @@ class UploadService {
25
28
  */
26
29
  uploadFileToS3 = async (
27
30
  file: File,
28
- options: UploadFileToS3Options = {},
29
- ): Promise<FileMetadata> => {
30
- const { directory, onProgress } = options;
31
+ { onProgress, directory, skipCheckFileType, onNotSupported, pathname }: UploadFileToS3Options,
32
+ ): Promise<{ data: FileMetadata; success: boolean }> => {
33
+ const { getElectronStoreState } = await import('@/store/electron');
34
+ const { electronSyncSelectors } = await import('@/store/electron/selectors');
35
+ // only if not enable sync
36
+ const state = getElectronStoreState();
37
+ const isSyncActive = electronSyncSelectors.isSyncActive(state);
38
+
39
+ // 桌面端上传逻辑(并且没开启 sync 同步)
40
+ if (isDesktop && !isSyncActive) {
41
+ const data = await this.uploadToDesktopS3(file);
42
+ return { data, success: true };
43
+ }
31
44
 
45
+ // 服务端上传逻辑
32
46
  if (isServerMode) {
33
- return this.uploadWithProgress(file, { directory, onProgress });
34
- } else {
35
- const fileArrayBuffer = await file.arrayBuffer();
47
+ // if is server mode, upload to server s3,
36
48
 
37
- // 1. check file hash
38
- const hash = sha256(fileArrayBuffer);
49
+ const data = await this.uploadToServerS3(file, { directory, onProgress, pathname });
50
+ return { data, success: true };
51
+ }
39
52
 
40
- return this.uploadToClientS3(hash, file);
53
+ // upload to client s3
54
+ // 客户端上传逻辑
55
+ if (!skipCheckFileType && !file.type.startsWith('image')) {
56
+ onNotSupported?.();
57
+ return { data: undefined as unknown as FileMetadata, success: false };
41
58
  }
59
+
60
+ const fileArrayBuffer = await file.arrayBuffer();
61
+
62
+ // 1. check file hash
63
+ const hash = sha256(fileArrayBuffer);
64
+ // Upload to the indexeddb in the browser
65
+ const data = await this.uploadToClientS3(hash, file);
66
+
67
+ return { data, success: true };
42
68
  };
43
69
 
44
70
  uploadBase64ToS3 = async (
@@ -79,7 +105,7 @@ class UploadService {
79
105
  const file = new File([blob], fileName, { type: mimeType });
80
106
 
81
107
  // 使用统一的上传方法
82
- const metadata = await this.uploadFileToS3(file, options);
108
+ const { data: metadata } = await this.uploadFileToS3(file, options);
83
109
  const hash = sha256(await file.arrayBuffer());
84
110
 
85
111
  return {
@@ -90,19 +116,27 @@ class UploadService {
90
116
  };
91
117
  };
92
118
 
93
- uploadWithProgress = async (
119
+ uploadDataToS3 = async (data: object, options: UploadFileToS3Options = {}) => {
120
+ const blob = new Blob([JSON.stringify(data)], { type: 'application/json' });
121
+ const file = new File([blob], options.filename || 'data.json', { type: 'application/json' });
122
+ return await this.uploadFileToS3(file, options);
123
+ };
124
+
125
+ uploadToServerS3 = async (
94
126
  file: File,
95
127
  {
96
128
  onProgress,
97
129
  directory,
130
+ pathname,
98
131
  }: {
99
132
  directory?: string;
100
133
  onProgress?: (status: FileUploadStatus, state: FileUploadState) => void;
134
+ pathname?: string;
101
135
  },
102
136
  ): Promise<FileMetadata> => {
103
137
  const xhr = new XMLHttpRequest();
104
138
 
105
- const { preSignUrl, ...result } = await this.getSignedUploadUrl(file, directory);
139
+ const { preSignUrl, ...result } = await this.getSignedUploadUrl(file, { directory, pathname });
106
140
  let startTime = Date.now();
107
141
  xhr.upload.addEventListener('progress', (event) => {
108
142
  if (event.lengthComputable) {
@@ -148,7 +182,7 @@ class UploadService {
148
182
  return result;
149
183
  };
150
184
 
151
- uploadToDesktop = async (file: File) => {
185
+ private uploadToDesktopS3 = async (file: File) => {
152
186
  const fileArrayBuffer = await file.arrayBuffer();
153
187
  const hash = sha256(fileArrayBuffer);
154
188
 
@@ -157,7 +191,7 @@ class UploadService {
157
191
  return metadata;
158
192
  };
159
193
 
160
- uploadToClientS3 = async (hash: string, file: File): Promise<FileMetadata> => {
194
+ private uploadToClientS3 = async (hash: string, file: File): Promise<FileMetadata> => {
161
195
  await clientS3Storage.putObject(hash, file);
162
196
 
163
197
  return {
@@ -183,7 +217,7 @@ class UploadService {
183
217
 
184
218
  private getSignedUploadUrl = async (
185
219
  file: File,
186
- directory?: string,
220
+ options: { directory?: string; pathname?: string } = {},
187
221
  ): Promise<
188
222
  FileMetadata & {
189
223
  preSignUrl: string;
@@ -193,8 +227,8 @@ class UploadService {
193
227
 
194
228
  // 精确到以 h 为单位的 path
195
229
  const date = (Date.now() / 1000 / 60 / 60).toFixed(0);
196
- const dirname = `${directory || fileEnv.NEXT_PUBLIC_S3_FILE_PATH}/${date}`;
197
- const pathname = `${dirname}/${filename}`;
230
+ const dirname = `${options.directory || fileEnv.NEXT_PUBLIC_S3_FILE_PATH}/${date}`;
231
+ const pathname = options.pathname ?? `${dirname}/${filename}`;
198
232
 
199
233
  const preSignUrl = await edgeClient.upload.createS3PreSignedUrl.mutate({ pathname });
200
234
 
@@ -41,6 +41,7 @@ describe('chatToolSlice - dalle', () => {
41
41
  vi.spyOn(uploadService, 'getImageFileByUrlWithCORS').mockResolvedValue(
42
42
  new File(['1'], 'file.png', { type: 'image/png' }),
43
43
  );
44
+ // @ts-ignore
44
45
  vi.spyOn(uploadService, 'uploadToClientS3').mockResolvedValue({} as any);
45
46
  vi.spyOn(ClientService.prototype, 'createFile').mockResolvedValue({
46
47
  id: mockId,
@@ -56,6 +57,7 @@ describe('chatToolSlice - dalle', () => {
56
57
  });
57
58
  // For each prompt, loading is toggled on and then off
58
59
  expect(imageGenerationService.generateImage).toHaveBeenCalledTimes(prompts.length);
60
+ // @ts-ignore
59
61
  expect(uploadService.uploadToClientS3).toHaveBeenCalledTimes(prompts.length);
60
62
  expect(result.current.toggleDallEImageLoading).toHaveBeenCalledTimes(prompts.length * 2);
61
63
  });
@@ -19,7 +19,7 @@ import {
19
19
  LocalReadFileState,
20
20
  LocalReadFilesState,
21
21
  LocalRenameFileState,
22
- } from '@/tools/local-files/type';
22
+ } from '@/tools/local-system/type';
23
23
 
24
24
  export interface LocalFileAction {
25
25
  internal_triggerLocalFileToolCalling: <T = any>(
@@ -4,11 +4,8 @@ import { StateCreator } from 'zustand/vanilla';
4
4
 
5
5
  import { message } from '@/components/AntdStaticMethods';
6
6
  import { LOBE_CHAT_CLOUD } from '@/const/branding';
7
- import { isDesktop, isServerMode } from '@/const/version';
8
7
  import { fileService } from '@/services/file';
9
8
  import { uploadService } from '@/services/upload';
10
- import { getElectronStoreState } from '@/store/electron';
11
- import { electronSyncSelectors } from '@/store/electron/selectors';
12
9
  import { FileMetadata, UploadFileItem } from '@/types/files';
13
10
 
14
11
  import { FileStore } from '../../store';
@@ -96,25 +93,8 @@ export const createFileUploadSlice: StateCreator<
96
93
  }
97
94
  // 2. if file don't exist, need upload files
98
95
  else {
99
- // only if not enable sync
100
- const state = getElectronStoreState();
101
- const isSyncActive = electronSyncSelectors.isSyncActive(state);
102
-
103
- if (isDesktop && !isSyncActive) {
104
- metadata = await uploadService.uploadToDesktop(file);
105
- } else if (isServerMode) {
106
- // if is server mode, upload to server s3, or upload to client s3
107
- metadata = await uploadService.uploadWithProgress(file, {
108
- onProgress: (status, upload) => {
109
- onStatusUpdate?.({
110
- id: file.name,
111
- type: 'updateFile',
112
- value: { status: status === 'success' ? 'processing' : status, uploadState: upload },
113
- });
114
- },
115
- });
116
- } else {
117
- if (!skipCheckFileType && !file.type.startsWith('image')) {
96
+ const { data, success } = await uploadService.uploadFileToS3(file, {
97
+ onNotSupported: () => {
118
98
  onStatusUpdate?.({ id: file.name, type: 'removeFile' });
119
99
  message.info({
120
100
  content: t('upload.fileOnlySupportInServerMode', {
@@ -124,12 +104,19 @@ export const createFileUploadSlice: StateCreator<
124
104
  }),
125
105
  duration: 5,
126
106
  });
127
- return;
128
- }
107
+ },
108
+ onProgress: (status, upload) => {
109
+ onStatusUpdate?.({
110
+ id: file.name,
111
+ type: 'updateFile',
112
+ value: { status: status === 'success' ? 'processing' : status, uploadState: upload },
113
+ });
114
+ },
115
+ skipCheckFileType,
116
+ });
117
+ if (!success) return;
129
118
 
130
- // Upload to the indexeddb in the browser
131
- metadata = await uploadService.uploadToClientS3(hash, file);
132
- }
119
+ metadata = data;
133
120
  }
134
121
 
135
122
  // 3. use more powerful file type detector to get file type
@@ -3,7 +3,7 @@ import { LobeBuiltinTool } from '@/types/tool';
3
3
 
4
4
  import { ArtifactsManifest } from './artifacts';
5
5
  import { DalleManifest } from './dalle';
6
- import { LocalFilesManifest } from './local-files';
6
+ import { LocalSystemManifest } from './local-system';
7
7
  import { WebBrowsingManifest } from './web-browsing';
8
8
 
9
9
  export const builtinTools: LobeBuiltinTool[] = [
@@ -19,8 +19,8 @@ export const builtinTools: LobeBuiltinTool[] = [
19
19
  },
20
20
  {
21
21
  hidden: !isDesktop,
22
- identifier: LocalFilesManifest.identifier,
23
- manifest: LocalFilesManifest,
22
+ identifier: LocalSystemManifest.identifier,
23
+ manifest: LocalSystemManifest,
24
24
  type: 'builtin',
25
25
  },
26
26
  {
@@ -5,7 +5,7 @@ import { Flexbox } from 'react-layout-kit';
5
5
 
6
6
  import { useChatStore } from '@/store/chat';
7
7
  import { chatToolSelectors } from '@/store/chat/selectors';
8
- import FileItem from '@/tools/local-files/components/FileItem';
8
+ import FileItem from '@/tools/local-system/components/FileItem';
9
9
  import { ChatMessagePluginError } from '@/types/message';
10
10
 
11
11
  interface SearchFilesProps {
@@ -2,7 +2,7 @@ import { ListLocalFileParams } from '@lobechat/electron-client-ipc';
2
2
  import React, { memo } from 'react';
3
3
 
4
4
  import { LocalFolder } from '@/features/LocalFile';
5
- import { LocalFileListState } from '@/tools/local-files/type';
5
+ import { LocalFileListState } from '@/tools/local-system/type';
6
6
  import { ChatMessagePluginError } from '@/types/message';
7
7
 
8
8
  import SearchResult from './Result';
@@ -3,7 +3,7 @@ import { memo } from 'react';
3
3
 
4
4
  import { useChatStore } from '@/store/chat';
5
5
  import { chatToolSelectors } from '@/store/chat/slices/builtinTool/selectors';
6
- import { LocalReadFileState } from '@/tools/local-files/type';
6
+ import { LocalReadFileState } from '@/tools/local-system/type';
7
7
  import { ChatMessagePluginError } from '@/types/message';
8
8
 
9
9
  import ReadFileSkeleton from './ReadFileSkeleton';
@@ -7,7 +7,7 @@ import React, { memo } from 'react';
7
7
  import { Flexbox } from 'react-layout-kit';
8
8
 
9
9
  import { LocalFile } from '@/features/LocalFile';
10
- import { LocalReadFileState } from '@/tools/local-files/type';
10
+ import { LocalReadFileState } from '@/tools/local-system/type';
11
11
  import { ChatMessagePluginError } from '@/types/message';
12
12
 
13
13
  const useStyles = createStyles(({ css, token }) => ({
@@ -3,7 +3,7 @@ import { Terminal } from '@xterm/xterm';
3
3
  import '@xterm/xterm/css/xterm.css';
4
4
  import { memo, useEffect, useRef } from 'react';
5
5
 
6
- import { LocalReadFileState } from '@/tools/local-files/type';
6
+ import { LocalReadFileState } from '@/tools/local-system/type';
7
7
  import { ChatMessagePluginError } from '@/types/message';
8
8
 
9
9
  interface RunCommandProps {
@@ -4,8 +4,8 @@ import { Flexbox } from 'react-layout-kit';
4
4
 
5
5
  import { useChatStore } from '@/store/chat';
6
6
  import { chatToolSelectors } from '@/store/chat/selectors';
7
- import FileItem from '@/tools/local-files/components/FileItem';
8
- import { FileResult } from '@/tools/local-files/type';
7
+ import FileItem from '@/tools/local-system/components/FileItem';
8
+ import { FileResult } from '@/tools/local-system/type';
9
9
  import { ChatMessagePluginError } from '@/types/message';
10
10
 
11
11
  interface SearchFilesProps {
@@ -7,7 +7,7 @@ import { Flexbox } from 'react-layout-kit';
7
7
 
8
8
  import { useChatStore } from '@/store/chat';
9
9
  import { chatToolSelectors } from '@/store/chat/selectors';
10
- import { LocalFileSearchState } from '@/tools/local-files/type';
10
+ import { LocalFileSearchState } from '@/tools/local-system/type';
11
11
 
12
12
  import SearchView from './SearchView';
13
13
 
@@ -1,7 +1,7 @@
1
1
  import { LocalSearchFilesParams } from '@lobechat/electron-client-ipc';
2
2
  import { memo } from 'react';
3
3
 
4
- import { LocalFileSearchState } from '@/tools/local-files/type';
4
+ import { LocalFileSearchState } from '@/tools/local-system/type';
5
5
  import { ChatMessagePluginError } from '@/types/message';
6
6
 
7
7
  import SearchResult from './Result';
@@ -1,7 +1,7 @@
1
1
  import { LocalFileItem } from '@lobechat/electron-client-ipc';
2
2
  import { memo } from 'react';
3
+ import { LocalSystemApiName } from 'src/tools/local-system';
3
4
 
4
- import { LocalFilesApiName } from '@/tools/local-files';
5
5
  import { BuiltinRenderProps } from '@/types/tool';
6
6
 
7
7
  import ListFiles from './ListFiles';
@@ -11,11 +11,11 @@ import SearchFiles from './SearchFiles';
11
11
  import WriteFile from './WriteFile';
12
12
 
13
13
  const RenderMap = {
14
- [LocalFilesApiName.searchLocalFiles]: SearchFiles,
15
- [LocalFilesApiName.listLocalFiles]: ListFiles,
16
- [LocalFilesApiName.readLocalFile]: ReadLocalFile,
17
- [LocalFilesApiName.renameLocalFile]: RenameLocalFile,
18
- [LocalFilesApiName.writeLocalFile]: WriteFile,
14
+ [LocalSystemApiName.searchLocalFiles]: SearchFiles,
15
+ [LocalSystemApiName.listLocalFiles]: ListFiles,
16
+ [LocalSystemApiName.readLocalFile]: ReadLocalFile,
17
+ [LocalSystemApiName.renameLocalFile]: RenameLocalFile,
18
+ [LocalSystemApiName.writeLocalFile]: WriteFile,
19
19
  };
20
20
 
21
21
  const LocalFilesRender = memo<BuiltinRenderProps<LocalFileItem[]>>(
@@ -2,7 +2,7 @@ import { BuiltinToolManifest } from '@/types/tool';
2
2
 
3
3
  import { systemPrompt } from './systemRole';
4
4
 
5
- export const LocalFilesApiName = {
5
+ export const LocalSystemApiName = {
6
6
  listLocalFiles: 'listLocalFiles',
7
7
  moveLocalFiles: 'moveLocalFiles',
8
8
  readLocalFile: 'readLocalFile',
@@ -11,12 +11,12 @@ export const LocalFilesApiName = {
11
11
  writeLocalFile: 'writeLocalFile',
12
12
  };
13
13
 
14
- export const LocalFilesManifest: BuiltinToolManifest = {
14
+ export const LocalSystemManifest: BuiltinToolManifest = {
15
15
  api: [
16
16
  {
17
17
  description:
18
18
  'List files and folders in a specified directory. Input should be a path. Output is a JSON array of file/folder names.',
19
- name: LocalFilesApiName.listLocalFiles,
19
+ name: LocalSystemApiName.listLocalFiles,
20
20
  parameters: {
21
21
  properties: {
22
22
  path: {
@@ -31,7 +31,7 @@ export const LocalFilesManifest: BuiltinToolManifest = {
31
31
  {
32
32
  description:
33
33
  'Read the content of a specific file. Input should be the file path. Output is the file content as a string.',
34
- name: LocalFilesApiName.readLocalFile,
34
+ name: LocalSystemApiName.readLocalFile,
35
35
  parameters: {
36
36
  properties: {
37
37
  loc: {
@@ -54,7 +54,7 @@ export const LocalFilesManifest: BuiltinToolManifest = {
54
54
  {
55
55
  description:
56
56
  'Search for files within the workspace based on a query string and optional filter options. Input should include the search query and any filter options. Output is a JSON array of matching file paths.',
57
- name: LocalFilesApiName.searchLocalFiles,
57
+ name: LocalSystemApiName.searchLocalFiles,
58
58
  parameters: {
59
59
  properties: {
60
60
  contentContains: {
@@ -130,7 +130,7 @@ export const LocalFilesManifest: BuiltinToolManifest = {
130
130
  {
131
131
  description:
132
132
  'Moves or renames multiple files/directories. Input is an array of objects, each containing an oldPath and a newPath.',
133
- name: LocalFilesApiName.moveLocalFiles,
133
+ name: LocalSystemApiName.moveLocalFiles,
134
134
  parameters: {
135
135
  properties: {
136
136
  items: {
@@ -160,7 +160,7 @@ export const LocalFilesManifest: BuiltinToolManifest = {
160
160
  {
161
161
  description:
162
162
  'Rename a file or folder in its current location. Input should be the current full path and the new name.',
163
- name: LocalFilesApiName.renameLocalFile,
163
+ name: LocalSystemApiName.renameLocalFile,
164
164
  parameters: {
165
165
  properties: {
166
166
  newName: {
@@ -179,7 +179,7 @@ export const LocalFilesManifest: BuiltinToolManifest = {
179
179
  {
180
180
  description:
181
181
  'Write content to a specific file. Input should be the file path and content. Overwrites existing file or creates a new one.',
182
- name: LocalFilesApiName.writeLocalFile,
182
+ name: LocalSystemApiName.writeLocalFile,
183
183
  parameters: {
184
184
  properties: {
185
185
  content: {
@@ -196,10 +196,10 @@ export const LocalFilesManifest: BuiltinToolManifest = {
196
196
  },
197
197
  },
198
198
  ],
199
- identifier: 'lobe-local-files',
199
+ identifier: 'lobe-local-system',
200
200
  meta: {
201
201
  avatar: '📁',
202
- title: 'Local Files',
202
+ title: 'Local System',
203
203
  },
204
204
  systemRole: systemPrompt,
205
205
  type: 'builtin',