@wonderwhy-er/desktop-commander 0.1.35 → 0.1.37

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 (47) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +88 -27
  3. package/dist/command-manager.js +1 -1
  4. package/dist/config-manager.d.ts +1 -0
  5. package/dist/config-manager.js +21 -4
  6. package/dist/config.d.ts +2 -2
  7. package/dist/config.js +2 -3
  8. package/dist/error-handlers.js +1 -1
  9. package/dist/handlers/edit-search-handlers.d.ts +3 -1
  10. package/dist/handlers/edit-search-handlers.js +6 -12
  11. package/dist/handlers/filesystem-handlers.js +1 -1
  12. package/dist/index.js +1 -1
  13. package/dist/polyform-license-src/edit/edit.d.ts +15 -0
  14. package/dist/polyform-license-src/edit/edit.js +163 -0
  15. package/dist/polyform-license-src/edit/fuzzySearch.d.ts +30 -0
  16. package/dist/polyform-license-src/edit/fuzzySearch.js +121 -0
  17. package/dist/polyform-license-src/edit/handlers.d.ts +16 -0
  18. package/dist/polyform-license-src/edit/handlers.js +24 -0
  19. package/dist/polyform-license-src/edit/index.d.ts +12 -0
  20. package/dist/polyform-license-src/edit/index.js +13 -0
  21. package/dist/polyform-license-src/edit/schemas.d.ts +25 -0
  22. package/dist/polyform-license-src/edit/schemas.js +16 -0
  23. package/dist/polyform-license-src/index.d.ts +9 -0
  24. package/dist/polyform-license-src/index.js +10 -0
  25. package/dist/server.js +71 -43
  26. package/dist/setup-claude-server.js +549 -288
  27. package/dist/terminal-manager.js +4 -2
  28. package/dist/tools/edit.d.ts +8 -6
  29. package/dist/tools/edit.js +161 -34
  30. package/dist/tools/execute.js +2 -2
  31. package/dist/tools/filesystem.js +59 -10
  32. package/dist/tools/fuzzySearch.d.ts +22 -0
  33. package/dist/tools/fuzzySearch.js +113 -0
  34. package/dist/tools/pdf-reader.d.ts +13 -0
  35. package/dist/tools/pdf-reader.js +214 -0
  36. package/dist/tools/schemas.d.ts +12 -3
  37. package/dist/tools/schemas.js +5 -2
  38. package/dist/tools/search.js +5 -4
  39. package/dist/utils/capture.d.ts +15 -0
  40. package/dist/utils/capture.js +175 -0
  41. package/dist/utils/withTimeout.d.ts +11 -0
  42. package/dist/utils/withTimeout.js +52 -0
  43. package/dist/utils.d.ts +10 -1
  44. package/dist/utils.js +99 -26
  45. package/dist/version.d.ts +1 -1
  46. package/dist/version.js +1 -1
  47. package/package.json +2 -2
package/dist/utils.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { platform } from 'os';
2
- import { createHash } from 'crypto';
2
+ import { randomUUID } from 'crypto';
3
3
  import * as https from 'https';
4
+ import { configManager } from './config-manager.js';
4
5
  let VERSION = 'unknown';
5
6
  try {
6
7
  const versionModule = await import('./version.js');
@@ -13,38 +14,106 @@ catch {
13
14
  const GA_MEASUREMENT_ID = 'G-NGGDNL0K4L'; // Replace with your GA4 Measurement ID
14
15
  const GA_API_SECRET = '5M0mC--2S_6t94m8WrI60A'; // Replace with your GA4 API Secret
15
16
  const GA_BASE_URL = `https://www.google-analytics.com/mp/collect?measurement_id=${GA_MEASUREMENT_ID}&api_secret=${GA_API_SECRET}`;
16
- // Set default tracking state
17
- const isTrackingEnabled = true;
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
18
19
  let uniqueUserId = 'unknown';
19
- // Try to generate a unique user ID without breaking if dependencies aren't available
20
- try {
21
- // Dynamic import to prevent crashing if dependency isn't available
22
- import('node-machine-id').then((machineIdModule) => {
23
- // Access the default export from the module
24
- uniqueUserId = machineIdModule.default.machineIdSync();
25
- }).catch(() => {
26
- // Fallback to a semi-random ID if machine-id isn't available
27
- uniqueUserId = createHash('sha256')
28
- .update(`${platform()}-${process.env.USER || process.env.USERNAME || 'user'}-${Date.now()}`)
29
- .digest('hex');
30
- });
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
+ }
31
36
  }
32
- catch {
33
- // Fallback to a semi-random ID if import fails
34
- uniqueUserId = createHash('sha256')
35
- .update(`${platform()}-${process.env.USER || process.env.USERNAME || 'user'}-${Date.now()}`)
36
- .digest('hex');
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
+ }
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
+ };
37
67
  }
38
68
  /**
39
69
  * Send an event to Google Analytics
40
70
  * @param event Event name
41
71
  * @param properties Optional event properties
42
72
  */
43
- export const capture = (event, properties) => {
44
- if (!isTrackingEnabled || !GA_MEASUREMENT_ID || !GA_API_SECRET) {
45
- return;
46
- }
73
+ export const capture = async (event, properties) => {
47
74
  try {
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
+ }
48
117
  // Prepare standard properties
49
118
  const baseProperties = {
50
119
  timestamp: new Date().toISOString(),
@@ -52,10 +121,10 @@ export const capture = (event, properties) => {
52
121
  app_version: VERSION,
53
122
  engagement_time_msec: "100"
54
123
  };
55
- // Combine with custom properties
124
+ // Combine with sanitized properties
56
125
  const eventProperties = {
57
126
  ...baseProperties,
58
- ...(properties || {})
127
+ ...sanitizedProperties
59
128
  };
60
129
  // Prepare GA4 payload
61
130
  const payload = {
@@ -115,6 +184,7 @@ export const capture = (event, properties) => {
115
184
  * @returns Promise that resolves with the operation result or the default value on timeout
116
185
  */
117
186
  export function withTimeout(operation, timeoutMs, operationName, defaultValue) {
187
+ // Don't sanitize operation name for logs - only telemetry will sanitize if needed
118
188
  return new Promise((resolve, reject) => {
119
189
  let isCompleted = false;
120
190
  // Set up timeout
@@ -125,6 +195,8 @@ export function withTimeout(operation, timeoutMs, operationName, defaultValue) {
125
195
  resolve(defaultValue);
126
196
  }
127
197
  else {
198
+ // Keep the original operation name in the error message
199
+ // Telemetry sanitization happens at the capture level
128
200
  reject(`__ERROR__: ${operationName} timed out after ${timeoutMs / 1000} seconds`);
129
201
  }
130
202
  }
@@ -146,6 +218,7 @@ export function withTimeout(operation, timeoutMs, operationName, defaultValue) {
146
218
  resolve(defaultValue);
147
219
  }
148
220
  else {
221
+ // Pass the original error unchanged - sanitization for telemetry happens in capture
149
222
  reject(error);
150
223
  }
151
224
  }
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.1.35";
1
+ export declare const VERSION = "0.1.37";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.1.35';
1
+ export const VERSION = '0.1.37';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wonderwhy-er/desktop-commander",
3
- "version": "0.1.35",
3
+ "version": "0.1.37",
4
4
  "description": "MCP server for terminal operations and file editing",
5
5
  "license": "MIT",
6
6
  "author": "Eduards Ruzga",
@@ -62,8 +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
67
  "zod": "^3.24.1",
68
68
  "zod-to-json-schema": "^3.23.5"
69
69
  },