@wonderwhy-er/desktop-commander 0.1.30 → 0.1.31
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/README.md +11 -2
- package/dist/server.js +78 -10
- package/dist/tools/edit.js +6 -3
- package/dist/tools/filesystem.d.ts +23 -2
- package/dist/tools/filesystem.js +91 -47
- package/dist/tools/mime-types.js +2 -8
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -108,6 +108,12 @@ The setup command will:
|
|
|
108
108
|
- Configure Claude's desktop app
|
|
109
109
|
- Add MCP servers to Claude's config if needed
|
|
110
110
|
|
|
111
|
+
### Updating Desktop Commander
|
|
112
|
+
|
|
113
|
+
When installed through npx (Option 1) or Smithery (Option 2), Desktop Commander will automatically update to the latest version whenever you restart Claude. No manual update process is needed.
|
|
114
|
+
|
|
115
|
+
For manual installations, you can update by running the setup command again.
|
|
116
|
+
|
|
111
117
|
## Usage
|
|
112
118
|
|
|
113
119
|
The server provides these tool categories:
|
|
@@ -122,7 +128,7 @@ The server provides these tool categories:
|
|
|
122
128
|
- `block_command`/`unblock_command`: Manage command blacklist
|
|
123
129
|
|
|
124
130
|
### Filesystem Tools
|
|
125
|
-
- `read_file`/`write_file`: File operations
|
|
131
|
+
- `read_file`/`write_file`: File operations (supports viewing PNG, JPEG, GIF, and WebP images directly in Claude)
|
|
126
132
|
- `create_directory`/`list_directory`: Directory management
|
|
127
133
|
- `move_file`: Move/rename files
|
|
128
134
|
- `search_files`: Pattern-based file search
|
|
@@ -252,7 +258,7 @@ Join our [Discord server](https://discord.gg/kQ27sNnZr7) to get help, share feed
|
|
|
252
258
|
https://www.youtube.com/watch?v=ly3bed99Dy8&lc=UgztdHvDMqTb9jiqnf54AaABAg](https://www.youtube.com/watch?v=ly3bed99Dy8&lc=UgztdHvDMqTb9jiqnf54AaABAg
|
|
253
259
|
)
|
|
254
260
|
|
|
255
|
-
[
|
|
257
263
|
https://www.youtube.com/watch?v=ly3bed99Dy8&lc=UgyQFTmYLJ4VBwIlmql4AaABAg](https://www.youtube.com/watch?v=ly3bed99Dy8&lc=UgyQFTmYLJ4VBwIlmql4AaABAg)
|
|
258
264
|
|
|
@@ -293,6 +299,9 @@ Unlike IDE-focused tools, Claude Desktop Commander provides a solution-centric a
|
|
|
293
299
|
### Do I need to pay for API credits?
|
|
294
300
|
No. This tool works with Claude Desktop's standard Pro subscription ($20/month), not with API calls, so you won't incur additional costs beyond the subscription fee.
|
|
295
301
|
|
|
302
|
+
### Does Desktop Commander automatically update?
|
|
303
|
+
Yes, when installed through npx or Smithery, Desktop Commander automatically updates to the latest version when you restart Claude. No manual update process is needed.
|
|
304
|
+
|
|
296
305
|
### What are the most common use cases?
|
|
297
306
|
- Exploring and understanding complex codebases
|
|
298
307
|
- Generating diagrams and documentation
|
package/dist/server.js
CHANGED
|
@@ -97,14 +97,17 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
97
97
|
{
|
|
98
98
|
name: "read_file",
|
|
99
99
|
description: "Read the complete contents of a file from the file system. " +
|
|
100
|
-
"
|
|
101
|
-
"
|
|
100
|
+
"Handles text files normally and image files are returned as viewable images. " +
|
|
101
|
+
"Recognized image types: PNG, JPEG, GIF, WebP. " +
|
|
102
|
+
"Only works within allowed directories.",
|
|
102
103
|
inputSchema: zodToJsonSchema(ReadFileArgsSchema),
|
|
103
104
|
},
|
|
104
105
|
{
|
|
105
106
|
name: "read_multiple_files",
|
|
106
107
|
description: "Read the contents of multiple files simultaneously. " +
|
|
107
108
|
"Each file's content is returned with its path as a reference. " +
|
|
109
|
+
"Handles text files normally and renders images as viewable content. " +
|
|
110
|
+
"Recognized image types: PNG, JPEG, GIF, WebP. " +
|
|
108
111
|
"Failed reads for individual files won't stop the entire operation. " +
|
|
109
112
|
"Only works within allowed directories.",
|
|
110
113
|
inputSchema: zodToJsonSchema(ReadMultipleFilesArgsSchema),
|
|
@@ -244,6 +247,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
244
247
|
}
|
|
245
248
|
catch (error) {
|
|
246
249
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
250
|
+
capture('server_' + name + "_error");
|
|
247
251
|
return {
|
|
248
252
|
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
249
253
|
};
|
|
@@ -253,12 +257,34 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
253
257
|
capture('server_read_file');
|
|
254
258
|
try {
|
|
255
259
|
const parsed = ReadFileArgsSchema.parse(args);
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
+
// Explicitly cast the result to FileResult since we're passing true
|
|
261
|
+
const fileResult = await readFile(parsed.path, true);
|
|
262
|
+
if (fileResult.isImage) {
|
|
263
|
+
// For image files, return as an image content type
|
|
264
|
+
return {
|
|
265
|
+
content: [
|
|
266
|
+
{
|
|
267
|
+
type: "text",
|
|
268
|
+
text: `Image file: ${parsed.path} (${fileResult.mimeType})\n`
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
type: "image",
|
|
272
|
+
data: fileResult.content,
|
|
273
|
+
mimeType: fileResult.mimeType
|
|
274
|
+
}
|
|
275
|
+
],
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
// For all other files, return as text
|
|
280
|
+
capture('server_' + "read_file_error");
|
|
281
|
+
return {
|
|
282
|
+
content: [{ type: "text", text: fileResult.content }],
|
|
283
|
+
};
|
|
284
|
+
}
|
|
260
285
|
}
|
|
261
286
|
catch (error) {
|
|
287
|
+
capture('server_' + name + "_error");
|
|
262
288
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
263
289
|
return {
|
|
264
290
|
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
@@ -269,12 +295,47 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
269
295
|
capture('server_read_multiple_files');
|
|
270
296
|
try {
|
|
271
297
|
const parsed = ReadMultipleFilesArgsSchema.parse(args);
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
298
|
+
const fileResults = await readMultipleFiles(parsed.paths);
|
|
299
|
+
// Create a text summary of all files
|
|
300
|
+
const textSummary = fileResults.map(result => {
|
|
301
|
+
if (result.error) {
|
|
302
|
+
return `${result.path}: Error - ${result.error}`;
|
|
303
|
+
}
|
|
304
|
+
else if (result.mimeType) {
|
|
305
|
+
return `${result.path}: ${result.mimeType} ${result.isImage ? '(image)' : '(text)'}`;
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
return `${result.path}: Unknown type`;
|
|
309
|
+
}
|
|
310
|
+
}).join("\n");
|
|
311
|
+
// Create content items for each file
|
|
312
|
+
const contentItems = [];
|
|
313
|
+
// Add the text summary
|
|
314
|
+
contentItems.push({ type: "text", text: textSummary });
|
|
315
|
+
// Add each file content
|
|
316
|
+
for (const result of fileResults) {
|
|
317
|
+
if (!result.error && result.content !== undefined) {
|
|
318
|
+
if (result.isImage && result.mimeType) {
|
|
319
|
+
// For image files, add an image content item
|
|
320
|
+
contentItems.push({
|
|
321
|
+
type: "image",
|
|
322
|
+
data: result.content,
|
|
323
|
+
mimeType: result.mimeType
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
// For text files, add a text summary
|
|
328
|
+
contentItems.push({
|
|
329
|
+
type: "text",
|
|
330
|
+
text: `\n--- ${result.path} contents: ---\n${result.content}`
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return { content: contentItems };
|
|
276
336
|
}
|
|
277
337
|
catch (error) {
|
|
338
|
+
capture('server_' + name + "_error");
|
|
278
339
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
279
340
|
return {
|
|
280
341
|
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
@@ -291,6 +352,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
291
352
|
};
|
|
292
353
|
}
|
|
293
354
|
catch (error) {
|
|
355
|
+
capture('server_' + name + "_error");
|
|
294
356
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
295
357
|
return {
|
|
296
358
|
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
@@ -307,6 +369,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
307
369
|
};
|
|
308
370
|
}
|
|
309
371
|
catch (error) {
|
|
372
|
+
capture('server_' + name + "_error");
|
|
310
373
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
311
374
|
return {
|
|
312
375
|
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
@@ -323,6 +386,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
323
386
|
};
|
|
324
387
|
}
|
|
325
388
|
catch (error) {
|
|
389
|
+
capture('server_' + name + "_error");
|
|
326
390
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
327
391
|
return {
|
|
328
392
|
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
@@ -339,6 +403,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
339
403
|
};
|
|
340
404
|
}
|
|
341
405
|
catch (error) {
|
|
406
|
+
capture('server_' + name + "_error");
|
|
342
407
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
343
408
|
return {
|
|
344
409
|
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
@@ -355,6 +420,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
355
420
|
};
|
|
356
421
|
}
|
|
357
422
|
catch (error) {
|
|
423
|
+
capture('server_' + name + "_error");
|
|
358
424
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
359
425
|
return {
|
|
360
426
|
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
@@ -382,6 +448,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
382
448
|
}
|
|
383
449
|
}
|
|
384
450
|
catch (error) {
|
|
451
|
+
capture('server_' + name + "_error");
|
|
385
452
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
386
453
|
return {
|
|
387
454
|
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
|
@@ -416,6 +483,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
416
483
|
};
|
|
417
484
|
}
|
|
418
485
|
catch (error) {
|
|
486
|
+
capture('server_' + name + "_error");
|
|
419
487
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
420
488
|
return {
|
|
421
489
|
content: [{ type: "text", text: `Error: ${errorMessage}` }],
|
package/dist/tools/edit.js
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { readFile, writeFile } from './filesystem.js';
|
|
2
2
|
export async function performSearchReplace(filePath, block) {
|
|
3
|
+
// Read file as plain string (don't pass true to get just the string)
|
|
3
4
|
const content = await readFile(filePath);
|
|
5
|
+
// Make sure content is a string
|
|
6
|
+
const contentStr = typeof content === 'string' ? content : content.content;
|
|
4
7
|
// Find first occurrence
|
|
5
|
-
const searchIndex =
|
|
8
|
+
const searchIndex = contentStr.indexOf(block.search);
|
|
6
9
|
if (searchIndex === -1) {
|
|
7
10
|
throw new Error(`Search content not found in ${filePath}`);
|
|
8
11
|
}
|
|
9
12
|
// Replace content
|
|
10
|
-
const newContent =
|
|
13
|
+
const newContent = contentStr.substring(0, searchIndex) +
|
|
11
14
|
block.replace +
|
|
12
|
-
|
|
15
|
+
contentStr.substring(searchIndex + block.search.length);
|
|
13
16
|
await writeFile(filePath, newContent);
|
|
14
17
|
}
|
|
15
18
|
export async function parseEditBlock(blockContent) {
|
|
@@ -1,7 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates a path to ensure it can be accessed or created.
|
|
3
|
+
* For existing paths, returns the real path (resolving symlinks).
|
|
4
|
+
* For non-existent paths, validates parent directories to ensure they exist.
|
|
5
|
+
*
|
|
6
|
+
* @param requestedPath The path to validate
|
|
7
|
+
* @returns Promise<string> The validated path
|
|
8
|
+
* @throws Error if the path or its parent directories don't exist
|
|
9
|
+
*/
|
|
1
10
|
export declare function validatePath(requestedPath: string): Promise<string>;
|
|
2
|
-
export
|
|
11
|
+
export interface FileResult {
|
|
12
|
+
content: string;
|
|
13
|
+
mimeType: string;
|
|
14
|
+
isImage: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function readFile(filePath: string, returnMetadata?: boolean): Promise<string | FileResult>;
|
|
3
17
|
export declare function writeFile(filePath: string, content: string): Promise<void>;
|
|
4
|
-
export
|
|
18
|
+
export interface MultiFileResult {
|
|
19
|
+
path: string;
|
|
20
|
+
content?: string;
|
|
21
|
+
mimeType?: string;
|
|
22
|
+
isImage?: boolean;
|
|
23
|
+
error?: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function readMultipleFiles(paths: string[]): Promise<MultiFileResult[]>;
|
|
5
26
|
export declare function createDirectory(dirPath: string): Promise<void>;
|
|
6
27
|
export declare function listDirectory(dirPath: string): Promise<string[]>;
|
|
7
28
|
export declare function moveFile(sourcePath: string, destinationPath: string): Promise<void>;
|
package/dist/tools/filesystem.js
CHANGED
|
@@ -23,10 +23,40 @@ function expandHome(filepath) {
|
|
|
23
23
|
}
|
|
24
24
|
return filepath;
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Recursively validates parent directories until it finds a valid one
|
|
28
|
+
* This function handles the case where we need to create nested directories
|
|
29
|
+
* and we need to check if any of the parent directories exist
|
|
30
|
+
*
|
|
31
|
+
* @param directoryPath The path to validate
|
|
32
|
+
* @returns Promise<boolean> True if a valid parent directory was found
|
|
33
|
+
*/
|
|
34
|
+
async function validateParentDirectories(directoryPath) {
|
|
35
|
+
const parentDir = path.dirname(directoryPath);
|
|
36
|
+
// Base case: we've reached the root or the same directory (shouldn't happen normally)
|
|
37
|
+
if (parentDir === directoryPath || parentDir === path.dirname(parentDir)) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
// Check if the parent directory exists
|
|
42
|
+
await fs.realpath(parentDir);
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Parent doesn't exist, recursively check its parent
|
|
47
|
+
return validateParentDirectories(parentDir);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Validates a path to ensure it can be accessed or created.
|
|
52
|
+
* For existing paths, returns the real path (resolving symlinks).
|
|
53
|
+
* For non-existent paths, validates parent directories to ensure they exist.
|
|
54
|
+
*
|
|
55
|
+
* @param requestedPath The path to validate
|
|
56
|
+
* @returns Promise<string> The validated path
|
|
57
|
+
* @throws Error if the path or its parent directories don't exist
|
|
58
|
+
*/
|
|
27
59
|
export async function validatePath(requestedPath) {
|
|
28
|
-
// Temporarily allow all paths by just returning the resolved path
|
|
29
|
-
// TODO: Implement configurable path validation
|
|
30
60
|
// Expand home directory if present
|
|
31
61
|
const expandedPath = expandHome(requestedPath);
|
|
32
62
|
// Convert to absolute path
|
|
@@ -40,53 +70,59 @@ export async function validatePath(requestedPath) {
|
|
|
40
70
|
return await fs.realpath(absolute);
|
|
41
71
|
}
|
|
42
72
|
catch (error) {
|
|
43
|
-
// Path doesn't exist
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
// Check if path is within allowed directories
|
|
55
|
-
const isAllowed = allowedDirectories.some(dir => normalizedRequested.startsWith(normalizePath(dir)));
|
|
56
|
-
if (!isAllowed) {
|
|
57
|
-
throw new Error(`Access denied - path outside allowed directories: ${absolute}`);
|
|
73
|
+
// Path doesn't exist - validate parent directories
|
|
74
|
+
if (await validateParentDirectories(absolute)) {
|
|
75
|
+
// Return the path if a valid parent exists
|
|
76
|
+
// This will be used for folder creation and many other file operations
|
|
77
|
+
return absolute;
|
|
78
|
+
}
|
|
79
|
+
// If no valid parent directory was found, still return the absolute path
|
|
80
|
+
// to maintain compatibility with upstream behavior, but log a warning
|
|
81
|
+
console.warn(`Warning: Parent directory does not exist: ${path.dirname(absolute)}`);
|
|
82
|
+
return absolute;
|
|
58
83
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
84
|
+
}
|
|
85
|
+
export async function readFile(filePath, returnMetadata) {
|
|
86
|
+
const validPath = await validatePath(filePath);
|
|
87
|
+
// Import the MIME type utilities
|
|
88
|
+
const { getMimeType, isImageFile } = await import('./mime-types.js');
|
|
89
|
+
// Detect the MIME type based on file extension
|
|
90
|
+
const mimeType = getMimeType(validPath);
|
|
91
|
+
const isImage = isImageFile(mimeType);
|
|
92
|
+
if (isImage) {
|
|
93
|
+
// For image files, read as Buffer and convert to base64
|
|
94
|
+
const buffer = await fs.readFile(validPath);
|
|
95
|
+
const content = buffer.toString('base64');
|
|
96
|
+
if (returnMetadata === true) {
|
|
97
|
+
return { content, mimeType, isImage };
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
return content;
|
|
67
101
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
// For
|
|
71
|
-
const parentDir = path.dirname(absolute);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// For all other files, try to read as UTF-8 text
|
|
72
105
|
try {
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
106
|
+
const content = await fs.readFile(validPath, "utf-8");
|
|
107
|
+
if (returnMetadata === true) {
|
|
108
|
+
return { content, mimeType, isImage };
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
return content;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
// If UTF-8 reading fails, treat as binary and return base64 but still as text
|
|
116
|
+
const buffer = await fs.readFile(validPath);
|
|
117
|
+
const content = `Binary file content (base64 encoded):\n${buffer.toString('base64')}`;
|
|
118
|
+
if (returnMetadata === true) {
|
|
119
|
+
return { content, mimeType: 'text/plain', isImage: false };
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
return content;
|
|
78
123
|
}
|
|
79
|
-
return absolute;
|
|
80
|
-
} catch {
|
|
81
|
-
throw new Error(`Parent directory does not exist: ${parentDir}`);
|
|
82
124
|
}
|
|
83
125
|
}
|
|
84
|
-
*/
|
|
85
|
-
}
|
|
86
|
-
// File operation tools
|
|
87
|
-
export async function readFile(filePath) {
|
|
88
|
-
const validPath = await validatePath(filePath);
|
|
89
|
-
return fs.readFile(validPath, "utf-8");
|
|
90
126
|
}
|
|
91
127
|
export async function writeFile(filePath, content) {
|
|
92
128
|
const validPath = await validatePath(filePath);
|
|
@@ -96,12 +132,20 @@ export async function readMultipleFiles(paths) {
|
|
|
96
132
|
return Promise.all(paths.map(async (filePath) => {
|
|
97
133
|
try {
|
|
98
134
|
const validPath = await validatePath(filePath);
|
|
99
|
-
const
|
|
100
|
-
return
|
|
135
|
+
const fileResult = await readFile(validPath, true);
|
|
136
|
+
return {
|
|
137
|
+
path: filePath,
|
|
138
|
+
content: typeof fileResult === 'string' ? fileResult : fileResult.content,
|
|
139
|
+
mimeType: typeof fileResult === 'string' ? "text/plain" : fileResult.mimeType,
|
|
140
|
+
isImage: typeof fileResult === 'string' ? false : fileResult.isImage
|
|
141
|
+
};
|
|
101
142
|
}
|
|
102
143
|
catch (error) {
|
|
103
144
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
104
|
-
return
|
|
145
|
+
return {
|
|
146
|
+
path: filePath,
|
|
147
|
+
error: errorMessage
|
|
148
|
+
};
|
|
105
149
|
}
|
|
106
150
|
}));
|
|
107
151
|
}
|
package/dist/tools/mime-types.js
CHANGED
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
// Simple MIME type detection based on file extension
|
|
2
2
|
export function getMimeType(filePath) {
|
|
3
3
|
const extension = filePath.toLowerCase().split('.').pop() || '';
|
|
4
|
-
// Image types
|
|
4
|
+
// Image types - only the formats we can display
|
|
5
5
|
const imageTypes = {
|
|
6
6
|
'png': 'image/png',
|
|
7
7
|
'jpg': 'image/jpeg',
|
|
8
8
|
'jpeg': 'image/jpeg',
|
|
9
9
|
'gif': 'image/gif',
|
|
10
|
-
'
|
|
11
|
-
'svg': 'image/svg+xml',
|
|
12
|
-
'webp': 'image/webp',
|
|
13
|
-
'ico': 'image/x-icon',
|
|
14
|
-
'tif': 'image/tiff',
|
|
15
|
-
'tiff': 'image/tiff',
|
|
10
|
+
'webp': 'image/webp'
|
|
16
11
|
};
|
|
17
|
-
// Text types - consider everything else as text for simplicity
|
|
18
12
|
// Check if the file is an image
|
|
19
13
|
if (extension in imageTypes) {
|
|
20
14
|
return imageTypes[extension];
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.
|
|
1
|
+
export declare const VERSION = "0.1.31";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '0.1.
|
|
1
|
+
export const VERSION = '0.1.31';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wonderwhy-er/desktop-commander",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.31",
|
|
4
4
|
"description": "MCP server for terminal operations and file editing",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Eduards Ruzga",
|
|
@@ -31,8 +31,7 @@
|
|
|
31
31
|
"setup": "npm install && npm run build && node setup-claude-server.js",
|
|
32
32
|
"setup:debug": "npm install && npm run build && node setup-claude-server.js --debug",
|
|
33
33
|
"prepare": "npm run build",
|
|
34
|
-
"test": "node test/
|
|
35
|
-
"test:watch": "nodemon test/test.js",
|
|
34
|
+
"test": "node test/run-all-tests.js",
|
|
36
35
|
"link:local": "npm run build && npm link",
|
|
37
36
|
"unlink:local": "npm unlink",
|
|
38
37
|
"inspector": "npx @modelcontextprotocol/inspector dist/index.js"
|
|
@@ -70,6 +69,7 @@
|
|
|
70
69
|
},
|
|
71
70
|
"devDependencies": {
|
|
72
71
|
"@types/node": "^20.17.24",
|
|
72
|
+
"nexe": "^5.0.0-beta.4",
|
|
73
73
|
"nodemon": "^3.0.2",
|
|
74
74
|
"shx": "^0.3.4",
|
|
75
75
|
"typescript": "^5.3.3"
|