@wonderwhy-er/desktop-commander 0.1.33 → 0.1.35

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 (50) hide show
  1. package/README.md +105 -36
  2. package/dist/command-manager.d.ts +1 -7
  3. package/dist/command-manager.js +31 -50
  4. package/dist/config-manager.d.ts +27 -16
  5. package/dist/config-manager.js +109 -191
  6. package/dist/config.js +8 -4
  7. package/dist/error-handlers.d.ts +7 -0
  8. package/dist/error-handlers.js +15 -0
  9. package/dist/handlers/command-handlers.d.ts +4 -18
  10. package/dist/handlers/command-handlers.js +15 -3
  11. package/dist/handlers/edit-search-handlers.d.ts +3 -12
  12. package/dist/handlers/edit-search-handlers.js +10 -7
  13. package/dist/handlers/filesystem-handlers.d.ts +9 -66
  14. package/dist/handlers/filesystem-handlers.js +106 -65
  15. package/dist/handlers/index.d.ts +0 -1
  16. package/dist/handlers/index.js +0 -1
  17. package/dist/handlers/process-handlers.d.ts +3 -12
  18. package/dist/handlers/terminal-handlers.d.ts +5 -24
  19. package/dist/index.js +18 -3
  20. package/dist/sandbox/index.d.ts +9 -0
  21. package/dist/sandbox/index.js +50 -0
  22. package/dist/sandbox/mac-sandbox.d.ts +19 -0
  23. package/dist/sandbox/mac-sandbox.js +174 -0
  24. package/dist/server.js +156 -176
  25. package/dist/setup-claude-server.js +98 -49
  26. package/dist/terminal-manager.d.ts +1 -1
  27. package/dist/terminal-manager.js +26 -4
  28. package/dist/tools/config.d.ts +0 -58
  29. package/dist/tools/config.js +44 -107
  30. package/dist/tools/debug-path.d.ts +1 -0
  31. package/dist/tools/debug-path.js +44 -0
  32. package/dist/tools/edit.d.ts +3 -1
  33. package/dist/tools/edit.js +19 -7
  34. package/dist/tools/execute.d.ts +4 -18
  35. package/dist/tools/execute.js +27 -8
  36. package/dist/tools/filesystem-fixed.d.ts +22 -0
  37. package/dist/tools/filesystem-fixed.js +176 -0
  38. package/dist/tools/filesystem.d.ts +4 -6
  39. package/dist/tools/filesystem.js +106 -75
  40. package/dist/tools/process.d.ts +3 -12
  41. package/dist/tools/process.js +12 -3
  42. package/dist/tools/schemas.d.ts +15 -14
  43. package/dist/tools/schemas.js +10 -6
  44. package/dist/tools/search.js +3 -3
  45. package/dist/types.d.ts +12 -0
  46. package/dist/utils.d.ts +5 -0
  47. package/dist/utils.js +92 -32
  48. package/dist/version.d.ts +1 -1
  49. package/dist/version.js +1 -1
  50. package/package.json +1 -2
package/dist/server.js CHANGED
@@ -1,9 +1,11 @@
1
1
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
2
  import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ListPromptsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
3
3
  import { zodToJsonSchema } from "zod-to-json-schema";
4
- import { ExecuteCommandArgsSchema, ReadOutputArgsSchema, ForceTerminateArgsSchema, ListSessionsArgsSchema, KillProcessArgsSchema, BlockCommandArgsSchema, UnblockCommandArgsSchema, ReadFileArgsSchema, ReadMultipleFilesArgsSchema, WriteFileArgsSchema, CreateDirectoryArgsSchema, ListDirectoryArgsSchema, MoveFileArgsSchema, SearchFilesArgsSchema, GetFileInfoArgsSchema, EditBlockArgsSchema, SearchCodeArgsSchema, } from './tools/schemas.js';
4
+ import { ExecuteCommandArgsSchema, ReadOutputArgsSchema, ForceTerminateArgsSchema, ListSessionsArgsSchema, KillProcessArgsSchema, ReadFileArgsSchema, ReadMultipleFilesArgsSchema, WriteFileArgsSchema, CreateDirectoryArgsSchema, ListDirectoryArgsSchema, MoveFileArgsSchema, SearchFilesArgsSchema, GetFileInfoArgsSchema, EditBlockArgsSchema, SearchCodeArgsSchema, GetConfigArgsSchema, SetConfigValueArgsSchema, ListProcessesArgsSchema, } from './tools/schemas.js';
5
+ import { getConfig, setConfigValue } from './tools/config.js';
5
6
  import { VERSION } from './version.js';
6
7
  import { capture } from "./utils.js";
8
+ console.error("Loading server.ts");
7
9
  export const server = new Server({
8
10
  name: "desktop-commander",
9
11
  version: VERSION,
@@ -28,210 +30,188 @@ server.setRequestHandler(ListPromptsRequestSchema, async () => {
28
30
  prompts: [],
29
31
  };
30
32
  });
33
+ console.error("Setting up request handlers...");
31
34
  server.setRequestHandler(ListToolsRequestSchema, async () => {
32
- return {
33
- tools: [
34
- // Terminal tools
35
- {
36
- name: "execute_command",
37
- description: "Execute a terminal command with timeout. Command will continue running in background if it doesn't complete within timeout.",
38
- inputSchema: zodToJsonSchema(ExecuteCommandArgsSchema),
39
- },
40
- {
41
- name: "read_output",
42
- description: "Read new output from a running terminal session.",
43
- inputSchema: zodToJsonSchema(ReadOutputArgsSchema),
44
- },
45
- {
46
- name: "force_terminate",
47
- description: "Force terminate a running terminal session.",
48
- inputSchema: zodToJsonSchema(ForceTerminateArgsSchema),
49
- },
50
- {
51
- name: "list_sessions",
52
- description: "List all active terminal sessions.",
53
- inputSchema: zodToJsonSchema(ListSessionsArgsSchema),
54
- },
55
- {
56
- name: "list_processes",
57
- description: "List all running processes. Returns process information including PID, " +
58
- "command name, CPU usage, and memory usage.",
59
- inputSchema: {
60
- type: "object",
61
- properties: {},
62
- required: [],
63
- },
64
- },
65
- {
66
- name: "kill_process",
67
- description: "Terminate a running process by PID. Use with caution as this will " +
68
- "forcefully terminate the specified process.",
69
- inputSchema: zodToJsonSchema(KillProcessArgsSchema),
70
- },
71
- {
72
- name: "block_command",
73
- description: "Add a command to the blacklist. Once blocked, the command cannot be executed until unblocked.",
74
- inputSchema: zodToJsonSchema(BlockCommandArgsSchema),
75
- },
76
- {
77
- name: "unblock_command",
78
- description: "Remove a command from the blacklist. Once unblocked, the command can be executed normally.",
79
- inputSchema: zodToJsonSchema(UnblockCommandArgsSchema),
80
- },
81
- {
82
- name: "list_blocked_commands",
83
- description: "List all currently blocked commands.",
84
- inputSchema: {
85
- type: "object",
86
- properties: {},
87
- required: [],
88
- },
89
- },
90
- // Filesystem tools
91
- {
92
- name: "read_file",
93
- description: "Read the complete contents of a file from the file system or a URL. " +
94
- "When reading from the file system, only works within allowed directories. " +
95
- "Can fetch content from URLs when isUrl parameter is set to true. " +
96
- "Handles text files normally and image files are returned as viewable images. " +
97
- "Recognized image types: PNG, JPEG, GIF, WebP.",
98
- inputSchema: zodToJsonSchema(ReadFileArgsSchema),
99
- },
100
- {
101
- name: "read_multiple_files",
102
- description: "Read the contents of multiple files simultaneously. " +
103
- "Each file's content is returned with its path as a reference. " +
104
- "Handles text files normally and renders images as viewable content. " +
105
- "Recognized image types: PNG, JPEG, GIF, WebP. " +
106
- "Failed reads for individual files won't stop the entire operation. " +
107
- "Only works within allowed directories.",
108
- inputSchema: zodToJsonSchema(ReadMultipleFilesArgsSchema),
109
- },
110
- {
111
- name: "write_file",
112
- description: "Completely replace file contents. Best for large changes (>20% of file) or when edit_block fails. " +
113
- "Use with caution as it will overwrite existing files. Only works within allowed directories.",
114
- inputSchema: zodToJsonSchema(WriteFileArgsSchema),
115
- },
116
- {
117
- name: "create_directory",
118
- description: "Create a new directory or ensure a directory exists. Can create multiple " +
119
- "nested directories in one operation. Only works within allowed directories.",
120
- inputSchema: zodToJsonSchema(CreateDirectoryArgsSchema),
121
- },
122
- {
123
- name: "list_directory",
124
- description: "Get a detailed listing of all files and directories in a specified path. " +
125
- "Results distinguish between files and directories with [FILE] and [DIR] prefixes. " +
126
- "Only works within allowed directories.",
127
- inputSchema: zodToJsonSchema(ListDirectoryArgsSchema),
128
- },
129
- {
130
- name: "move_file",
131
- description: "Move or rename files and directories. Can move files between directories " +
132
- "and rename them in a single operation. Both source and destination must be " +
133
- "within allowed directories.",
134
- inputSchema: zodToJsonSchema(MoveFileArgsSchema),
135
- },
136
- {
137
- name: "search_files",
138
- description: "Finds files by name using a case-insensitive substring matching. " +
139
- "Searches through all subdirectories from the starting path. " +
140
- "Has a default timeout of 30 seconds which can be customized using the timeoutMs parameter. " +
141
- "Only searches within allowed directories.",
142
- inputSchema: zodToJsonSchema(SearchFilesArgsSchema),
143
- },
144
- {
145
- name: "search_code",
146
- description: "Search for text/code patterns within file contents using ripgrep. " +
147
- "Fast and powerful search similar to VS Code search functionality. " +
148
- "Supports regular expressions, file pattern filtering, and context lines. " +
149
- "Has a default timeout of 30 seconds which can be customized. " +
150
- "Only searches within allowed directories.",
151
- inputSchema: zodToJsonSchema(SearchCodeArgsSchema),
152
- },
153
- {
154
- name: "get_file_info",
155
- description: "Retrieve detailed metadata about a file or directory including size, " +
156
- "creation time, last modified time, permissions, and type. " +
157
- "Only works within allowed directories.",
158
- inputSchema: zodToJsonSchema(GetFileInfoArgsSchema),
159
- },
160
- {
161
- name: "list_allowed_directories",
162
- description: "Returns the list of directories that this server is allowed to access.",
163
- inputSchema: {
164
- type: "object",
165
- properties: {},
166
- required: [],
167
- },
168
- },
169
- {
170
- name: "edit_block",
171
- description: "Apply surgical text replacements to files. Best for small changes (<20% of file size). " +
172
- "Call repeatedly to change multiple blocks. Will verify changes after application. " +
173
- "Format:\nfilepath\n<<<<<<< SEARCH\ncontent to find\n=======\nnew content\n>>>>>>> REPLACE",
174
- inputSchema: zodToJsonSchema(EditBlockArgsSchema),
175
- },
176
- ],
177
- };
35
+ try {
36
+ console.error("Generating tools list...");
37
+ return {
38
+ tools: [
39
+ // Configuration tools
40
+ {
41
+ name: "get_config",
42
+ description: "Get the complete server configuration as JSON. Config includes fields for: blockedCommands (array of blocked shell commands), defaultShell (shell to use for commands), allowedDirectories (paths the server can access).",
43
+ inputSchema: zodToJsonSchema(GetConfigArgsSchema),
44
+ },
45
+ {
46
+ name: "set_config_value",
47
+ description: "Set a specific configuration value by key. WARNING: Should be used in a separate chat from file operations and command execution to prevent security issues. Config keys include: blockedCommands (array), defaultShell (string), allowedDirectories (array of paths). IMPORTANT: Setting allowedDirectories to an empty array ([]) allows full access to the entire file system, regardless of the operating system.",
48
+ inputSchema: zodToJsonSchema(SetConfigValueArgsSchema),
49
+ },
50
+ // Terminal tools
51
+ {
52
+ name: "execute_command",
53
+ description: "Execute a terminal command with timeout. Command will continue running in background if it doesn't complete within timeout.",
54
+ inputSchema: zodToJsonSchema(ExecuteCommandArgsSchema),
55
+ },
56
+ {
57
+ name: "read_output",
58
+ description: "Read new output from a running terminal session.",
59
+ inputSchema: zodToJsonSchema(ReadOutputArgsSchema),
60
+ },
61
+ {
62
+ name: "force_terminate",
63
+ description: "Force terminate a running terminal session.",
64
+ inputSchema: zodToJsonSchema(ForceTerminateArgsSchema),
65
+ },
66
+ {
67
+ name: "list_sessions",
68
+ description: "List all active terminal sessions.",
69
+ inputSchema: zodToJsonSchema(ListSessionsArgsSchema),
70
+ },
71
+ {
72
+ name: "list_processes",
73
+ description: "List all running processes. Returns process information including PID, command name, CPU usage, and memory usage.",
74
+ inputSchema: zodToJsonSchema(ListProcessesArgsSchema),
75
+ },
76
+ {
77
+ name: "kill_process",
78
+ description: "Terminate a running process by PID. Use with caution as this will forcefully terminate the specified process.",
79
+ inputSchema: zodToJsonSchema(KillProcessArgsSchema),
80
+ },
81
+ // Filesystem tools
82
+ {
83
+ name: "read_file",
84
+ description: "Read the complete contents of a file from the file system or a URL. When reading from the file system, only works within allowed directories. Can fetch content from URLs when isUrl parameter is set to true. Handles text files normally and image files are returned as viewable images. Recognized image types: PNG, JPEG, GIF, WebP.",
85
+ inputSchema: zodToJsonSchema(ReadFileArgsSchema),
86
+ },
87
+ {
88
+ name: "read_multiple_files",
89
+ description: "Read the contents of multiple files simultaneously. Each file's content is returned with its path as a reference. Handles text files normally and renders images as viewable content. Recognized image types: PNG, JPEG, GIF, WebP. Failed reads for individual files won't stop the entire operation. Only works within allowed directories.",
90
+ inputSchema: zodToJsonSchema(ReadMultipleFilesArgsSchema),
91
+ },
92
+ {
93
+ name: "write_file",
94
+ description: "Completely replace file contents. Best for large changes (>20% of file) or when edit_block fails. Use with caution as it will overwrite existing files. Only works within allowed directories.",
95
+ inputSchema: zodToJsonSchema(WriteFileArgsSchema),
96
+ },
97
+ {
98
+ name: "create_directory",
99
+ description: "Create a new directory or ensure a directory exists. Can create multiple nested directories in one operation. Only works within allowed directories.",
100
+ inputSchema: zodToJsonSchema(CreateDirectoryArgsSchema),
101
+ },
102
+ {
103
+ name: "list_directory",
104
+ description: "Get a detailed listing of all files and directories in a specified path. Results distinguish between files and directories with [FILE] and [DIR] prefixes. Only works within allowed directories.",
105
+ inputSchema: zodToJsonSchema(ListDirectoryArgsSchema),
106
+ },
107
+ {
108
+ name: "move_file",
109
+ description: "Move or rename files and directories. Can move files between directories and rename them in a single operation. Both source and destination must be within allowed directories.",
110
+ inputSchema: zodToJsonSchema(MoveFileArgsSchema),
111
+ },
112
+ {
113
+ name: "search_files",
114
+ description: "Finds files by name using a case-insensitive substring matching. Searches through all subdirectories from the starting path. Has a default timeout of 30 seconds which can be customized using the timeoutMs parameter. Only searches within allowed directories.",
115
+ inputSchema: zodToJsonSchema(SearchFilesArgsSchema),
116
+ },
117
+ {
118
+ name: "search_code",
119
+ description: "Search for text/code patterns within file contents using ripgrep. Fast and powerful search similar to VS Code search functionality. Supports regular expressions, file pattern filtering, and context lines. Has a default timeout of 30 seconds which can be customized. Only searches within allowed directories.",
120
+ inputSchema: zodToJsonSchema(SearchCodeArgsSchema),
121
+ },
122
+ {
123
+ name: "get_file_info",
124
+ description: "Retrieve detailed metadata about a file or directory including size, creation time, last modified time, permissions, and type. Only works within allowed directories.",
125
+ inputSchema: zodToJsonSchema(GetFileInfoArgsSchema),
126
+ },
127
+ // Note: list_allowed_directories removed - use get_config to check allowedDirectories
128
+ // Text editing tools
129
+ {
130
+ name: "edit_block",
131
+ description: "Apply surgical text replacements to files. Best for small changes (<20% of file size). Call repeatedly to change multiple blocks. Will verify changes after application. Format:\nfilepath\n<<<<<<< SEARCH\ncontent to find\n=======\nnew content\n>>>>>>> REPLACE",
132
+ inputSchema: zodToJsonSchema(EditBlockArgsSchema),
133
+ },
134
+ ],
135
+ };
136
+ }
137
+ catch (error) {
138
+ console.error("Error in list_tools request handler:", error);
139
+ throw error;
140
+ }
178
141
  });
179
142
  import * as handlers from './handlers/index.js';
180
143
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
181
144
  try {
182
145
  const { name, arguments: args } = request.params;
183
- capture('server_call_tool');
184
- // Add a single dynamic capture for the specific tool
185
- capture('server_' + name);
146
+ capture('server_call_tool', {
147
+ name
148
+ });
186
149
  // Using a more structured approach with dedicated handlers
187
150
  switch (name) {
151
+ // Config tools
152
+ case "get_config":
153
+ try {
154
+ return await getConfig();
155
+ }
156
+ catch (error) {
157
+ capture('server_request_error', { message: `Error in get_config handler: ${error}` });
158
+ return {
159
+ content: [{ type: "text", text: `Error: Failed to get configuration` }],
160
+ isError: true,
161
+ };
162
+ }
163
+ case "set_config_value":
164
+ try {
165
+ return await setConfigValue(args);
166
+ }
167
+ catch (error) {
168
+ capture('server_request_error', { message: `Error in set_config_value handler: ${error}` });
169
+ return {
170
+ content: [{ type: "text", text: `Error: Failed to set configuration value` }],
171
+ isError: true,
172
+ };
173
+ }
188
174
  // Terminal tools
189
175
  case "execute_command":
190
- return handlers.handleExecuteCommand(args);
176
+ return await handlers.handleExecuteCommand(args);
191
177
  case "read_output":
192
- return handlers.handleReadOutput(args);
178
+ return await handlers.handleReadOutput(args);
193
179
  case "force_terminate":
194
- return handlers.handleForceTerminate(args);
180
+ return await handlers.handleForceTerminate(args);
195
181
  case "list_sessions":
196
- return handlers.handleListSessions();
182
+ return await handlers.handleListSessions();
197
183
  // Process tools
198
184
  case "list_processes":
199
- return handlers.handleListProcesses();
185
+ return await handlers.handleListProcesses();
200
186
  case "kill_process":
201
- return handlers.handleKillProcess(args);
202
- // Command management tools
203
- case "block_command":
204
- return handlers.handleBlockCommand(args);
205
- case "unblock_command":
206
- return handlers.handleUnblockCommand(args);
207
- case "list_blocked_commands":
208
- return handlers.handleListBlockedCommands();
187
+ return await handlers.handleKillProcess(args);
209
188
  // Filesystem tools
210
189
  case "read_file":
211
- return handlers.handleReadFile(args);
190
+ return await handlers.handleReadFile(args);
212
191
  case "read_multiple_files":
213
- return handlers.handleReadMultipleFiles(args);
192
+ return await handlers.handleReadMultipleFiles(args);
214
193
  case "write_file":
215
- return handlers.handleWriteFile(args);
194
+ return await handlers.handleWriteFile(args);
216
195
  case "create_directory":
217
- return handlers.handleCreateDirectory(args);
196
+ return await handlers.handleCreateDirectory(args);
218
197
  case "list_directory":
219
- return handlers.handleListDirectory(args);
198
+ return await handlers.handleListDirectory(args);
220
199
  case "move_file":
221
- return handlers.handleMoveFile(args);
200
+ return await handlers.handleMoveFile(args);
222
201
  case "search_files":
223
- return handlers.handleSearchFiles(args);
202
+ return await handlers.handleSearchFiles(args);
224
203
  case "search_code":
225
- return handlers.handleSearchCode(args);
204
+ return await handlers.handleSearchCode(args);
226
205
  case "get_file_info":
227
- return handlers.handleGetFileInfo(args);
228
- case "list_allowed_directories":
229
- return handlers.handleListAllowedDirectories();
206
+ return await handlers.handleGetFileInfo(args);
230
207
  case "edit_block":
231
- return handlers.handleEditBlock(args);
208
+ return await handlers.handleEditBlock(args);
232
209
  default:
233
210
  capture('server_unknown_tool', { name });
234
- throw new Error(`Unknown tool: ${name}`);
211
+ return {
212
+ content: [{ type: "text", text: `Error: Unknown tool: ${name}` }],
213
+ isError: true,
214
+ };
235
215
  }
236
216
  }
237
217
  catch (error) {
@@ -5,28 +5,25 @@ import { fileURLToPath } from 'url';
5
5
  import { dirname } from 'path';
6
6
  import { exec } from "node:child_process";
7
7
  import { version as nodeVersion } from 'process';
8
+ import * as https from 'https';
8
9
 
9
- // Optional analytics - will gracefully degrade if posthog-node isn't available
10
- let client = null;
10
+ // Google Analytics configuration
11
+ const GA_MEASUREMENT_ID = 'G-NGGDNL0K4L'; // Replace with your GA4 Measurement ID
12
+ const GA_API_SECRET = '5M0mC--2S_6t94m8WrI60A'; // Replace with your GA4 API Secret
13
+ const GA_BASE_URL = `https://www.google-analytics.com/mp/collect?measurement_id=${GA_MEASUREMENT_ID}&api_secret=${GA_API_SECRET}`;
14
+
15
+ // Optional analytics - will gracefully degrade if dependencies aren't available
11
16
  let uniqueUserId = 'unknown';
12
17
 
13
18
  try {
14
- const { PostHog } = await import('posthog-node');
19
+ // Only dependency is node-machine-id
15
20
  const machineIdModule = await import('node-machine-id');
16
-
17
- client = new PostHog(
18
- 'phc_BW8KJ0cajzj2v8qfMhvDQ4dtFdgHPzeYcMRvRFGvQdH',
19
- {
20
- host: 'https://eu.i.posthog.com',
21
- flushAt: 1, // send all every time
22
- flushInterval: 0 //send always
23
- }
24
- );
25
-
21
+
26
22
  // Get a unique user ID
27
23
  uniqueUserId = machineIdModule.machineIdSync();
28
24
  } catch (error) {
29
- //console.error('Analytics module not available - continuing without tracking');
25
+ // Fall back to a semi-unique identifier if machine-id is not available
26
+ uniqueUserId = `${platform()}-${process.env.USER || process.env.USERNAME || 'unknown'}-${Date.now()}`;
30
27
  }
31
28
 
32
29
  // Function to get npm version
@@ -127,33 +124,98 @@ async function getTrackingProperties(additionalProps = {}) {
127
124
  const version = await getVersion();
128
125
  return {
129
126
  platform: platform(),
130
- nodeVersion: nodeVersion,
131
- npmVersion: npmVersionCache,
132
- executionContext: context.runMethod,
133
- isCI: context.isCI,
127
+ node_version: nodeVersion,
128
+ npm_version: npmVersionCache,
129
+ execution_context: context.runMethod,
130
+ is_ci: context.isCI,
134
131
  shell: context.shell,
135
- DCVersion: version,
136
- timestamp: new Date().toISOString(),
132
+ app_version: version,
133
+ engagement_time_msec: "100",
137
134
  ...additionalProps
138
135
  };
139
136
  }
140
137
 
141
- // Helper function for tracking that handles missing PostHog gracefully
138
+ // Helper function for tracking that handles errors gracefully
142
139
  async function trackEvent(eventName, additionalProps = {}) {
143
- if (!client) return; // Skip tracking if PostHog client isn't available
140
+ if (!GA_MEASUREMENT_ID || !GA_API_SECRET) return; // Skip tracking if GA isn't configured
144
141
 
145
142
  try {
146
- client.capture({
147
- distinctId: uniqueUserId,
148
- event: eventName,
149
- properties: await getTrackingProperties(additionalProps)
143
+ // Get enriched properties
144
+ const eventProperties = await getTrackingProperties(additionalProps);
145
+
146
+ // Prepare GA4 payload
147
+ const payload = {
148
+ client_id: uniqueUserId,
149
+ non_personalized_ads: false,
150
+ timestamp_micros: Date.now() * 1000,
151
+ events: [{
152
+ name: eventName,
153
+ params: eventProperties
154
+ }]
155
+ };
156
+
157
+ // Send to Google Analytics
158
+ const postData = JSON.stringify(payload);
159
+
160
+ const options = {
161
+ method: 'POST',
162
+ headers: {
163
+ 'Content-Type': 'application/json',
164
+ 'Content-Length': Buffer.byteLength(postData)
165
+ }
166
+ };
167
+
168
+ return new Promise((resolve) => {
169
+ const req = https.request(GA_BASE_URL, options, (res) => {
170
+ // Optional response handling
171
+ let data = '';
172
+ res.on('data', (chunk) => {
173
+ data += chunk;
174
+ });
175
+
176
+ res.on('end', () => {
177
+ resolve(true);
178
+ });
179
+ });
180
+
181
+ req.on('error', (error) => {
182
+ // Silently fail - we don't want tracking issues to break functionality
183
+ resolve(false);
184
+ });
185
+
186
+ // Set timeout to prevent blocking
187
+ req.setTimeout(3000, () => {
188
+ req.destroy();
189
+ resolve(false);
190
+ });
191
+
192
+ req.write(postData);
193
+ req.end();
194
+
195
+ // read response from request
196
+ req.on('response', (res) => {
197
+ // Optional response handling
198
+ let data = '';
199
+ res.on('data', (chunk) => {
200
+ data += chunk;
201
+ });
202
+
203
+ //response status
204
+ res.on('error', (error) => {
205
+ resolve(false);
206
+ });
207
+
208
+ res.on('end', () => {
209
+ resolve(true);
210
+ });
211
+ });
150
212
  });
151
213
  } catch (error) {
152
- // Silently fail if tracking fails - we don't want to break the setup process
153
- //console.log(`Note: Event tracking unavailable for ${eventName}`);
214
+ logToFile(`Error tracking event ${eventName}: ${error}`, true);
215
+ // Silently fail if tracking fails - we don't want to break the application
216
+ return false;
154
217
  }
155
218
  }
156
-
157
219
  // Initial tracking
158
220
  trackEvent('npx_setup_start');
159
221
 
@@ -258,12 +320,16 @@ async function restartClaude() {
258
320
  } else if (platform === "linux") {
259
321
  await execAsync(`claude`)
260
322
  }
261
- } catch{}
323
+ logToFile(`Claude has been restarted.`)
324
+ } catch{
262
325
 
263
- logToFile(`Claude has been restarted.`)
326
+ }
327
+
328
+
264
329
  } catch (error) {
265
330
  await trackEvent('npx_setup_restart_claude_error', { error: error.message });
266
- logToFile(`Failed to restart Claude: ${error}`, true)
331
+ logToFile(`Failed to restart Claude: ${error}. Please restart it manually.`, true)
332
+ logToFile(`If Claude Desktop is not installed use this link to download https://claude.ai/download`, true)
267
333
  }
268
334
  }
269
335
 
@@ -413,26 +479,9 @@ export default async function setup() {
413
479
 
414
480
  await trackEvent('npx_setup_complete');
415
481
 
416
- // Safe shutdown
417
- if (client) {
418
- try {
419
- await client.shutdown();
420
- } catch (error) {
421
- // Ignore shutdown errors
422
- }
423
- }
424
482
  } catch (error) {
425
483
  await trackEvent('npx_setup_final_error', { error: error.message });
426
484
  logToFile(`Error updating Claude configuration: ${error}`, true);
427
-
428
- // Safe shutdown
429
- if (client) {
430
- try {
431
- await client.shutdown();
432
- } catch (error) {
433
- // Ignore shutdown errors
434
- }
435
- }
436
485
  process.exit(1);
437
486
  }
438
487
  }
@@ -9,7 +9,7 @@ interface CompletedSession {
9
9
  export declare class TerminalManager {
10
10
  private sessions;
11
11
  private completedSessions;
12
- executeCommand(command: string, timeoutMs?: number): Promise<CommandExecutionResult>;
12
+ executeCommand(command: string, timeoutMs?: number, shell?: string): Promise<CommandExecutionResult>;
13
13
  getNewOutput(pid: number): string | null;
14
14
  forceTerminate(pid: number): boolean;
15
15
  listActiveSessions(): ActiveSession[];
@@ -1,16 +1,38 @@
1
1
  import { spawn } from 'child_process';
2
2
  import { DEFAULT_COMMAND_TIMEOUT } from './config.js';
3
+ import { configManager } from './config-manager.js';
4
+ import { capture } from "./utils.js";
3
5
  export class TerminalManager {
4
6
  constructor() {
5
7
  this.sessions = new Map();
6
8
  this.completedSessions = new Map();
7
9
  }
8
- async executeCommand(command, timeoutMs = DEFAULT_COMMAND_TIMEOUT) {
9
- const process = spawn(command, [], { shell: true });
10
+ async executeCommand(command, timeoutMs = DEFAULT_COMMAND_TIMEOUT, shell) {
11
+ // Get the shell from config if not specified
12
+ let shellToUse = shell;
13
+ if (!shellToUse) {
14
+ try {
15
+ const config = await configManager.getConfig();
16
+ shellToUse = config.shell || true;
17
+ }
18
+ catch (error) {
19
+ // If there's an error getting the config, fall back to default
20
+ shellToUse = true;
21
+ }
22
+ }
23
+ const spawnOptions = {
24
+ shell: shellToUse
25
+ };
26
+ const process = spawn(command, [], spawnOptions);
10
27
  let output = '';
11
28
  // Ensure process.pid is defined before proceeding
12
29
  if (!process.pid) {
13
- throw new Error('Failed to get process ID');
30
+ // Return a consistent error object instead of throwing
31
+ return {
32
+ pid: -1, // Use -1 to indicate an error state
33
+ output: 'Error: Failed to get process ID. The command could not be executed.',
34
+ isBlocked: false
35
+ };
14
36
  }
15
37
  const session = {
16
38
  pid: process.pid,
@@ -96,7 +118,7 @@ export class TerminalManager {
96
118
  return true;
97
119
  }
98
120
  catch (error) {
99
- console.error(`Failed to terminate process ${pid}:`, error);
121
+ capture('server_request_error', { error: error, message: `Failed to terminate process ${pid}:` });
100
122
  return false;
101
123
  }
102
124
  }