@lobehub/lobehub 2.0.0-next.303 → 2.0.0-next.304
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/CHANGELOG.md +25 -0
- package/apps/desktop/src/common/routes.ts +8 -8
- package/apps/desktop/src/main/const/dir.ts +2 -2
- package/apps/desktop/src/main/const/env.ts +4 -4
- package/apps/desktop/src/main/const/store.ts +3 -3
- package/apps/desktop/src/main/controllers/AuthCtr.ts +1 -1
- package/apps/desktop/src/main/controllers/McpInstallCtr.ts +8 -8
- package/apps/desktop/src/main/controllers/NetworkProxyCtr.ts +9 -9
- package/apps/desktop/src/main/controllers/RemoteServerSyncCtr.ts +8 -8
- package/apps/desktop/src/main/core/App.ts +9 -9
- package/apps/desktop/src/main/core/infrastructure/StaticFileServerManager.ts +2 -2
- package/apps/desktop/src/main/core/ui/ShortcutManager.ts +10 -10
- package/apps/desktop/src/main/core/ui/TrayManager.ts +12 -12
- package/apps/desktop/src/main/locales/resources.ts +4 -4
- package/apps/desktop/src/main/menus/impls/macOS.ts +1 -1
- package/apps/desktop/src/main/menus/types.ts +5 -5
- package/apps/desktop/src/main/modules/updater/configs.ts +10 -10
- package/apps/desktop/src/main/modules/updater/utils.ts +9 -9
- package/apps/desktop/src/main/services/fileSrv.ts +62 -62
- package/apps/desktop/src/main/shortcuts/config.ts +3 -3
- package/apps/desktop/src/main/types/protocol.ts +12 -12
- package/apps/desktop/src/main/utils/file-system.ts +2 -2
- package/apps/desktop/src/main/utils/logger.ts +5 -5
- package/apps/desktop/src/main/utils/protocol.ts +32 -32
- package/changelog/v1.json +9 -0
- package/locales/en-US/plugin.json +1 -0
- package/locales/zh-CN/discover.json +4 -4
- package/locales/zh-CN/plugin.json +1 -0
- package/package.json +1 -1
- package/packages/builtin-tool-group-management/src/client/Inspector/ExecuteAgentTask/index.tsx +78 -0
- package/packages/builtin-tool-group-management/src/client/Inspector/{ExecuteTasks → ExecuteAgentTasks}/index.tsx +1 -5
- package/packages/builtin-tool-group-management/src/client/Inspector/index.ts +4 -2
- package/packages/database/src/schemas/relations.ts +4 -4
- package/src/features/Conversation/ChatList/components/AutoScroll.tsx +3 -9
- package/src/features/Conversation/ChatList/components/VirtualizedList.tsx +2 -6
- package/src/locales/default/plugin.ts +1 -0
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
import { isDev } from '@/const/env';
|
|
2
2
|
import { getDesktopEnv } from '@/env';
|
|
3
3
|
|
|
4
|
-
//
|
|
4
|
+
// Update channel (stable, beta, alpha, etc.)
|
|
5
5
|
export const UPDATE_CHANNEL = getDesktopEnv().UPDATE_CHANNEL || 'stable';
|
|
6
6
|
|
|
7
|
-
//
|
|
7
|
+
// Determine if stable channel
|
|
8
8
|
export const isStableChannel = UPDATE_CHANNEL === 'stable' || !UPDATE_CHANNEL;
|
|
9
9
|
|
|
10
|
-
//
|
|
10
|
+
// Custom update server URL (for stable channel)
|
|
11
11
|
// e.g., https://releases.lobehub.com/stable
|
|
12
12
|
export const UPDATE_SERVER_URL = getDesktopEnv().UPDATE_SERVER_URL;
|
|
13
13
|
|
|
14
|
-
// GitHub
|
|
14
|
+
// GitHub configuration (for beta/nightly channels, or as fallback)
|
|
15
15
|
export const githubConfig = {
|
|
16
16
|
owner: 'lobehub',
|
|
17
17
|
repo: 'lobe-chat',
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
export const updaterConfig = {
|
|
21
|
-
//
|
|
21
|
+
// 应用Update configuration
|
|
22
22
|
app: {
|
|
23
|
-
//
|
|
23
|
+
// Whether to auto-check for updates
|
|
24
24
|
autoCheckUpdate: true,
|
|
25
|
-
//
|
|
25
|
+
// Whether to auto-download updates
|
|
26
26
|
autoDownloadUpdate: true,
|
|
27
|
-
//
|
|
28
|
-
checkUpdateInterval: 60 * 60 * 1000, // 1
|
|
27
|
+
// Update check interval (milliseconds)
|
|
28
|
+
checkUpdateInterval: 60 * 60 * 1000, // 1 hour
|
|
29
29
|
},
|
|
30
30
|
|
|
31
|
-
//
|
|
31
|
+
// Whether to enable application updates
|
|
32
32
|
enableAppUpdate: !isDev,
|
|
33
33
|
|
|
34
34
|
// 是否启用渲染层热更新
|
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
import semver from 'semver';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
* @param currentVersion
|
|
6
|
-
* @param nextVersion
|
|
7
|
-
* @returns
|
|
4
|
+
* Determine if application update is needed rather than just renderer update
|
|
5
|
+
* @param currentVersion Current version
|
|
6
|
+
* @param nextVersion New version
|
|
7
|
+
* @returns Whether application update is needed
|
|
8
8
|
*/
|
|
9
9
|
export const shouldUpdateApp = (currentVersion: string, nextVersion: string): boolean => {
|
|
10
|
-
//
|
|
10
|
+
// If version contains .app suffix, force application update
|
|
11
11
|
if (nextVersion.includes('.app')) {
|
|
12
12
|
return true;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
try {
|
|
16
|
-
//
|
|
16
|
+
// Parse version number
|
|
17
17
|
const current = semver.parse(currentVersion);
|
|
18
18
|
const next = semver.parse(nextVersion);
|
|
19
19
|
|
|
20
20
|
if (!current || !next) return true;
|
|
21
21
|
|
|
22
|
-
//
|
|
22
|
+
// Application update needed when major or minor version changes
|
|
23
23
|
if (current.major !== next.major || current.minor !== next.minor) {
|
|
24
24
|
return true;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
//
|
|
27
|
+
// For patch version changes only, prioritize renderer hot update
|
|
28
28
|
return false;
|
|
29
29
|
} catch {
|
|
30
|
-
//
|
|
30
|
+
// Default to application update when parsing fails
|
|
31
31
|
return true;
|
|
32
32
|
}
|
|
33
33
|
};
|
|
@@ -11,7 +11,7 @@ import { createLogger } from '@/utils/logger';
|
|
|
11
11
|
import { ServiceModule } from './index';
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
14
|
+
* File not found error class
|
|
15
15
|
*/
|
|
16
16
|
export class FileNotFoundError extends Error {
|
|
17
17
|
constructor(
|
|
@@ -46,8 +46,8 @@ export interface FileMetadata {
|
|
|
46
46
|
|
|
47
47
|
export default class FileService extends ServiceModule {
|
|
48
48
|
/**
|
|
49
|
-
*
|
|
50
|
-
* @deprecated
|
|
49
|
+
* Get legacy upload directory path
|
|
50
|
+
* @deprecated Only for backward compatibility with legacy file access, new files should be stored under custom paths in FILE_STORAGE_DIR
|
|
51
51
|
*/
|
|
52
52
|
get UPLOADS_DIR() {
|
|
53
53
|
return join(this.app.appStoragePath, FILE_STORAGE_DIR, 'uploads');
|
|
@@ -58,7 +58,7 @@ export default class FileService extends ServiceModule {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
|
-
*
|
|
61
|
+
* Upload file to local storage
|
|
62
62
|
*/
|
|
63
63
|
async uploadFile({
|
|
64
64
|
content,
|
|
@@ -69,15 +69,15 @@ export default class FileService extends ServiceModule {
|
|
|
69
69
|
}: UploadFileParams): Promise<{ metadata: FileMetadata; success: boolean }> {
|
|
70
70
|
logger.info(`Starting to upload file: ${filename}, hash: ${hash}, path: ${filePath}`);
|
|
71
71
|
try {
|
|
72
|
-
//
|
|
72
|
+
// Get current timestamp, avoid repeated Date.now() calls
|
|
73
73
|
const now = Date.now();
|
|
74
74
|
const date = (now / 1000 / 60 / 60).toFixed(0);
|
|
75
75
|
|
|
76
|
-
//
|
|
76
|
+
// Use provided filePath as the file storage path
|
|
77
77
|
const fullStoragePath = join(this.app.appStoragePath, FILE_STORAGE_DIR, filePath);
|
|
78
78
|
logger.debug(`Target file storage path: ${fullStoragePath}`);
|
|
79
79
|
|
|
80
|
-
//
|
|
80
|
+
// Ensure target directory exists
|
|
81
81
|
const targetDir = path.dirname(fullStoragePath);
|
|
82
82
|
logger.debug(`Ensuring target directory exists: ${targetDir}`);
|
|
83
83
|
makeSureDirExist(targetDir);
|
|
@@ -85,23 +85,23 @@ export default class FileService extends ServiceModule {
|
|
|
85
85
|
const savedPath = fullStoragePath;
|
|
86
86
|
logger.debug(`Final file save path: ${savedPath}`);
|
|
87
87
|
|
|
88
|
-
//
|
|
88
|
+
// Create Buffer based on content type
|
|
89
89
|
let buffer: Buffer;
|
|
90
90
|
if (typeof content === 'string') {
|
|
91
|
-
//
|
|
91
|
+
// Base64 string from server
|
|
92
92
|
buffer = Buffer.from(content, 'base64');
|
|
93
93
|
logger.debug(`Creating buffer from Base64 string, size: ${buffer.length} bytes`);
|
|
94
94
|
} else {
|
|
95
|
-
//
|
|
95
|
+
// ArrayBuffer from browser
|
|
96
96
|
buffer = Buffer.from(content);
|
|
97
97
|
logger.debug(`Creating buffer from ArrayBuffer, size: ${buffer.length} bytes`);
|
|
98
98
|
}
|
|
99
99
|
await writeFile(savedPath, buffer);
|
|
100
100
|
|
|
101
|
-
//
|
|
101
|
+
// Write metadata file
|
|
102
102
|
const metaFilePath = `${savedPath}.meta`;
|
|
103
103
|
const metadata = {
|
|
104
|
-
createdAt: now, //
|
|
104
|
+
createdAt: now, // Use unified timestamp
|
|
105
105
|
filename,
|
|
106
106
|
hash,
|
|
107
107
|
size: buffer.length,
|
|
@@ -110,18 +110,18 @@ export default class FileService extends ServiceModule {
|
|
|
110
110
|
logger.debug(`Writing metadata file: ${metaFilePath}`);
|
|
111
111
|
await writeFile(metaFilePath, JSON.stringify(metadata, null, 2));
|
|
112
112
|
|
|
113
|
-
//
|
|
113
|
+
// Return S3-compatible metadata format
|
|
114
114
|
const desktopPath = `desktop://${filePath}`;
|
|
115
115
|
logger.info(`File upload successful: ${desktopPath}`);
|
|
116
116
|
|
|
117
|
-
//
|
|
117
|
+
// Extract filename and directory information from path
|
|
118
118
|
const parsedPath = path.parse(filePath);
|
|
119
119
|
const dirname = parsedPath.dir || '';
|
|
120
120
|
const savedFilename = parsedPath.base;
|
|
121
121
|
|
|
122
122
|
return {
|
|
123
123
|
metadata: {
|
|
124
|
-
date, //
|
|
124
|
+
date, // Keep timestamp format for compatibility and time tracking
|
|
125
125
|
dirname,
|
|
126
126
|
filename: savedFilename,
|
|
127
127
|
path: desktopPath,
|
|
@@ -135,25 +135,25 @@ export default class FileService extends ServiceModule {
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
/**
|
|
138
|
-
*
|
|
138
|
+
* Check if path is in legacy format (timestamp directory)
|
|
139
139
|
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
140
|
+
* Legacy path format: {timestamp}/{hash}.{ext} (e.g., 1234567890/abc123.png)
|
|
141
|
+
* New path format: arbitrary custom paths (e.g., user_uploads/images/photo.png, ai_generations/image.jpg)
|
|
142
142
|
*
|
|
143
|
-
* @param path -
|
|
144
|
-
* @returns true
|
|
143
|
+
* @param path - Relative path, without desktop:// prefix
|
|
144
|
+
* @returns true if legacy format, false if new format
|
|
145
145
|
*/
|
|
146
146
|
private isLegacyPath(path: string): boolean {
|
|
147
147
|
const parts = path.split('/');
|
|
148
148
|
if (parts.length < 2) return false;
|
|
149
149
|
|
|
150
|
-
//
|
|
151
|
-
//
|
|
150
|
+
// If the first part is purely numeric (timestamp), consider it legacy format
|
|
151
|
+
// Timestamp format: Unix timestamp accurate to the hour, typically 10 digits
|
|
152
152
|
return /^\d+$/.test(parts[0]);
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
/**
|
|
156
|
-
*
|
|
156
|
+
* Get file content
|
|
157
157
|
*/
|
|
158
158
|
async getFile(path: string): Promise<{ content: ArrayBuffer; mimeType: string }> {
|
|
159
159
|
logger.info(`Getting file content: ${path}`);
|
|
@@ -164,30 +164,30 @@ export default class FileService extends ServiceModule {
|
|
|
164
164
|
throw new Error(`Invalid desktop file path: ${path}`);
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
-
//
|
|
168
|
-
//
|
|
167
|
+
// Normalize path format
|
|
168
|
+
// Possible formats received: desktop:/12345/file.png or desktop://12345/file.png
|
|
169
169
|
const normalizedPath = path.replace(/^desktop:\/+/, 'desktop://');
|
|
170
170
|
logger.debug(`Normalized path: ${normalizedPath}`);
|
|
171
171
|
|
|
172
|
-
//
|
|
172
|
+
// Parse path
|
|
173
173
|
const relativePath = normalizedPath.replace('desktop://', '');
|
|
174
174
|
|
|
175
|
-
//
|
|
175
|
+
// Smart routing: decide which directory to read file from based on path format
|
|
176
176
|
let filePath: string;
|
|
177
177
|
let isLegacyAttempt = false;
|
|
178
178
|
|
|
179
179
|
if (this.isLegacyPath(relativePath)) {
|
|
180
|
-
//
|
|
180
|
+
// Legacy path: read from uploads directory (backward compatibility)
|
|
181
181
|
filePath = join(this.UPLOADS_DIR, relativePath);
|
|
182
182
|
isLegacyAttempt = true;
|
|
183
183
|
logger.debug(`Legacy path detected, reading from uploads directory: ${filePath}`);
|
|
184
184
|
} else {
|
|
185
|
-
//
|
|
185
|
+
// New path: read from FILE_STORAGE_DIR root directory
|
|
186
186
|
filePath = join(this.app.appStoragePath, FILE_STORAGE_DIR, relativePath);
|
|
187
187
|
logger.debug(`New path format, reading from storage root: ${filePath}`);
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
//
|
|
190
|
+
// Read file content, if first attempt fails and is legacy path, try new path
|
|
191
191
|
logger.debug(`Starting to read file content`);
|
|
192
192
|
let content: Buffer;
|
|
193
193
|
try {
|
|
@@ -195,14 +195,14 @@ export default class FileService extends ServiceModule {
|
|
|
195
195
|
logger.debug(`File content read complete, size: ${content.length} bytes`);
|
|
196
196
|
} catch (firstError) {
|
|
197
197
|
if (isLegacyAttempt) {
|
|
198
|
-
//
|
|
198
|
+
// If legacy path read fails, try reading from new path
|
|
199
199
|
const fallbackPath = join(this.app.appStoragePath, FILE_STORAGE_DIR, relativePath);
|
|
200
200
|
logger.debug(
|
|
201
201
|
`Legacy path read failed, attempting fallback to storage root: ${fallbackPath}`,
|
|
202
202
|
);
|
|
203
203
|
try {
|
|
204
204
|
content = await readFilePromise(fallbackPath);
|
|
205
|
-
filePath = fallbackPath; //
|
|
205
|
+
filePath = fallbackPath; // Update filePath for subsequent metadata reading
|
|
206
206
|
logger.debug(`Fallback read successful, size: ${content.length} bytes`);
|
|
207
207
|
} catch (fallbackError) {
|
|
208
208
|
logger.error(
|
|
@@ -215,9 +215,9 @@ export default class FileService extends ServiceModule {
|
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
//
|
|
218
|
+
// Read metadata to get MIME type
|
|
219
219
|
const metaFilePath = `${filePath}.meta`;
|
|
220
|
-
let mimeType = 'application/octet-stream'; //
|
|
220
|
+
let mimeType = 'application/octet-stream'; // Default MIME type
|
|
221
221
|
logger.debug(`Attempting to read metadata file: ${metaFilePath}`);
|
|
222
222
|
|
|
223
223
|
try {
|
|
@@ -229,7 +229,7 @@ export default class FileService extends ServiceModule {
|
|
|
229
229
|
logger.warn(
|
|
230
230
|
`Failed to read metadata file: ${(metaError as Error).message}, using default MIME type`,
|
|
231
231
|
);
|
|
232
|
-
//
|
|
232
|
+
// If metadata file doesn't exist, try to guess MIME type from file extension
|
|
233
233
|
const ext = path.split('.').pop()?.toLowerCase();
|
|
234
234
|
if (ext) {
|
|
235
235
|
if (['jpg', 'jpeg'].includes(ext)) mimeType = 'image/jpeg';
|
|
@@ -271,7 +271,7 @@ export default class FileService extends ServiceModule {
|
|
|
271
271
|
} catch (error) {
|
|
272
272
|
logger.error(`File retrieval failed:`, error);
|
|
273
273
|
|
|
274
|
-
//
|
|
274
|
+
// If file not found error, throw custom FileNotFoundError
|
|
275
275
|
if (error instanceof Error && error.message.includes('ENOENT')) {
|
|
276
276
|
throw new FileNotFoundError(`File not found: ${path}`, path);
|
|
277
277
|
}
|
|
@@ -281,7 +281,7 @@ export default class FileService extends ServiceModule {
|
|
|
281
281
|
}
|
|
282
282
|
|
|
283
283
|
/**
|
|
284
|
-
*
|
|
284
|
+
* Delete file
|
|
285
285
|
*/
|
|
286
286
|
async deleteFile(path: string): Promise<{ success: boolean }> {
|
|
287
287
|
logger.info(`Deleting file: ${path}`);
|
|
@@ -292,42 +292,42 @@ export default class FileService extends ServiceModule {
|
|
|
292
292
|
throw new Error(`Invalid desktop file path: ${path}`);
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
-
//
|
|
295
|
+
// Normalize path format
|
|
296
296
|
const normalizedPath = path.replace(/^desktop:\/+/, 'desktop://');
|
|
297
297
|
|
|
298
|
-
//
|
|
298
|
+
// Parse path
|
|
299
299
|
const relativePath = normalizedPath.replace('desktop://', '');
|
|
300
300
|
|
|
301
|
-
//
|
|
301
|
+
// Smart routing: decide which directory to delete file from based on path format
|
|
302
302
|
let filePath: string;
|
|
303
303
|
let isLegacyAttempt = false;
|
|
304
304
|
|
|
305
305
|
if (this.isLegacyPath(relativePath)) {
|
|
306
|
-
//
|
|
306
|
+
// Legacy path: delete from uploads directory (backward compatibility)
|
|
307
307
|
filePath = join(this.UPLOADS_DIR, relativePath);
|
|
308
308
|
isLegacyAttempt = true;
|
|
309
309
|
logger.debug(`Legacy path detected, deleting from uploads directory: ${filePath}`);
|
|
310
310
|
} else {
|
|
311
|
-
//
|
|
311
|
+
// New path: delete from FILE_STORAGE_DIR root directory
|
|
312
312
|
filePath = join(this.app.appStoragePath, FILE_STORAGE_DIR, relativePath);
|
|
313
313
|
logger.debug(`New path format, deleting from storage root: ${filePath}`);
|
|
314
314
|
}
|
|
315
315
|
|
|
316
|
-
//
|
|
316
|
+
// Delete file and its metadata, if first attempt fails and is legacy path, try new path
|
|
317
317
|
logger.debug(`Starting file deletion`);
|
|
318
318
|
try {
|
|
319
319
|
await unlinkPromise(filePath);
|
|
320
320
|
logger.debug(`File deletion successful`);
|
|
321
321
|
} catch (firstError) {
|
|
322
322
|
if (isLegacyAttempt) {
|
|
323
|
-
//
|
|
323
|
+
// If legacy path deletion fails, try deleting from new path
|
|
324
324
|
const fallbackPath = join(this.app.appStoragePath, FILE_STORAGE_DIR, relativePath);
|
|
325
325
|
logger.debug(
|
|
326
326
|
`Legacy path deletion failed, attempting fallback to storage root: ${fallbackPath}`,
|
|
327
327
|
);
|
|
328
328
|
try {
|
|
329
329
|
await unlinkPromise(fallbackPath);
|
|
330
|
-
filePath = fallbackPath; //
|
|
330
|
+
filePath = fallbackPath; // Update filePath for subsequent metadata deletion
|
|
331
331
|
logger.debug(`Fallback deletion successful`);
|
|
332
332
|
} catch (fallbackError) {
|
|
333
333
|
logger.error(
|
|
@@ -340,7 +340,7 @@ export default class FileService extends ServiceModule {
|
|
|
340
340
|
}
|
|
341
341
|
}
|
|
342
342
|
|
|
343
|
-
//
|
|
343
|
+
// Try to delete metadata file, but don't require it to exist
|
|
344
344
|
try {
|
|
345
345
|
logger.debug(`Attempting to delete metadata file`);
|
|
346
346
|
await unlinkPromise(`${filePath}.meta`);
|
|
@@ -358,13 +358,13 @@ export default class FileService extends ServiceModule {
|
|
|
358
358
|
}
|
|
359
359
|
|
|
360
360
|
/**
|
|
361
|
-
*
|
|
361
|
+
* Batch delete files
|
|
362
362
|
*/
|
|
363
363
|
async deleteFiles(paths: string[]): Promise<DeleteFilesResponse> {
|
|
364
364
|
logger.info(`Batch deleting files, count: ${paths.length}`);
|
|
365
365
|
const errors: { message: string; path: string }[] = [];
|
|
366
366
|
|
|
367
|
-
//
|
|
367
|
+
// Process all deletion requests in parallel
|
|
368
368
|
logger.debug(`Starting parallel deletion requests`);
|
|
369
369
|
const results = await Promise.allSettled(
|
|
370
370
|
paths.map(async (path) => {
|
|
@@ -382,7 +382,7 @@ export default class FileService extends ServiceModule {
|
|
|
382
382
|
}),
|
|
383
383
|
);
|
|
384
384
|
|
|
385
|
-
//
|
|
385
|
+
// Process results
|
|
386
386
|
logger.debug(`Processing batch deletion results`);
|
|
387
387
|
results.forEach((result) => {
|
|
388
388
|
if (result.status === 'rejected') {
|
|
@@ -411,31 +411,31 @@ export default class FileService extends ServiceModule {
|
|
|
411
411
|
|
|
412
412
|
async getFilePath(path: string): Promise<string> {
|
|
413
413
|
logger.debug(`Getting filesystem path: ${path}`);
|
|
414
|
-
//
|
|
414
|
+
// Handle desktop:// paths
|
|
415
415
|
if (!path.startsWith('desktop://')) {
|
|
416
416
|
logger.error(`Invalid desktop file path: ${path}`);
|
|
417
417
|
throw new Error(`Invalid desktop file path: ${path}`);
|
|
418
418
|
}
|
|
419
419
|
|
|
420
|
-
//
|
|
420
|
+
// Normalize path format
|
|
421
421
|
const normalizedPath = path.replace(/^desktop:\/+/, 'desktop://');
|
|
422
422
|
|
|
423
|
-
//
|
|
423
|
+
// Parse path
|
|
424
424
|
const relativePath = normalizedPath.replace('desktop://', '');
|
|
425
425
|
|
|
426
|
-
//
|
|
426
|
+
// Smart routing: decide which directory to get file path from based on path format
|
|
427
427
|
let fullPath: string;
|
|
428
428
|
if (this.isLegacyPath(relativePath)) {
|
|
429
|
-
//
|
|
429
|
+
// Legacy path: get from uploads directory (backward compatibility)
|
|
430
430
|
fullPath = join(this.UPLOADS_DIR, relativePath);
|
|
431
431
|
logger.debug(`Legacy path detected, resolved to uploads directory: ${fullPath}`);
|
|
432
432
|
|
|
433
|
-
//
|
|
433
|
+
// Check if file exists, if not try new path
|
|
434
434
|
try {
|
|
435
435
|
await fs.promises.access(fullPath, fs.constants.F_OK);
|
|
436
436
|
logger.debug(`Legacy path file exists: ${fullPath}`);
|
|
437
437
|
} catch {
|
|
438
|
-
//
|
|
438
|
+
// If legacy path file doesn't exist, try new path
|
|
439
439
|
const fallbackPath = join(this.app.appStoragePath, FILE_STORAGE_DIR, relativePath);
|
|
440
440
|
logger.debug(`Legacy path file not found, trying fallback path: ${fallbackPath}`);
|
|
441
441
|
try {
|
|
@@ -443,14 +443,14 @@ export default class FileService extends ServiceModule {
|
|
|
443
443
|
fullPath = fallbackPath;
|
|
444
444
|
logger.debug(`Fallback path file exists: ${fullPath}`);
|
|
445
445
|
} catch {
|
|
446
|
-
//
|
|
446
|
+
// Neither path exists, return original legacy path (maintain existing behavior)
|
|
447
447
|
logger.debug(
|
|
448
448
|
`Neither legacy nor fallback path exists, returning legacy path: ${fullPath}`,
|
|
449
449
|
);
|
|
450
450
|
}
|
|
451
451
|
}
|
|
452
452
|
} else {
|
|
453
|
-
//
|
|
453
|
+
// New path: get from FILE_STORAGE_DIR root directory
|
|
454
454
|
fullPath = join(this.app.appStoragePath, FILE_STORAGE_DIR, relativePath);
|
|
455
455
|
logger.debug(`New path format, resolved to storage root: ${fullPath}`);
|
|
456
456
|
}
|
|
@@ -460,19 +460,19 @@ export default class FileService extends ServiceModule {
|
|
|
460
460
|
|
|
461
461
|
async getFileHTTPURL(path: string): Promise<string> {
|
|
462
462
|
logger.debug(`Getting file HTTP URL: ${path}`);
|
|
463
|
-
//
|
|
463
|
+
// Handle desktop:// paths
|
|
464
464
|
if (!path.startsWith('desktop://')) {
|
|
465
465
|
logger.error(`Invalid desktop file path: ${path}`);
|
|
466
466
|
throw new Error(`Invalid desktop file path: ${path}`);
|
|
467
467
|
}
|
|
468
468
|
|
|
469
|
-
//
|
|
469
|
+
// Normalize path format
|
|
470
470
|
const normalizedPath = path.replace(/^desktop:\/+/, 'desktop://');
|
|
471
471
|
|
|
472
|
-
//
|
|
472
|
+
// Parse path: extract path/to/file.png from desktop://path/to/file.png
|
|
473
473
|
const relativePath = normalizedPath.replace('desktop://', '');
|
|
474
474
|
|
|
475
|
-
//
|
|
475
|
+
// Use StaticFileServerManager to get file server domain, then construct full URL
|
|
476
476
|
const serverDomain = this.app.staticFileServerManager.getFileServerDomain();
|
|
477
477
|
const httpURL = `${serverDomain}${LOCAL_STORAGE_URL_PREFIX}/${relativePath}`;
|
|
478
478
|
logger.debug(`Generated HTTP URL: ${httpURL}`);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Shortcut action type enum
|
|
3
3
|
*/
|
|
4
4
|
export const ShortcutActionEnum = {
|
|
5
5
|
openSettings: 'openSettings',
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Show/hide main window
|
|
8
8
|
*/
|
|
9
9
|
showApp: 'showApp',
|
|
10
10
|
} as const;
|
|
@@ -12,7 +12,7 @@ export const ShortcutActionEnum = {
|
|
|
12
12
|
export type ShortcutActionType = (typeof ShortcutActionEnum)[keyof typeof ShortcutActionEnum];
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
15
|
+
* Default shortcut configuration
|
|
16
16
|
*/
|
|
17
17
|
export const DEFAULT_SHORTCUTS_CONFIG: Record<ShortcutActionType, string> = {
|
|
18
18
|
[ShortcutActionEnum.showApp]: 'Control+E',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MCP Schema - stdio
|
|
2
|
+
* MCP Schema - stdio configuration type
|
|
3
3
|
*/
|
|
4
4
|
export interface McpStdioConfig {
|
|
5
5
|
args?: string[];
|
|
@@ -9,7 +9,7 @@ export interface McpStdioConfig {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* MCP Schema - http
|
|
12
|
+
* MCP Schema - http configuration type
|
|
13
13
|
*/
|
|
14
14
|
export interface McpHttpConfig {
|
|
15
15
|
headers?: Record<string, string>;
|
|
@@ -18,26 +18,26 @@ export interface McpHttpConfig {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
* MCP Schema
|
|
21
|
+
* MCP Schema configuration type
|
|
22
22
|
*/
|
|
23
23
|
export type McpConfig = McpStdioConfig | McpHttpConfig;
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
* MCP Schema
|
|
27
|
-
*
|
|
26
|
+
* MCP Schema object
|
|
27
|
+
* Conforms to RFC 0001 definition
|
|
28
28
|
*/
|
|
29
29
|
export interface McpSchema {
|
|
30
|
-
/**
|
|
30
|
+
/** Plugin author */
|
|
31
31
|
author: string;
|
|
32
|
-
/**
|
|
32
|
+
/** Plugin configuration */
|
|
33
33
|
config: McpConfig;
|
|
34
|
-
/**
|
|
34
|
+
/** Plugin description */
|
|
35
35
|
description: string;
|
|
36
|
-
/**
|
|
36
|
+
/** Plugin homepage */
|
|
37
37
|
homepage?: string;
|
|
38
|
-
/**
|
|
38
|
+
/** Plugin icon */
|
|
39
39
|
icon?: string;
|
|
40
|
-
/**
|
|
40
|
+
/** Plugin unique identifier,必须与URL中的id参数匹配 */
|
|
41
41
|
identifier: string;
|
|
42
42
|
/** 插件名称 */
|
|
43
43
|
name: string;
|
|
@@ -49,7 +49,7 @@ export interface McpSchema {
|
|
|
49
49
|
* 协议URL解析结果
|
|
50
50
|
*/
|
|
51
51
|
export interface ProtocolUrlParsed {
|
|
52
|
-
/**
|
|
52
|
+
/** Action type (e.g., 'install') */
|
|
53
53
|
action: string;
|
|
54
54
|
/** 原始URL */
|
|
55
55
|
originalUrl: string;
|
|
@@ -4,11 +4,11 @@ export const makeSureDirExist = (dir: string) => {
|
|
|
4
4
|
try {
|
|
5
5
|
statSync(dir);
|
|
6
6
|
} catch {
|
|
7
|
-
//
|
|
7
|
+
// Use recursive: true, no effect if directory exists, create if it doesn't
|
|
8
8
|
try {
|
|
9
9
|
mkdirSync(dir, { recursive: true });
|
|
10
10
|
} catch (mkdirError: any) {
|
|
11
|
-
//
|
|
11
|
+
// Throw error if directory creation fails (e.g., permission issues)
|
|
12
12
|
throw new Error(`Could not create target directory: ${dir}. Error: ${mkdirError.message}`);
|
|
13
13
|
}
|
|
14
14
|
}
|
|
@@ -3,14 +3,14 @@ import electronLog from 'electron-log';
|
|
|
3
3
|
|
|
4
4
|
import { getDesktopEnv } from '@/env';
|
|
5
5
|
|
|
6
|
-
//
|
|
7
|
-
electronLog.transports.file.level = 'info'; //
|
|
6
|
+
// Configure electron-log
|
|
7
|
+
electronLog.transports.file.level = 'info'; // Log info level and above in production
|
|
8
8
|
electronLog.transports.console.level =
|
|
9
9
|
getDesktopEnv().NODE_ENV === 'development'
|
|
10
|
-
? 'debug' //
|
|
11
|
-
: 'warn'; //
|
|
10
|
+
? 'debug' // Show more logs in development
|
|
11
|
+
: 'warn'; // Only show warnings and errors in production
|
|
12
12
|
|
|
13
|
-
//
|
|
13
|
+
// Create namespaced debugger
|
|
14
14
|
export const createLogger = (namespace: string) => {
|
|
15
15
|
const debugLogger = debug(namespace);
|
|
16
16
|
|