@wonderwhy-er/desktop-commander 0.1.30 → 0.1.32
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 +31 -6
- package/dist/handlers/command-handlers.d.ts +27 -0
- package/dist/handlers/command-handlers.js +31 -0
- package/dist/handlers/edit-search-handlers.d.ts +18 -0
- package/dist/handlers/edit-search-handlers.js +72 -0
- package/dist/handlers/filesystem-handlers.d.ts +90 -0
- package/dist/handlers/filesystem-handlers.js +179 -0
- package/dist/handlers/index.d.ts +5 -0
- package/dist/handlers/index.js +6 -0
- package/dist/handlers/process-handlers.d.ts +18 -0
- package/dist/handlers/process-handlers.js +15 -0
- package/dist/handlers/terminal-handlers.d.ts +36 -0
- package/dist/handlers/terminal-handlers.js +29 -0
- package/dist/server.js +54 -259
- package/dist/setup-claude-server.js +1 -1
- package/dist/tools/edit.js +6 -3
- package/dist/tools/filesystem.d.ts +44 -2
- package/dist/tools/filesystem.js +215 -56
- package/dist/tools/mime-types.js +2 -8
- package/dist/tools/schemas.d.ts +9 -0
- package/dist/tools/schemas.js +3 -0
- package/dist/tools/search.js +9 -0
- package/dist/utils.d.ts +11 -0
- package/dist/utils.js +39 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -3
package/dist/tools/filesystem.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import fs from "fs/promises";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import os from 'os';
|
|
4
|
+
import fetch from 'cross-fetch';
|
|
5
|
+
import { withTimeout } from '../utils.js';
|
|
4
6
|
// Store allowed directories - temporarily allowing all paths
|
|
5
7
|
// TODO: Make this configurable through a configuration file
|
|
6
8
|
const allowedDirectories = [
|
|
@@ -23,70 +25,219 @@ function expandHome(filepath) {
|
|
|
23
25
|
}
|
|
24
26
|
return filepath;
|
|
25
27
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
//
|
|
28
|
+
/**
|
|
29
|
+
* Recursively validates parent directories until it finds a valid one
|
|
30
|
+
* This function handles the case where we need to create nested directories
|
|
31
|
+
* and we need to check if any of the parent directories exist
|
|
32
|
+
*
|
|
33
|
+
* @param directoryPath The path to validate
|
|
34
|
+
* @returns Promise<boolean> True if a valid parent directory was found
|
|
35
|
+
*/
|
|
36
|
+
async function validateParentDirectories(directoryPath) {
|
|
37
|
+
const parentDir = path.dirname(directoryPath);
|
|
38
|
+
// Base case: we've reached the root or the same directory (shouldn't happen normally)
|
|
39
|
+
if (parentDir === directoryPath || parentDir === path.dirname(parentDir)) {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
37
42
|
try {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return
|
|
43
|
+
// Check if the parent directory exists
|
|
44
|
+
await fs.realpath(parentDir);
|
|
45
|
+
return true;
|
|
41
46
|
}
|
|
42
|
-
catch
|
|
43
|
-
//
|
|
44
|
-
|
|
47
|
+
catch {
|
|
48
|
+
// Parent doesn't exist, recursively check its parent
|
|
49
|
+
return validateParentDirectories(parentDir);
|
|
45
50
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Validates a path to ensure it can be accessed or created.
|
|
54
|
+
* For existing paths, returns the real path (resolving symlinks).
|
|
55
|
+
* For non-existent paths, validates parent directories to ensure they exist.
|
|
56
|
+
*
|
|
57
|
+
* @param requestedPath The path to validate
|
|
58
|
+
* @returns Promise<string> The validated path
|
|
59
|
+
* @throws Error if the path or its parent directories don't exist
|
|
60
|
+
*/
|
|
61
|
+
export async function validatePath(requestedPath) {
|
|
62
|
+
const PATH_VALIDATION_TIMEOUT = 10000; // 10 seconds timeout
|
|
63
|
+
const validationOperation = async () => {
|
|
64
|
+
// Expand home directory if present
|
|
65
|
+
const expandedPath = expandHome(requestedPath);
|
|
66
|
+
// Convert to absolute path
|
|
67
|
+
const absolute = path.isAbsolute(expandedPath)
|
|
68
|
+
? path.resolve(expandedPath)
|
|
69
|
+
: path.resolve(process.cwd(), expandedPath);
|
|
70
|
+
// Check if path exists
|
|
71
|
+
try {
|
|
72
|
+
const stats = await fs.stat(absolute);
|
|
73
|
+
// If path exists, resolve any symlinks
|
|
74
|
+
return await fs.realpath(absolute);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
// Path doesn't exist - validate parent directories
|
|
78
|
+
if (await validateParentDirectories(absolute)) {
|
|
79
|
+
// Return the path if a valid parent exists
|
|
80
|
+
// This will be used for folder creation and many other file operations
|
|
81
|
+
return absolute;
|
|
82
|
+
}
|
|
83
|
+
// If no valid parent found, return the absolute path anyway
|
|
84
|
+
return absolute;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
// Execute with timeout
|
|
88
|
+
const result = await withTimeout(validationOperation(), PATH_VALIDATION_TIMEOUT, `Path validation for ${requestedPath}`, null);
|
|
89
|
+
if (result === null) {
|
|
90
|
+
throw new Error(`Path validation timed out after ${PATH_VALIDATION_TIMEOUT / 1000} seconds for: ${requestedPath}`);
|
|
58
91
|
}
|
|
59
|
-
|
|
60
|
-
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Read file content from a URL
|
|
96
|
+
* @param url URL to fetch content from
|
|
97
|
+
* @param returnMetadata Whether to return metadata with the content
|
|
98
|
+
* @returns File content or file result with metadata
|
|
99
|
+
*/
|
|
100
|
+
export async function readFileFromUrl(url, returnMetadata) {
|
|
101
|
+
// Import the MIME type utilities
|
|
102
|
+
const { isImageFile } = await import('./mime-types.js');
|
|
103
|
+
// Set up fetch with timeout
|
|
104
|
+
const FETCH_TIMEOUT_MS = 30000;
|
|
105
|
+
const controller = new AbortController();
|
|
106
|
+
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
61
107
|
try {
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
108
|
+
const response = await fetch(url, {
|
|
109
|
+
signal: controller.signal
|
|
110
|
+
});
|
|
111
|
+
// Clear the timeout since fetch completed
|
|
112
|
+
clearTimeout(timeoutId);
|
|
113
|
+
if (!response.ok) {
|
|
114
|
+
throw new Error(`HTTP error! Status: ${response.status}`);
|
|
67
115
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
throw new Error("Access denied - parent directory outside allowed directories");
|
|
116
|
+
// Get MIME type from Content-Type header
|
|
117
|
+
const contentType = response.headers.get('content-type') || 'text/plain';
|
|
118
|
+
const isImage = isImageFile(contentType);
|
|
119
|
+
if (isImage) {
|
|
120
|
+
// For images, convert to base64
|
|
121
|
+
const buffer = await response.arrayBuffer();
|
|
122
|
+
const content = Buffer.from(buffer).toString('base64');
|
|
123
|
+
if (returnMetadata === true) {
|
|
124
|
+
return { content, mimeType: contentType, isImage };
|
|
78
125
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
126
|
+
else {
|
|
127
|
+
return content;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
// For text content
|
|
132
|
+
const content = await response.text();
|
|
133
|
+
if (returnMetadata === true) {
|
|
134
|
+
return { content, mimeType: contentType, isImage };
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
return content;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
// Clear the timeout to prevent memory leaks
|
|
143
|
+
clearTimeout(timeoutId);
|
|
144
|
+
// Improve error message for timeout/abort cases
|
|
145
|
+
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
146
|
+
throw new Error(`URL fetch timed out after ${FETCH_TIMEOUT_MS}ms: ${url}`);
|
|
82
147
|
}
|
|
148
|
+
throw new Error(`Failed to fetch URL: ${error instanceof Error ? error.message : String(error)}`);
|
|
83
149
|
}
|
|
84
|
-
*/
|
|
85
150
|
}
|
|
86
|
-
|
|
87
|
-
|
|
151
|
+
/**
|
|
152
|
+
* Read file content from the local filesystem
|
|
153
|
+
* @param filePath Path to the file
|
|
154
|
+
* @param returnMetadata Whether to return metadata with the content
|
|
155
|
+
* @returns File content or file result with metadata
|
|
156
|
+
*/
|
|
157
|
+
export async function readFileFromDisk(filePath, returnMetadata) {
|
|
158
|
+
// Import the MIME type utilities
|
|
159
|
+
const { getMimeType, isImageFile } = await import('./mime-types.js');
|
|
88
160
|
const validPath = await validatePath(filePath);
|
|
89
|
-
|
|
161
|
+
// Check file size before attempting to read
|
|
162
|
+
try {
|
|
163
|
+
const stats = await fs.stat(validPath);
|
|
164
|
+
const MAX_SIZE = 100 * 1024; // 100KB limit
|
|
165
|
+
if (stats.size > MAX_SIZE) {
|
|
166
|
+
const message = `File too large (${(stats.size / 1024).toFixed(2)}KB > ${MAX_SIZE / 1024}KB limit)`;
|
|
167
|
+
if (returnMetadata) {
|
|
168
|
+
return {
|
|
169
|
+
content: message,
|
|
170
|
+
mimeType: 'text/plain',
|
|
171
|
+
isImage: false
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
return message;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
// If we can't stat the file, continue anyway and let the read operation handle errors
|
|
181
|
+
console.error(`Failed to stat file ${validPath}:`, error);
|
|
182
|
+
}
|
|
183
|
+
// Detect the MIME type based on file extension
|
|
184
|
+
const mimeType = getMimeType(validPath);
|
|
185
|
+
const isImage = isImageFile(mimeType);
|
|
186
|
+
const FILE_READ_TIMEOUT = 30000; // 30 seconds timeout for file operations
|
|
187
|
+
// Use withTimeout to handle potential hangs
|
|
188
|
+
const readOperation = async () => {
|
|
189
|
+
if (isImage) {
|
|
190
|
+
// For image files, read as Buffer and convert to base64
|
|
191
|
+
const buffer = await fs.readFile(validPath);
|
|
192
|
+
const content = buffer.toString('base64');
|
|
193
|
+
if (returnMetadata === true) {
|
|
194
|
+
return { content, mimeType, isImage };
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
return content;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
// For all other files, try to read as UTF-8 text
|
|
202
|
+
try {
|
|
203
|
+
const content = await fs.readFile(validPath, "utf-8");
|
|
204
|
+
if (returnMetadata === true) {
|
|
205
|
+
return { content, mimeType, isImage };
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
return content;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
// If UTF-8 reading fails, treat as binary and return base64 but still as text
|
|
213
|
+
const buffer = await fs.readFile(validPath);
|
|
214
|
+
const content = `Binary file content (base64 encoded):\n${buffer.toString('base64')}`;
|
|
215
|
+
if (returnMetadata === true) {
|
|
216
|
+
return { content, mimeType: 'text/plain', isImage: false };
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
return content;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
// Execute with timeout
|
|
225
|
+
const result = await withTimeout(readOperation(), FILE_READ_TIMEOUT, `Read file operation for ${filePath}`, returnMetadata ?
|
|
226
|
+
{ content: `Operation timed out after ${FILE_READ_TIMEOUT / 1000} seconds`, mimeType: 'text/plain', isImage: false } :
|
|
227
|
+
`Operation timed out after ${FILE_READ_TIMEOUT / 1000} seconds`);
|
|
228
|
+
return result;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Read a file from either the local filesystem or a URL
|
|
232
|
+
* @param filePath Path to the file or URL
|
|
233
|
+
* @param returnMetadata Whether to return metadata with the content
|
|
234
|
+
* @param isUrl Whether the path is a URL
|
|
235
|
+
* @returns File content or file result with metadata
|
|
236
|
+
*/
|
|
237
|
+
export async function readFile(filePath, returnMetadata, isUrl) {
|
|
238
|
+
return isUrl
|
|
239
|
+
? readFileFromUrl(filePath, returnMetadata)
|
|
240
|
+
: readFileFromDisk(filePath, returnMetadata);
|
|
90
241
|
}
|
|
91
242
|
export async function writeFile(filePath, content) {
|
|
92
243
|
const validPath = await validatePath(filePath);
|
|
@@ -96,12 +247,20 @@ export async function readMultipleFiles(paths) {
|
|
|
96
247
|
return Promise.all(paths.map(async (filePath) => {
|
|
97
248
|
try {
|
|
98
249
|
const validPath = await validatePath(filePath);
|
|
99
|
-
const
|
|
100
|
-
return
|
|
250
|
+
const fileResult = await readFile(validPath, true);
|
|
251
|
+
return {
|
|
252
|
+
path: filePath,
|
|
253
|
+
content: typeof fileResult === 'string' ? fileResult : fileResult.content,
|
|
254
|
+
mimeType: typeof fileResult === 'string' ? "text/plain" : fileResult.mimeType,
|
|
255
|
+
isImage: typeof fileResult === 'string' ? false : fileResult.isImage
|
|
256
|
+
};
|
|
101
257
|
}
|
|
102
258
|
catch (error) {
|
|
103
259
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
104
|
-
return
|
|
260
|
+
return {
|
|
261
|
+
path: filePath,
|
|
262
|
+
error: errorMessage
|
|
263
|
+
};
|
|
105
264
|
}
|
|
106
265
|
}));
|
|
107
266
|
}
|
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/tools/schemas.d.ts
CHANGED
|
@@ -47,10 +47,13 @@ export declare const UnblockCommandArgsSchema: z.ZodObject<{
|
|
|
47
47
|
}>;
|
|
48
48
|
export declare const ReadFileArgsSchema: z.ZodObject<{
|
|
49
49
|
path: z.ZodString;
|
|
50
|
+
isUrl: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
50
51
|
}, "strip", z.ZodTypeAny, {
|
|
51
52
|
path: string;
|
|
53
|
+
isUrl: boolean;
|
|
52
54
|
}, {
|
|
53
55
|
path: string;
|
|
56
|
+
isUrl?: boolean | undefined;
|
|
54
57
|
}>;
|
|
55
58
|
export declare const ReadMultipleFilesArgsSchema: z.ZodObject<{
|
|
56
59
|
paths: z.ZodArray<z.ZodString, "many">;
|
|
@@ -96,12 +99,15 @@ export declare const MoveFileArgsSchema: z.ZodObject<{
|
|
|
96
99
|
export declare const SearchFilesArgsSchema: z.ZodObject<{
|
|
97
100
|
path: z.ZodString;
|
|
98
101
|
pattern: z.ZodString;
|
|
102
|
+
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
99
103
|
}, "strip", z.ZodTypeAny, {
|
|
100
104
|
path: string;
|
|
101
105
|
pattern: string;
|
|
106
|
+
timeoutMs?: number | undefined;
|
|
102
107
|
}, {
|
|
103
108
|
path: string;
|
|
104
109
|
pattern: string;
|
|
110
|
+
timeoutMs?: number | undefined;
|
|
105
111
|
}>;
|
|
106
112
|
export declare const GetFileInfoArgsSchema: z.ZodObject<{
|
|
107
113
|
path: z.ZodString;
|
|
@@ -118,9 +124,11 @@ export declare const SearchCodeArgsSchema: z.ZodObject<{
|
|
|
118
124
|
maxResults: z.ZodOptional<z.ZodNumber>;
|
|
119
125
|
includeHidden: z.ZodOptional<z.ZodBoolean>;
|
|
120
126
|
contextLines: z.ZodOptional<z.ZodNumber>;
|
|
127
|
+
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
121
128
|
}, "strip", z.ZodTypeAny, {
|
|
122
129
|
path: string;
|
|
123
130
|
pattern: string;
|
|
131
|
+
timeoutMs?: number | undefined;
|
|
124
132
|
filePattern?: string | undefined;
|
|
125
133
|
ignoreCase?: boolean | undefined;
|
|
126
134
|
maxResults?: number | undefined;
|
|
@@ -129,6 +137,7 @@ export declare const SearchCodeArgsSchema: z.ZodObject<{
|
|
|
129
137
|
}, {
|
|
130
138
|
path: string;
|
|
131
139
|
pattern: string;
|
|
140
|
+
timeoutMs?: number | undefined;
|
|
132
141
|
filePattern?: string | undefined;
|
|
133
142
|
ignoreCase?: boolean | undefined;
|
|
134
143
|
maxResults?: number | undefined;
|
package/dist/tools/schemas.js
CHANGED
|
@@ -23,6 +23,7 @@ export const UnblockCommandArgsSchema = z.object({
|
|
|
23
23
|
// Filesystem tools schemas
|
|
24
24
|
export const ReadFileArgsSchema = z.object({
|
|
25
25
|
path: z.string(),
|
|
26
|
+
isUrl: z.boolean().optional().default(false),
|
|
26
27
|
});
|
|
27
28
|
export const ReadMultipleFilesArgsSchema = z.object({
|
|
28
29
|
paths: z.array(z.string()),
|
|
@@ -44,6 +45,7 @@ export const MoveFileArgsSchema = z.object({
|
|
|
44
45
|
export const SearchFilesArgsSchema = z.object({
|
|
45
46
|
path: z.string(),
|
|
46
47
|
pattern: z.string(),
|
|
48
|
+
timeoutMs: z.number().optional(),
|
|
47
49
|
});
|
|
48
50
|
export const GetFileInfoArgsSchema = z.object({
|
|
49
51
|
path: z.string(),
|
|
@@ -57,6 +59,7 @@ export const SearchCodeArgsSchema = z.object({
|
|
|
57
59
|
maxResults: z.number().optional(),
|
|
58
60
|
includeHidden: z.boolean().optional(),
|
|
59
61
|
contextLines: z.number().optional(),
|
|
62
|
+
timeoutMs: z.number().optional(),
|
|
60
63
|
});
|
|
61
64
|
// Edit tools schemas
|
|
62
65
|
export const EditBlockArgsSchema = z.object({
|
package/dist/tools/search.js
CHANGED
|
@@ -35,6 +35,11 @@ export async function searchCode(options) {
|
|
|
35
35
|
const results = [];
|
|
36
36
|
const rg = spawn(rgPath, args);
|
|
37
37
|
let stdoutBuffer = '';
|
|
38
|
+
// Store a reference to the child process for potential termination
|
|
39
|
+
const childProcess = rg;
|
|
40
|
+
// Store in a process list - this could be expanded to a global registry
|
|
41
|
+
// of running search processes if needed for management
|
|
42
|
+
globalThis.currentSearchProcess = childProcess;
|
|
38
43
|
rg.stdout.on('data', (data) => {
|
|
39
44
|
stdoutBuffer += data.toString();
|
|
40
45
|
});
|
|
@@ -42,6 +47,10 @@ export async function searchCode(options) {
|
|
|
42
47
|
console.error(`ripgrep error: ${data}`);
|
|
43
48
|
});
|
|
44
49
|
rg.on('close', (code) => {
|
|
50
|
+
// Clean up the global reference
|
|
51
|
+
if (globalThis.currentSearchProcess === childProcess) {
|
|
52
|
+
delete globalThis.currentSearchProcess;
|
|
53
|
+
}
|
|
45
54
|
if (code === 0 || code === 1) {
|
|
46
55
|
// Process the buffered output
|
|
47
56
|
const lines = stdoutBuffer.trim().split('\n');
|
package/dist/utils.d.ts
CHANGED
|
@@ -1 +1,12 @@
|
|
|
1
1
|
export declare const capture: (event: string, properties?: any) => void;
|
|
2
|
+
/**
|
|
3
|
+
* Executes a promise with a timeout. If the promise doesn't resolve or reject within
|
|
4
|
+
* the specified timeout, returns the provided default value.
|
|
5
|
+
*
|
|
6
|
+
* @param operation The promise to execute
|
|
7
|
+
* @param timeoutMs Timeout in milliseconds
|
|
8
|
+
* @param operationName Name of the operation (for logs)
|
|
9
|
+
* @param defaultValue Value to return if the operation times out
|
|
10
|
+
* @returns Promise that resolves with the operation result or the default value on timeout
|
|
11
|
+
*/
|
|
12
|
+
export declare function withTimeout<T>(operation: Promise<T>, timeoutMs: number, operationName: string, defaultValue: T): Promise<T>;
|
package/dist/utils.js
CHANGED
|
@@ -19,7 +19,7 @@ try {
|
|
|
19
19
|
// Access the default export from the module
|
|
20
20
|
uniqueUserId = machineIdModule.default.machineIdSync();
|
|
21
21
|
if (isTrackingEnabled) {
|
|
22
|
-
posthog = new PostHog('
|
|
22
|
+
posthog = new PostHog('phc_BW8KJ0cajzj2v8qfMhvDQ4dtFdgHPzeYcMRvRFGvQdH', {
|
|
23
23
|
host: 'https://eu.i.posthog.com',
|
|
24
24
|
flushAt: 3, // send all every time
|
|
25
25
|
flushInterval: 5 // send always
|
|
@@ -54,3 +54,41 @@ export const capture = (event, properties) => {
|
|
|
54
54
|
// Silently fail - we don't want analytics issues to break functionality
|
|
55
55
|
}
|
|
56
56
|
};
|
|
57
|
+
/**
|
|
58
|
+
* Executes a promise with a timeout. If the promise doesn't resolve or reject within
|
|
59
|
+
* the specified timeout, returns the provided default value.
|
|
60
|
+
*
|
|
61
|
+
* @param operation The promise to execute
|
|
62
|
+
* @param timeoutMs Timeout in milliseconds
|
|
63
|
+
* @param operationName Name of the operation (for logs)
|
|
64
|
+
* @param defaultValue Value to return if the operation times out
|
|
65
|
+
* @returns Promise that resolves with the operation result or the default value on timeout
|
|
66
|
+
*/
|
|
67
|
+
export function withTimeout(operation, timeoutMs, operationName, defaultValue) {
|
|
68
|
+
return new Promise((resolve) => {
|
|
69
|
+
let isCompleted = false;
|
|
70
|
+
// Set up timeout
|
|
71
|
+
const timeoutId = setTimeout(() => {
|
|
72
|
+
if (!isCompleted) {
|
|
73
|
+
isCompleted = true;
|
|
74
|
+
resolve(defaultValue);
|
|
75
|
+
}
|
|
76
|
+
}, timeoutMs);
|
|
77
|
+
// Execute the operation
|
|
78
|
+
operation
|
|
79
|
+
.then(result => {
|
|
80
|
+
if (!isCompleted) {
|
|
81
|
+
isCompleted = true;
|
|
82
|
+
clearTimeout(timeoutId);
|
|
83
|
+
resolve(result);
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
.catch(error => {
|
|
87
|
+
if (!isCompleted) {
|
|
88
|
+
isCompleted = true;
|
|
89
|
+
clearTimeout(timeoutId);
|
|
90
|
+
resolve(defaultValue);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.1.
|
|
1
|
+
export declare const VERSION = "0.1.32";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '0.1.
|
|
1
|
+
export const VERSION = '0.1.32';
|
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.32",
|
|
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"
|
|
@@ -62,6 +61,7 @@
|
|
|
62
61
|
"dependencies": {
|
|
63
62
|
"@modelcontextprotocol/sdk": "^1.8.0",
|
|
64
63
|
"@vscode/ripgrep": "^1.15.9",
|
|
64
|
+
"cross-fetch": "^4.1.0",
|
|
65
65
|
"glob": "^10.3.10",
|
|
66
66
|
"node-machine-id": "^1.1.12",
|
|
67
67
|
"posthog-node": "^4.11.1",
|
|
@@ -70,6 +70,7 @@
|
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/node": "^20.17.24",
|
|
73
|
+
"nexe": "^5.0.0-beta.4",
|
|
73
74
|
"nodemon": "^3.0.2",
|
|
74
75
|
"shx": "^0.3.4",
|
|
75
76
|
"typescript": "^5.3.3"
|