@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.
- package/README.md +105 -36
- package/dist/command-manager.d.ts +1 -7
- package/dist/command-manager.js +31 -50
- package/dist/config-manager.d.ts +27 -16
- package/dist/config-manager.js +109 -191
- package/dist/config.js +8 -4
- package/dist/error-handlers.d.ts +7 -0
- package/dist/error-handlers.js +15 -0
- package/dist/handlers/command-handlers.d.ts +4 -18
- package/dist/handlers/command-handlers.js +15 -3
- package/dist/handlers/edit-search-handlers.d.ts +3 -12
- package/dist/handlers/edit-search-handlers.js +10 -7
- package/dist/handlers/filesystem-handlers.d.ts +9 -66
- package/dist/handlers/filesystem-handlers.js +106 -65
- package/dist/handlers/index.d.ts +0 -1
- package/dist/handlers/index.js +0 -1
- package/dist/handlers/process-handlers.d.ts +3 -12
- package/dist/handlers/terminal-handlers.d.ts +5 -24
- package/dist/index.js +18 -3
- package/dist/sandbox/index.d.ts +9 -0
- package/dist/sandbox/index.js +50 -0
- package/dist/sandbox/mac-sandbox.d.ts +19 -0
- package/dist/sandbox/mac-sandbox.js +174 -0
- package/dist/server.js +156 -176
- package/dist/setup-claude-server.js +98 -49
- package/dist/terminal-manager.d.ts +1 -1
- package/dist/terminal-manager.js +26 -4
- package/dist/tools/config.d.ts +0 -58
- package/dist/tools/config.js +44 -107
- package/dist/tools/debug-path.d.ts +1 -0
- package/dist/tools/debug-path.js +44 -0
- package/dist/tools/edit.d.ts +3 -1
- package/dist/tools/edit.js +19 -7
- package/dist/tools/execute.d.ts +4 -18
- package/dist/tools/execute.js +27 -8
- package/dist/tools/filesystem-fixed.d.ts +22 -0
- package/dist/tools/filesystem-fixed.js +176 -0
- package/dist/tools/filesystem.d.ts +4 -6
- package/dist/tools/filesystem.js +106 -75
- package/dist/tools/process.d.ts +3 -12
- package/dist/tools/process.js +12 -3
- package/dist/tools/schemas.d.ts +15 -14
- package/dist/tools/schemas.js +10 -6
- package/dist/tools/search.js +3 -3
- package/dist/types.d.ts +12 -0
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +92 -32
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- 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,
|
|
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
|
-
|
|
33
|
-
tools
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
},
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
"
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
"
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
185
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
10
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
127
|
+
node_version: nodeVersion,
|
|
128
|
+
npm_version: npmVersionCache,
|
|
129
|
+
execution_context: context.runMethod,
|
|
130
|
+
is_ci: context.isCI,
|
|
134
131
|
shell: context.shell,
|
|
135
|
-
|
|
136
|
-
|
|
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
|
|
138
|
+
// Helper function for tracking that handles errors gracefully
|
|
142
139
|
async function trackEvent(eventName, additionalProps = {}) {
|
|
143
|
-
if (!
|
|
140
|
+
if (!GA_MEASUREMENT_ID || !GA_API_SECRET) return; // Skip tracking if GA isn't configured
|
|
144
141
|
|
|
145
142
|
try {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
153
|
-
//
|
|
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
|
-
|
|
323
|
+
logToFile(`Claude has been restarted.`)
|
|
324
|
+
} catch{
|
|
262
325
|
|
|
263
|
-
|
|
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}
|
|
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[];
|
package/dist/terminal-manager.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
121
|
+
capture('server_request_error', { error: error, message: `Failed to terminate process ${pid}:` });
|
|
100
122
|
return false;
|
|
101
123
|
}
|
|
102
124
|
}
|