@wonderwhy-er/desktop-commander 0.1.34 → 0.1.36

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 (63) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +186 -56
  3. package/dist/command-manager.d.ts +1 -7
  4. package/dist/command-manager.js +31 -50
  5. package/dist/config-manager.d.ts +28 -16
  6. package/dist/config-manager.js +124 -189
  7. package/dist/config.d.ts +2 -2
  8. package/dist/config.js +7 -4
  9. package/dist/error-handlers.js +4 -0
  10. package/dist/handlers/edit-search-handlers.d.ts +3 -1
  11. package/dist/handlers/edit-search-handlers.js +9 -19
  12. package/dist/handlers/filesystem-handlers.d.ts +0 -4
  13. package/dist/handlers/filesystem-handlers.js +11 -19
  14. package/dist/handlers/index.d.ts +0 -1
  15. package/dist/handlers/index.js +0 -1
  16. package/dist/index.js +19 -4
  17. package/dist/polyform-license-src/edit/edit.d.ts +15 -0
  18. package/dist/polyform-license-src/edit/edit.js +163 -0
  19. package/dist/polyform-license-src/edit/fuzzySearch.d.ts +30 -0
  20. package/dist/polyform-license-src/edit/fuzzySearch.js +121 -0
  21. package/dist/polyform-license-src/edit/handlers.d.ts +16 -0
  22. package/dist/polyform-license-src/edit/handlers.js +24 -0
  23. package/dist/polyform-license-src/edit/index.d.ts +12 -0
  24. package/dist/polyform-license-src/edit/index.js +13 -0
  25. package/dist/polyform-license-src/edit/schemas.d.ts +25 -0
  26. package/dist/polyform-license-src/edit/schemas.js +16 -0
  27. package/dist/polyform-license-src/index.d.ts +9 -0
  28. package/dist/polyform-license-src/index.js +10 -0
  29. package/dist/sandbox/index.d.ts +9 -0
  30. package/dist/sandbox/index.js +50 -0
  31. package/dist/sandbox/mac-sandbox.d.ts +19 -0
  32. package/dist/sandbox/mac-sandbox.js +174 -0
  33. package/dist/server.js +181 -176
  34. package/dist/setup-claude-server.js +554 -244
  35. package/dist/terminal-manager.d.ts +1 -1
  36. package/dist/terminal-manager.js +22 -3
  37. package/dist/tools/config.d.ts +0 -58
  38. package/dist/tools/config.js +44 -107
  39. package/dist/tools/debug-path.d.ts +1 -0
  40. package/dist/tools/debug-path.js +44 -0
  41. package/dist/tools/edit.d.ts +8 -6
  42. package/dist/tools/edit.js +165 -35
  43. package/dist/tools/execute.js +6 -6
  44. package/dist/tools/filesystem-fixed.d.ts +22 -0
  45. package/dist/tools/filesystem-fixed.js +176 -0
  46. package/dist/tools/filesystem.d.ts +4 -6
  47. package/dist/tools/filesystem.js +157 -87
  48. package/dist/tools/fuzzySearch.d.ts +22 -0
  49. package/dist/tools/fuzzySearch.js +113 -0
  50. package/dist/tools/pdf-reader.d.ts +13 -0
  51. package/dist/tools/pdf-reader.js +214 -0
  52. package/dist/tools/schemas.d.ts +29 -19
  53. package/dist/tools/schemas.js +15 -8
  54. package/dist/tools/search.js +5 -4
  55. package/dist/utils/capture.d.ts +15 -0
  56. package/dist/utils/capture.js +175 -0
  57. package/dist/utils/withTimeout.d.ts +11 -0
  58. package/dist/utils/withTimeout.js +52 -0
  59. package/dist/utils.d.ts +15 -1
  60. package/dist/utils.js +174 -41
  61. package/dist/version.d.ts +1 -1
  62. package/dist/version.js +1 -1
  63. package/package.json +2 -3
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Executes a promise with a timeout. If the promise doesn't resolve or reject within
3
+ * the specified timeout, returns the provided default value.
4
+ *
5
+ * @param operation The promise to execute
6
+ * @param timeoutMs Timeout in milliseconds
7
+ * @param operationName Name of the operation (for logs)
8
+ * @param defaultValue Value to return if the operation times out
9
+ * @returns Promise that resolves with the operation result or the default value on timeout
10
+ */
11
+ export declare function withTimeout<T>(operation: Promise<T>, timeoutMs: number, operationName: string, defaultValue: T): Promise<T>;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Executes a promise with a timeout. If the promise doesn't resolve or reject within
3
+ * the specified timeout, returns the provided default value.
4
+ *
5
+ * @param operation The promise to execute
6
+ * @param timeoutMs Timeout in milliseconds
7
+ * @param operationName Name of the operation (for logs)
8
+ * @param defaultValue Value to return if the operation times out
9
+ * @returns Promise that resolves with the operation result or the default value on timeout
10
+ */
11
+ export function withTimeout(operation, timeoutMs, operationName, defaultValue) {
12
+ // Don't sanitize operation name for logs - only telemetry will sanitize if needed
13
+ return new Promise((resolve, reject) => {
14
+ let isCompleted = false;
15
+ // Set up timeout
16
+ const timeoutId = setTimeout(() => {
17
+ if (!isCompleted) {
18
+ isCompleted = true;
19
+ if (defaultValue !== null) {
20
+ resolve(defaultValue);
21
+ }
22
+ else {
23
+ // Keep the original operation name in the error message
24
+ // Telemetry sanitization happens at the capture level
25
+ reject(`__ERROR__: ${operationName} timed out after ${timeoutMs / 1000} seconds`);
26
+ }
27
+ }
28
+ }, timeoutMs);
29
+ // Execute the operation
30
+ operation
31
+ .then(result => {
32
+ if (!isCompleted) {
33
+ isCompleted = true;
34
+ clearTimeout(timeoutId);
35
+ resolve(result);
36
+ }
37
+ })
38
+ .catch(error => {
39
+ if (!isCompleted) {
40
+ isCompleted = true;
41
+ clearTimeout(timeoutId);
42
+ if (defaultValue !== null) {
43
+ resolve(defaultValue);
44
+ }
45
+ else {
46
+ // Pass the original error unchanged - sanitization for telemetry happens in capture
47
+ reject(error);
48
+ }
49
+ }
50
+ });
51
+ });
52
+ }
package/dist/utils.d.ts CHANGED
@@ -1,4 +1,18 @@
1
- export declare const capture: (event: string, properties?: any) => void;
1
+ /**
2
+ * Sanitizes error objects to remove potentially sensitive information like file paths
3
+ * @param error Error object or string to sanitize
4
+ * @returns An object with sanitized message and optional error code
5
+ */
6
+ export declare function sanitizeError(error: any): {
7
+ message: string;
8
+ code?: string;
9
+ };
10
+ /**
11
+ * Send an event to Google Analytics
12
+ * @param event Event name
13
+ * @param properties Optional event properties
14
+ */
15
+ export declare const capture: (event: string, properties?: any) => Promise<void>;
2
16
  /**
3
17
  * Executes a promise with a timeout. If the promise doesn't resolve or reject within
4
18
  * the specified timeout, returns the provided default value.
package/dist/utils.js CHANGED
@@ -1,54 +1,173 @@
1
1
  import { platform } from 'os';
2
+ import { randomUUID } from 'crypto';
3
+ import * as https from 'https';
4
+ import { configManager } from './config-manager.js';
2
5
  let VERSION = 'unknown';
3
6
  try {
4
7
  const versionModule = await import('./version.js');
5
8
  VERSION = versionModule.VERSION;
6
9
  }
7
10
  catch {
11
+ // Continue without version info if not available
8
12
  }
9
- // Set default tracking state
10
- const isTrackingEnabled = true;
13
+ // Configuration
14
+ const GA_MEASUREMENT_ID = 'G-NGGDNL0K4L'; // Replace with your GA4 Measurement ID
15
+ const GA_API_SECRET = '5M0mC--2S_6t94m8WrI60A'; // Replace with your GA4 API Secret
16
+ const GA_BASE_URL = `https://www.google-analytics.com/mp/collect?measurement_id=${GA_MEASUREMENT_ID}&api_secret=${GA_API_SECRET}`;
17
+ const GA_DEBUG_BASE_URL = `https://www.google-analytics.com/debug/mp/collect?measurement_id=${GA_MEASUREMENT_ID}&api_secret=${GA_API_SECRET}`;
18
+ // Will be initialized when needed
11
19
  let uniqueUserId = 'unknown';
12
- let posthog = null;
13
- // Try to load PostHog without breaking if it's not available
14
- try {
15
- // Dynamic imports to prevent crashing if dependencies aren't available
16
- import('posthog-node').then((posthogModule) => {
17
- const PostHog = posthogModule.PostHog;
18
- import('node-machine-id').then((machineIdModule) => {
19
- // Access the default export from the module
20
- uniqueUserId = machineIdModule.default.machineIdSync();
21
- if (isTrackingEnabled) {
22
- posthog = new PostHog('phc_BW8KJ0cajzj2v8qfMhvDQ4dtFdgHPzeYcMRvRFGvQdH', {
23
- host: 'https://eu.i.posthog.com',
24
- flushAt: 3, // send all every time
25
- flushInterval: 5 // send always
26
- });
27
- }
28
- }).catch(() => {
29
- // Silently fail - we don't want analytics issues to break functionality
30
- });
31
- }).catch(() => {
32
- // Silently fail - we don't want analytics issues to break functionality
33
- });
34
- }
35
- catch {
36
- //console.log('Analytics module not available - continuing without tracking');
20
+ // Function to get or create a persistent UUID
21
+ async function getOrCreateUUID() {
22
+ try {
23
+ // Try to get the UUID from the config
24
+ let clientId = await configManager.getValue('clientId');
25
+ // If it doesn't exist, create a new one and save it
26
+ if (!clientId) {
27
+ clientId = randomUUID();
28
+ await configManager.setValue('clientId', clientId);
29
+ }
30
+ return clientId;
31
+ }
32
+ catch (error) {
33
+ // Fallback to a random UUID if config operations fail
34
+ return randomUUID();
35
+ }
37
36
  }
38
- export const capture = (event, properties) => {
39
- if (!posthog || !isTrackingEnabled) {
40
- return;
37
+ /**
38
+ * Sanitizes error objects to remove potentially sensitive information like file paths
39
+ * @param error Error object or string to sanitize
40
+ * @returns An object with sanitized message and optional error code
41
+ */
42
+ export function sanitizeError(error) {
43
+ let errorMessage = '';
44
+ let errorCode = undefined;
45
+ if (error instanceof Error) {
46
+ // Extract just the error name and message without stack trace
47
+ errorMessage = error.name + ': ' + error.message;
48
+ // Extract error code if available (common in Node.js errors)
49
+ if ('code' in error) {
50
+ errorCode = error.code;
51
+ }
41
52
  }
53
+ else if (typeof error === 'string') {
54
+ errorMessage = error;
55
+ }
56
+ else {
57
+ errorMessage = 'Unknown error';
58
+ }
59
+ // Remove any file paths using regex
60
+ // This pattern matches common path formats including Windows and Unix-style paths
61
+ errorMessage = errorMessage.replace(/(?:\/|\\)[\w\d_.-\/\\]+/g, '[PATH]');
62
+ errorMessage = errorMessage.replace(/[A-Za-z]:\\[\w\d_.-\/\\]+/g, '[PATH]');
63
+ return {
64
+ message: errorMessage,
65
+ code: errorCode
66
+ };
67
+ }
68
+ /**
69
+ * Send an event to Google Analytics
70
+ * @param event Event name
71
+ * @param properties Optional event properties
72
+ */
73
+ export const capture = async (event, properties) => {
42
74
  try {
43
- properties = properties || {};
44
- properties.timestamp = new Date().toISOString();
45
- properties.platform = platform();
46
- properties.DCVersion = VERSION;
47
- posthog.capture({
48
- distinctId: uniqueUserId,
49
- event,
50
- properties
75
+ // Check if telemetry is enabled in config (defaults to true if not set)
76
+ const telemetryEnabled = await configManager.getValue('telemetryEnabled');
77
+ // If telemetry is explicitly disabled or GA credentials are missing, don't send
78
+ if (telemetryEnabled === false || !GA_MEASUREMENT_ID || !GA_API_SECRET) {
79
+ return;
80
+ }
81
+ // Get or create the client ID if not already initialized
82
+ if (uniqueUserId === 'unknown') {
83
+ uniqueUserId = await getOrCreateUUID();
84
+ }
85
+ // Create a deep copy of properties to avoid modifying the original objects
86
+ // This ensures we don't alter error objects that are also returned to the AI
87
+ let sanitizedProperties;
88
+ try {
89
+ sanitizedProperties = properties ? JSON.parse(JSON.stringify(properties)) : {};
90
+ }
91
+ catch (e) {
92
+ sanitizedProperties = {};
93
+ }
94
+ // Sanitize error objects if present
95
+ if (sanitizedProperties.error) {
96
+ // Handle different types of error objects
97
+ if (typeof sanitizedProperties.error === 'object' && sanitizedProperties.error !== null) {
98
+ const sanitized = sanitizeError(sanitizedProperties.error);
99
+ sanitizedProperties.error = sanitized.message;
100
+ if (sanitized.code) {
101
+ sanitizedProperties.errorCode = sanitized.code;
102
+ }
103
+ }
104
+ else if (typeof sanitizedProperties.error === 'string') {
105
+ sanitizedProperties.error = sanitizeError(sanitizedProperties.error).message;
106
+ }
107
+ }
108
+ // Remove any properties that might contain paths
109
+ const sensitiveKeys = ['path', 'filePath', 'directory', 'file_path', 'sourcePath', 'destinationPath', 'fullPath', 'rootPath'];
110
+ for (const key of Object.keys(sanitizedProperties)) {
111
+ const lowerKey = key.toLowerCase();
112
+ if (sensitiveKeys.some(sensitiveKey => lowerKey.includes(sensitiveKey)) &&
113
+ lowerKey !== 'fileextension') { // keep fileExtension as it's safe
114
+ delete sanitizedProperties[key];
115
+ }
116
+ }
117
+ // Prepare standard properties
118
+ const baseProperties = {
119
+ timestamp: new Date().toISOString(),
120
+ platform: platform(),
121
+ app_version: VERSION,
122
+ engagement_time_msec: "100"
123
+ };
124
+ // Combine with sanitized properties
125
+ const eventProperties = {
126
+ ...baseProperties,
127
+ ...sanitizedProperties
128
+ };
129
+ // Prepare GA4 payload
130
+ const payload = {
131
+ client_id: uniqueUserId,
132
+ non_personalized_ads: false,
133
+ timestamp_micros: Date.now() * 1000,
134
+ events: [{
135
+ name: event,
136
+ params: eventProperties
137
+ }]
138
+ };
139
+ // Send data to Google Analytics
140
+ const postData = JSON.stringify(payload);
141
+ const options = {
142
+ method: 'POST',
143
+ headers: {
144
+ 'Content-Type': 'application/json',
145
+ 'Content-Length': Buffer.byteLength(postData)
146
+ }
147
+ };
148
+ const req = https.request(GA_BASE_URL, options, (res) => {
149
+ // Response handling (optional)
150
+ let data = '';
151
+ res.on('data', (chunk) => {
152
+ data += chunk;
153
+ });
154
+ res.on('end', () => {
155
+ if (res.statusCode !== 200 && res.statusCode !== 204) {
156
+ // Optional debug logging
157
+ // console.debug(`GA tracking error: ${res.statusCode} ${data}`);
158
+ }
159
+ });
160
+ });
161
+ req.on('error', () => {
162
+ // Silently fail - we don't want analytics issues to break functionality
163
+ });
164
+ // Set timeout to prevent blocking the app
165
+ req.setTimeout(3000, () => {
166
+ req.destroy();
51
167
  });
168
+ // Send data
169
+ req.write(postData);
170
+ req.end();
52
171
  }
53
172
  catch {
54
173
  // Silently fail - we don't want analytics issues to break functionality
@@ -65,13 +184,21 @@ export const capture = (event, properties) => {
65
184
  * @returns Promise that resolves with the operation result or the default value on timeout
66
185
  */
67
186
  export function withTimeout(operation, timeoutMs, operationName, defaultValue) {
68
- return new Promise((resolve) => {
187
+ // Don't sanitize operation name for logs - only telemetry will sanitize if needed
188
+ return new Promise((resolve, reject) => {
69
189
  let isCompleted = false;
70
190
  // Set up timeout
71
191
  const timeoutId = setTimeout(() => {
72
192
  if (!isCompleted) {
73
193
  isCompleted = true;
74
- resolve(defaultValue);
194
+ if (defaultValue !== null) {
195
+ resolve(defaultValue);
196
+ }
197
+ else {
198
+ // Keep the original operation name in the error message
199
+ // Telemetry sanitization happens at the capture level
200
+ reject(`__ERROR__: ${operationName} timed out after ${timeoutMs / 1000} seconds`);
201
+ }
75
202
  }
76
203
  }, timeoutMs);
77
204
  // Execute the operation
@@ -87,7 +214,13 @@ export function withTimeout(operation, timeoutMs, operationName, defaultValue) {
87
214
  if (!isCompleted) {
88
215
  isCompleted = true;
89
216
  clearTimeout(timeoutId);
90
- resolve(defaultValue);
217
+ if (defaultValue !== null) {
218
+ resolve(defaultValue);
219
+ }
220
+ else {
221
+ // Pass the original error unchanged - sanitization for telemetry happens in capture
222
+ reject(error);
223
+ }
91
224
  }
92
225
  });
93
226
  });
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.1.34";
1
+ export declare const VERSION = "0.1.36";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.1.34';
1
+ export const VERSION = '0.1.36';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wonderwhy-er/desktop-commander",
3
- "version": "0.1.34",
3
+ "version": "0.1.36",
4
4
  "description": "MCP server for terminal operations and file editing",
5
5
  "license": "MIT",
6
6
  "author": "Eduards Ruzga",
@@ -62,9 +62,8 @@
62
62
  "@modelcontextprotocol/sdk": "^1.8.0",
63
63
  "@vscode/ripgrep": "^1.15.9",
64
64
  "cross-fetch": "^4.1.0",
65
+ "fastest-levenshtein": "^1.0.16",
65
66
  "glob": "^10.3.10",
66
- "node-machine-id": "^1.1.12",
67
- "posthog-node": "^4.11.1",
68
67
  "zod": "^3.24.1",
69
68
  "zod-to-json-schema": "^3.23.5"
70
69
  },