@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.
- package/LICENSE +2 -2
- package/README.md +88 -27
- package/dist/command-manager.js +1 -1
- package/dist/config-manager.d.ts +1 -0
- package/dist/config-manager.js +21 -4
- package/dist/config.d.ts +2 -2
- package/dist/config.js +2 -3
- package/dist/error-handlers.js +1 -1
- package/dist/handlers/edit-search-handlers.d.ts +3 -1
- package/dist/handlers/edit-search-handlers.js +6 -12
- package/dist/handlers/filesystem-handlers.js +1 -1
- package/dist/index.js +1 -1
- package/dist/polyform-license-src/edit/edit.d.ts +15 -0
- package/dist/polyform-license-src/edit/edit.js +163 -0
- package/dist/polyform-license-src/edit/fuzzySearch.d.ts +30 -0
- package/dist/polyform-license-src/edit/fuzzySearch.js +121 -0
- package/dist/polyform-license-src/edit/handlers.d.ts +16 -0
- package/dist/polyform-license-src/edit/handlers.js +24 -0
- package/dist/polyform-license-src/edit/index.d.ts +12 -0
- package/dist/polyform-license-src/edit/index.js +13 -0
- package/dist/polyform-license-src/edit/schemas.d.ts +25 -0
- package/dist/polyform-license-src/edit/schemas.js +16 -0
- package/dist/polyform-license-src/index.d.ts +9 -0
- package/dist/polyform-license-src/index.js +10 -0
- package/dist/server.js +71 -43
- package/dist/setup-claude-server.js +549 -288
- package/dist/terminal-manager.js +4 -2
- package/dist/tools/edit.d.ts +8 -6
- package/dist/tools/edit.js +161 -34
- package/dist/tools/execute.js +2 -2
- package/dist/tools/filesystem.js +59 -10
- package/dist/tools/fuzzySearch.d.ts +22 -0
- package/dist/tools/fuzzySearch.js +113 -0
- package/dist/tools/pdf-reader.d.ts +13 -0
- package/dist/tools/pdf-reader.js +214 -0
- package/dist/tools/schemas.d.ts +12 -3
- package/dist/tools/schemas.js +5 -2
- package/dist/tools/search.js +5 -4
- package/dist/utils/capture.d.ts +15 -0
- package/dist/utils/capture.js +175 -0
- package/dist/utils/withTimeout.d.ts +11 -0
- package/dist/utils/withTimeout.js +52 -0
- package/dist/utils.d.ts +10 -1
- package/dist/utils.js +99 -26
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
|
@@ -1,31 +1,35 @@
|
|
|
1
1
|
import { homedir, platform } from 'os';
|
|
2
2
|
import { join } from 'path';
|
|
3
|
-
import { readFileSync, writeFileSync, existsSync, appendFileSync } from 'fs';
|
|
3
|
+
import { readFileSync, writeFileSync, existsSync, appendFileSync, mkdirSync } from 'fs';
|
|
4
4
|
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
8
|
import * as https from 'https';
|
|
9
|
+
import { randomUUID } from 'crypto';
|
|
9
10
|
|
|
10
11
|
// Google Analytics configuration
|
|
11
12
|
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
|
|
13
|
+
const GA_API_SECRET = '5M0mC--2S_6t94m8WrI60A'; // Replace with your GA4 API Secre
|
|
13
14
|
const GA_BASE_URL = `https://www.google-analytics.com/mp/collect?measurement_id=${GA_MEASUREMENT_ID}&api_secret=${GA_API_SECRET}`;
|
|
14
15
|
|
|
15
|
-
//
|
|
16
|
+
// Generate a unique anonymous ID using UUID - consistent with privacy policy
|
|
16
17
|
let uniqueUserId = 'unknown';
|
|
17
18
|
|
|
18
19
|
try {
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// Get a unique user ID
|
|
23
|
-
uniqueUserId = machineIdModule.machineIdSync();
|
|
20
|
+
// Use randomUUID from crypto module instead of machine-id
|
|
21
|
+
// This generates a truly random identifier not tied to hardware
|
|
22
|
+
uniqueUserId = randomUUID();
|
|
24
23
|
} catch (error) {
|
|
25
|
-
// Fall back to a semi-unique identifier if
|
|
26
|
-
uniqueUserId =
|
|
24
|
+
// Fall back to a semi-unique identifier if UUID generation fails
|
|
25
|
+
uniqueUserId = `random-${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
|
|
27
26
|
}
|
|
28
27
|
|
|
28
|
+
// Setup tracking
|
|
29
|
+
let setupSteps = []; // Track setup progress
|
|
30
|
+
let setupStartTime = Date.now();
|
|
31
|
+
|
|
32
|
+
|
|
29
33
|
// Function to get npm version
|
|
30
34
|
async function getNpmVersion() {
|
|
31
35
|
try {
|
|
@@ -52,7 +56,7 @@ const getVersion = async () => {
|
|
|
52
56
|
}
|
|
53
57
|
};
|
|
54
58
|
|
|
55
|
-
// Function to detect shell
|
|
59
|
+
// Function to detect shell environmen
|
|
56
60
|
function detectShell() {
|
|
57
61
|
// Check for Windows shells
|
|
58
62
|
if (process.platform === 'win32') {
|
|
@@ -62,7 +66,7 @@ function detectShell() {
|
|
|
62
66
|
if (process.env.TERM?.includes('xterm')) return 'xterm-on-windows';
|
|
63
67
|
if (process.env.ComSpec?.toLowerCase().includes('powershell')) return 'powershell';
|
|
64
68
|
if (process.env.PROMPT) return 'cmd';
|
|
65
|
-
|
|
69
|
+
|
|
66
70
|
// WSL detection
|
|
67
71
|
if (process.env.WSL_DISTRO_NAME || process.env.WSLENV) {
|
|
68
72
|
return `wsl-${process.env.WSL_DISTRO_NAME || 'unknown'}`;
|
|
@@ -70,7 +74,7 @@ function detectShell() {
|
|
|
70
74
|
|
|
71
75
|
return 'windows-unknown';
|
|
72
76
|
}
|
|
73
|
-
|
|
77
|
+
|
|
74
78
|
// Unix-based shells
|
|
75
79
|
if (process.env.SHELL) {
|
|
76
80
|
const shellPath = process.env.SHELL.toLowerCase();
|
|
@@ -82,30 +86,30 @@ function detectShell() {
|
|
|
82
86
|
if (shellPath.includes('dash')) return 'dash';
|
|
83
87
|
return `other-unix-${shellPath.split('/').pop()}`;
|
|
84
88
|
}
|
|
85
|
-
|
|
89
|
+
|
|
86
90
|
// Terminal emulators and IDE terminals
|
|
87
91
|
if (process.env.TERM_PROGRAM) {
|
|
88
92
|
return process.env.TERM_PROGRAM.toLowerCase();
|
|
89
93
|
}
|
|
90
|
-
|
|
94
|
+
|
|
91
95
|
return 'unknown-shell';
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
// Function to determine execution context
|
|
95
99
|
function getExecutionContext() {
|
|
96
100
|
// Check if running from npx
|
|
97
|
-
const isNpx = process.env.npm_lifecycle_event === 'npx' ||
|
|
101
|
+
const isNpx = process.env.npm_lifecycle_event === 'npx' ||
|
|
98
102
|
process.env.npm_execpath?.includes('npx') ||
|
|
99
103
|
process.env._?.includes('npx') ||
|
|
100
104
|
import.meta.url.includes('node_modules');
|
|
101
|
-
|
|
105
|
+
|
|
102
106
|
// Check if installed globally
|
|
103
107
|
const isGlobal = process.env.npm_config_global === 'true' ||
|
|
104
108
|
process.argv[1]?.includes('node_modules/.bin');
|
|
105
|
-
|
|
109
|
+
|
|
106
110
|
// Check if it's run from a script in package.json
|
|
107
111
|
const isNpmScript = !!process.env.npm_lifecycle_script;
|
|
108
|
-
|
|
112
|
+
|
|
109
113
|
return {
|
|
110
114
|
runMethod: isNpx ? 'npx' : (isGlobal ? 'global' : (isNpmScript ? 'npm_script' : 'direct')),
|
|
111
115
|
isCI: !!process.env.CI || !!process.env.GITHUB_ACTIONS || !!process.env.TRAVIS || !!process.env.CIRCLECI,
|
|
@@ -115,135 +119,170 @@ function getExecutionContext() {
|
|
|
115
119
|
|
|
116
120
|
// Helper function to get standard environment properties for tracking
|
|
117
121
|
let npmVersionCache = null;
|
|
118
|
-
async function getTrackingProperties(additionalProps = {}) {
|
|
119
|
-
if (npmVersionCache === null) {
|
|
120
|
-
npmVersionCache = await getNpmVersion();
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const context = getExecutionContext();
|
|
124
|
-
const version = await getVersion();
|
|
125
|
-
return {
|
|
126
|
-
platform: platform(),
|
|
127
|
-
node_version: nodeVersion,
|
|
128
|
-
npm_version: npmVersionCache,
|
|
129
|
-
execution_context: context.runMethod,
|
|
130
|
-
is_ci: context.isCI,
|
|
131
|
-
shell: context.shell,
|
|
132
|
-
app_version: version,
|
|
133
|
-
engagement_time_msec: "100",
|
|
134
|
-
...additionalProps
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
122
|
|
|
138
|
-
//
|
|
139
|
-
async function
|
|
140
|
-
|
|
141
|
-
|
|
123
|
+
// Enhanced version with step tracking - will replace the original after initialization
|
|
124
|
+
async function enhancedGetTrackingProperties(additionalProps = {}) {
|
|
125
|
+
const propertiesStep = addSetupStep('get_tracking_properties');
|
|
142
126
|
try {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
127
|
+
if (npmVersionCache === null) {
|
|
128
|
+
npmVersionCache = await getNpmVersion();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const context = getExecutionContext();
|
|
132
|
+
const version = await getVersion();
|
|
133
|
+
|
|
134
|
+
updateSetupStep(propertiesStep, 'completed');
|
|
135
|
+
return {
|
|
136
|
+
platform: platform(),
|
|
137
|
+
node_version: nodeVersion,
|
|
138
|
+
npm_version: npmVersionCache,
|
|
139
|
+
execution_context: context.runMethod,
|
|
140
|
+
is_ci: context.isCI,
|
|
141
|
+
shell: context.shell,
|
|
142
|
+
app_version: version,
|
|
143
|
+
engagement_time_msec: "100",
|
|
144
|
+
...additionalProps
|
|
155
145
|
};
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
'Content-Length': Buffer.byteLength(postData)
|
|
165
|
-
}
|
|
146
|
+
} catch (error) {
|
|
147
|
+
updateSetupStep(propertiesStep, 'failed', error);
|
|
148
|
+
return {
|
|
149
|
+
platform: platform(),
|
|
150
|
+
node_version: nodeVersion,
|
|
151
|
+
error: error.message,
|
|
152
|
+
engagement_time_msec: "100",
|
|
153
|
+
...additionalProps
|
|
166
154
|
};
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
202
157
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
158
|
+
// Enhanced tracking function with retries and better error handling
|
|
159
|
+
// This replaces the basic implementation for all tracking after initialization
|
|
160
|
+
async function trackEvent(eventName, additionalProps = {}) {
|
|
161
|
+
const trackingStep = addSetupStep(`track_event_${eventName}`);
|
|
162
|
+
|
|
163
|
+
if (!GA_MEASUREMENT_ID || !GA_API_SECRET) {
|
|
164
|
+
updateSetupStep(trackingStep, 'skipped', new Error('GA not configured'));
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Add retry capability
|
|
169
|
+
const maxRetries = 2;
|
|
170
|
+
let attempt = 0;
|
|
171
|
+
let lastError = null;
|
|
172
|
+
|
|
173
|
+
while (attempt <= maxRetries) {
|
|
174
|
+
try {
|
|
175
|
+
attempt++;
|
|
176
|
+
|
|
177
|
+
// Get enriched properties
|
|
178
|
+
const eventProperties = await enhancedGetTrackingProperties(additionalProps);
|
|
179
|
+
|
|
180
|
+
// Prepare GA4 payload
|
|
181
|
+
const payload = {
|
|
182
|
+
client_id: uniqueUserId,
|
|
183
|
+
non_personalized_ads: false,
|
|
184
|
+
timestamp_micros: Date.now() * 1000,
|
|
185
|
+
events: [{
|
|
186
|
+
name: eventName,
|
|
187
|
+
params: eventProperties
|
|
188
|
+
}]
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// Send to Google Analytics
|
|
192
|
+
const postData = JSON.stringify(payload);
|
|
193
|
+
|
|
194
|
+
const options = {
|
|
195
|
+
method: 'POST',
|
|
196
|
+
headers: {
|
|
197
|
+
'Content-Type': 'application/json',
|
|
198
|
+
'Content-Length': Buffer.byteLength(postData)
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const result = await new Promise((resolve, reject) => {
|
|
203
|
+
const req = https.request(GA_BASE_URL, options);
|
|
204
|
+
|
|
205
|
+
// Set timeout to prevent blocking
|
|
206
|
+
const timeoutId = setTimeout(() => {
|
|
207
|
+
req.destroy();
|
|
208
|
+
reject(new Error('Request timeout'));
|
|
209
|
+
}, 5000); // Increased timeout to 5 seconds
|
|
210
|
+
|
|
211
|
+
req.on('error', (error) => {
|
|
212
|
+
clearTimeout(timeoutId);
|
|
213
|
+
reject(error);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
req.on('response', (res) => {
|
|
217
|
+
clearTimeout(timeoutId);
|
|
218
|
+
let data = '';
|
|
219
|
+
|
|
220
|
+
res.on('data', (chunk) => {
|
|
221
|
+
data += chunk;
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
res.on('error', (error) => {
|
|
225
|
+
reject(error);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
res.on('end', () => {
|
|
229
|
+
if (res.statusCode >= 200 && res.statusCode < 300) {
|
|
230
|
+
resolve({ success: true, data });
|
|
231
|
+
} else {
|
|
232
|
+
reject(new Error(`HTTP error ${res.statusCode}: ${data}`));
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
req.write(postData);
|
|
238
|
+
req.end();
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
updateSetupStep(trackingStep, 'completed');
|
|
242
|
+
return result;
|
|
243
|
+
|
|
244
|
+
} catch (error) {
|
|
245
|
+
lastError = error;
|
|
246
|
+
logToFile(`Error tracking event ${eventName} (attempt ${attempt}/${maxRetries + 1}): ${error}`, true);
|
|
247
|
+
|
|
248
|
+
if (attempt <= maxRetries) {
|
|
249
|
+
// Wait before retry (exponential backoff)
|
|
250
|
+
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// All retries failed
|
|
256
|
+
updateSetupStep(trackingStep, 'failed', lastError);
|
|
216
257
|
return false;
|
|
217
|
-
}
|
|
218
258
|
}
|
|
219
|
-
|
|
220
|
-
|
|
259
|
+
|
|
260
|
+
// Ensure tracking completes before process exits
|
|
261
|
+
async function ensureTrackingCompleted(eventName, additionalProps = {}, timeoutMs = 6000) {
|
|
262
|
+
return new Promise(async (resolve) => {
|
|
263
|
+
const timeoutId = setTimeout(() => {
|
|
264
|
+
logToFile(`Tracking timeout for ${eventName}`, true);
|
|
265
|
+
resolve(false);
|
|
266
|
+
}, timeoutMs);
|
|
267
|
+
|
|
268
|
+
try {
|
|
269
|
+
await trackEvent(eventName, additionalProps);
|
|
270
|
+
clearTimeout(timeoutId);
|
|
271
|
+
resolve(true);
|
|
272
|
+
} catch (error) {
|
|
273
|
+
clearTimeout(timeoutId);
|
|
274
|
+
logToFile(`Failed to complete tracking for ${eventName}: ${error}`, true);
|
|
275
|
+
resolve(false);
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
221
280
|
|
|
222
281
|
// Fix for Windows ESM path resolution
|
|
223
282
|
const __filename = fileURLToPath(import.meta.url);
|
|
224
283
|
const __dirname = dirname(__filename);
|
|
225
284
|
|
|
226
|
-
//
|
|
227
|
-
const os = platform();
|
|
228
|
-
const isWindows = os === 'win32'; // Define isWindows variable
|
|
229
|
-
let claudeConfigPath;
|
|
230
|
-
|
|
231
|
-
switch (os) {
|
|
232
|
-
case 'win32':
|
|
233
|
-
claudeConfigPath = join(process.env.APPDATA, 'Claude', 'claude_desktop_config.json');
|
|
234
|
-
break;
|
|
235
|
-
case 'darwin':
|
|
236
|
-
claudeConfigPath = join(homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
237
|
-
break;
|
|
238
|
-
case 'linux':
|
|
239
|
-
claudeConfigPath = join(homedir(), '.config', 'Claude', 'claude_desktop_config.json');
|
|
240
|
-
break;
|
|
241
|
-
default:
|
|
242
|
-
// Fallback for other platforms
|
|
243
|
-
claudeConfigPath = join(homedir(), '.claude_desktop_config.json');
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Setup logging
|
|
285
|
+
// Setup logging early to capture everything
|
|
247
286
|
const LOG_FILE = join(__dirname, 'setup.log');
|
|
248
287
|
|
|
249
288
|
function logToFile(message, isError = false) {
|
|
@@ -251,7 +290,7 @@ function logToFile(message, isError = false) {
|
|
|
251
290
|
const logMessage = `${timestamp} - ${isError ? 'ERROR: ' : ''}${message}\n`;
|
|
252
291
|
try {
|
|
253
292
|
appendFileSync(LOG_FILE, logMessage);
|
|
254
|
-
// For setup script, we'll still output to console but in JSON
|
|
293
|
+
// For setup script, we'll still output to console but in JSON forma
|
|
255
294
|
const jsonOutput = {
|
|
256
295
|
type: isError ? 'error' : 'info',
|
|
257
296
|
timestamp,
|
|
@@ -268,227 +307,449 @@ function logToFile(message, isError = false) {
|
|
|
268
307
|
}
|
|
269
308
|
}
|
|
270
309
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
310
|
+
// Setup global error handlers
|
|
311
|
+
process.on('uncaughtException', async (error) => {
|
|
312
|
+
logToFile(`Uncaught exception: ${error.stack || error.message}`, true);
|
|
313
|
+
await trackEvent('npx_setup_uncaught_exception', { error: error.message });
|
|
314
|
+
process.exit(1);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
process.on('unhandledRejection', async (reason, promise) => {
|
|
318
|
+
logToFile(`Unhandled rejection at: ${promise}, reason: ${reason}`, true);
|
|
319
|
+
await trackEvent('npx_setup_unhandled_rejection', { error: String(reason) });
|
|
320
|
+
process.exit(1);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// Track when the process is about to exi
|
|
324
|
+
let isExiting = false;
|
|
325
|
+
process.on('exit', () => {
|
|
326
|
+
if (!isExiting) {
|
|
327
|
+
isExiting = true;
|
|
328
|
+
// Synchronous tracking for exit handler
|
|
329
|
+
logToFile('Process is exiting. Some tracking events may not be sent.');
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
// Function to check for debug mode argument
|
|
335
|
+
function isDebugMode() {
|
|
336
|
+
return process.argv.includes('--debug');
|
|
337
|
+
}
|
|
277
338
|
|
|
278
|
-
|
|
339
|
+
// Initial tracking - ensure it completes before continuing
|
|
340
|
+
await ensureTrackingCompleted('npx_setup_start', {
|
|
341
|
+
argv: process.argv.join(' '),
|
|
342
|
+
start_time: new Date().toISOString()
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// Determine OS and set appropriate config path
|
|
346
|
+
const os = platform();
|
|
347
|
+
const isWindows = os === 'win32';
|
|
348
|
+
let claudeConfigPath;
|
|
349
|
+
|
|
350
|
+
switch (os) {
|
|
351
|
+
case 'win32':
|
|
352
|
+
claudeConfigPath = join(process.env.APPDATA, 'Claude', 'claude_desktop_config.json');
|
|
353
|
+
break;
|
|
354
|
+
case 'darwin':
|
|
355
|
+
claudeConfigPath = join(homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
356
|
+
break;
|
|
357
|
+
case 'linux':
|
|
358
|
+
claudeConfigPath = join(homedir(), '.config', 'Claude', 'claude_desktop_config.json');
|
|
359
|
+
break;
|
|
360
|
+
default:
|
|
361
|
+
// Fallback for other platforms
|
|
362
|
+
claudeConfigPath = join(homedir(), '.claude_desktop_config.json');
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
// Tracking step functions
|
|
368
|
+
function addSetupStep(step, status = 'started', error = null) {
|
|
369
|
+
const timestamp = Date.now();
|
|
370
|
+
setupSteps.push({
|
|
371
|
+
step,
|
|
372
|
+
status,
|
|
373
|
+
timestamp,
|
|
374
|
+
timeFromStart: timestamp - setupStartTime,
|
|
375
|
+
error: error ? error.message || String(error) : null
|
|
376
|
+
});
|
|
377
|
+
return setupSteps.length - 1; // Return the index for later updates
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function updateSetupStep(index, status, error = null) {
|
|
381
|
+
if (setupSteps[index]) {
|
|
382
|
+
const timestamp = Date.now();
|
|
383
|
+
setupSteps[index].status = status;
|
|
384
|
+
setupSteps[index].completionTime = timestamp;
|
|
385
|
+
setupSteps[index].timeFromStart = timestamp - setupStartTime;
|
|
279
386
|
if (error) {
|
|
280
|
-
|
|
281
|
-
return;
|
|
387
|
+
setupSteps[index].error = error.message || String(error);
|
|
282
388
|
}
|
|
283
|
-
|
|
284
|
-
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
try {
|
|
393
|
+
// Only dependency is node-machine-id
|
|
394
|
+
const machineIdInitStep = addSetupStep('initialize_machine_id');
|
|
395
|
+
try {
|
|
396
|
+
const machineIdModule = await import('node-machine-id');
|
|
397
|
+
// Get a unique user ID
|
|
398
|
+
uniqueUserId = machineIdModule.machineIdSync();
|
|
399
|
+
updateSetupStep(machineIdInitStep, 'completed');
|
|
400
|
+
} catch (error) {
|
|
401
|
+
// Fall back to a semi-unique identifier if machine-id is not available
|
|
402
|
+
uniqueUserId = `${platform()}-${process.env.USER || process.env.USERNAME || 'unknown'}-${Date.now()}`;
|
|
403
|
+
updateSetupStep(machineIdInitStep, 'fallback', error);
|
|
404
|
+
}
|
|
405
|
+
} catch (error) {
|
|
406
|
+
logToFile(`Error initializing user ID: ${error}`, true);
|
|
407
|
+
addSetupStep('initialize_machine_id', 'failed', error);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
async function execAsync(command) {
|
|
414
|
+
const execStep = addSetupStep(`exec_${command.substring(0, 20)}...`);
|
|
415
|
+
return new Promise((resolve, reject) => {
|
|
416
|
+
// Use PowerShell on Windows for better Unicode support and consistency
|
|
417
|
+
const actualCommand = isWindows
|
|
418
|
+
? `cmd.exe /c ${command}`
|
|
419
|
+
: command;
|
|
420
|
+
|
|
421
|
+
exec(actualCommand, { timeout: 10000 }, (error, stdout, stderr) => {
|
|
422
|
+
if (error) {
|
|
423
|
+
updateSetupStep(execStep, 'failed', error);
|
|
424
|
+
reject(error);
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
updateSetupStep(execStep, 'completed');
|
|
428
|
+
resolve({ stdout, stderr });
|
|
429
|
+
});
|
|
285
430
|
});
|
|
286
431
|
}
|
|
287
432
|
|
|
288
433
|
async function restartClaude() {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
//
|
|
293
|
-
|
|
434
|
+
const restartStep = addSetupStep('restart_claude');
|
|
435
|
+
try {
|
|
436
|
+
const platform = process.platform;
|
|
437
|
+
// Track restart attempt
|
|
438
|
+
await trackEvent('npx_setup_restart_claude_attempt', { platform });
|
|
439
|
+
|
|
440
|
+
// Try to kill Claude process first
|
|
441
|
+
const killStep = addSetupStep('kill_claude_process');
|
|
442
|
+
try {
|
|
294
443
|
switch (platform) {
|
|
295
444
|
case "win32":
|
|
296
|
-
|
|
297
445
|
await execAsync(
|
|
298
446
|
`taskkill /F /IM "Claude.exe"`,
|
|
299
|
-
)
|
|
447
|
+
);
|
|
300
448
|
break;
|
|
301
449
|
case "darwin":
|
|
302
450
|
await execAsync(
|
|
303
451
|
`killall "Claude"`,
|
|
304
|
-
)
|
|
452
|
+
);
|
|
305
453
|
break;
|
|
306
454
|
case "linux":
|
|
307
455
|
await execAsync(
|
|
308
456
|
`pkill -f "claude"`,
|
|
309
|
-
)
|
|
457
|
+
);
|
|
310
458
|
break;
|
|
311
459
|
}
|
|
312
|
-
|
|
313
|
-
|
|
460
|
+
updateSetupStep(killStep, 'completed');
|
|
461
|
+
await trackEvent('npx_setup_kill_claude_success', { platform });
|
|
462
|
+
} catch (killError) {
|
|
463
|
+
// It's okay if Claude isn't running - update step but continue
|
|
464
|
+
updateSetupStep(killStep, 'no_process_found', killError);
|
|
465
|
+
await trackEvent('npx_setup_kill_claude_not_needed', { platform });
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Wait a bit to ensure process termination
|
|
469
|
+
await new Promise((resolve) => setTimeout(resolve, 3000));
|
|
470
|
+
|
|
471
|
+
// Try to start Claude
|
|
472
|
+
const startStep = addSetupStep('start_claude_process');
|
|
314
473
|
try {
|
|
315
474
|
if (platform === "win32") {
|
|
316
|
-
// it
|
|
317
|
-
|
|
475
|
+
// Windows - note it won't actually start Claude
|
|
476
|
+
logToFile("Windows: Claude restart skipped - requires manual restart");
|
|
477
|
+
updateSetupStep(startStep, 'skipped');
|
|
478
|
+
await trackEvent('npx_setup_start_claude_skipped', { platform });
|
|
318
479
|
} else if (platform === "darwin") {
|
|
319
|
-
await execAsync(`open -a "Claude"`)
|
|
480
|
+
await execAsync(`open -a "Claude"`);
|
|
481
|
+
updateSetupStep(startStep, 'completed');
|
|
482
|
+
await trackEvent('npx_setup_start_claude_success', { platform });
|
|
320
483
|
} else if (platform === "linux") {
|
|
321
|
-
await execAsync(`claude`)
|
|
484
|
+
await execAsync(`claude`);
|
|
485
|
+
updateSetupStep(startStep, 'completed');
|
|
486
|
+
await trackEvent('npx_setup_start_claude_success', { platform });
|
|
322
487
|
}
|
|
323
|
-
logToFile(`Claude has been restarted.`)
|
|
324
|
-
} catch{
|
|
325
488
|
|
|
489
|
+
logToFile(`Claude has been restarted.`);
|
|
490
|
+
updateSetupStep(restartStep, 'completed');
|
|
491
|
+
await trackEvent('npx_setup_restart_claude_success', { platform });
|
|
492
|
+
} catch (startError) {
|
|
493
|
+
updateSetupStep(startStep, 'failed', startError);
|
|
494
|
+
await trackEvent('npx_setup_start_claude_error', {
|
|
495
|
+
platform,
|
|
496
|
+
error: startError.message
|
|
497
|
+
});
|
|
498
|
+
throw startError; // Re-throw to handle in the outer catch
|
|
326
499
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
} catch (error) {
|
|
500
|
+
} catch (error) {
|
|
501
|
+
updateSetupStep(restartStep, 'failed', error);
|
|
330
502
|
await trackEvent('npx_setup_restart_claude_error', { error: error.message });
|
|
331
|
-
|
|
332
|
-
logToFile(`If Claude Desktop is not installed use this link to download https://claude.ai/download`, true)
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Check if config file exists and create default if not
|
|
337
|
-
if (!existsSync(claudeConfigPath)) {
|
|
338
|
-
logToFile(`Claude config file not found at: ${claudeConfigPath}`);
|
|
339
|
-
logToFile('Creating default config file...');
|
|
340
|
-
|
|
341
|
-
// Track new installation
|
|
342
|
-
await trackEvent('npx_setup_create_default_config');
|
|
343
|
-
|
|
344
|
-
// Create the directory if it doesn't exist
|
|
345
|
-
const configDir = dirname(claudeConfigPath);
|
|
346
|
-
if (!existsSync(configDir)) {
|
|
347
|
-
import('fs').then(fs => fs.mkdirSync(configDir, { recursive: true }));
|
|
503
|
+
logToFile(`Failed to restart Claude: ${error}. Please restart it manually.`, true);
|
|
504
|
+
logToFile(`If Claude Desktop is not installed use this link to download https://claude.ai/download`, true);
|
|
348
505
|
}
|
|
349
|
-
|
|
350
|
-
// Create default config with shell based on platform
|
|
351
|
-
const defaultConfig = {
|
|
352
|
-
"serverConfig": isWindows
|
|
353
|
-
? {
|
|
354
|
-
"command": "cmd.exe",
|
|
355
|
-
"args": ["/c"]
|
|
356
|
-
}
|
|
357
|
-
: {
|
|
358
|
-
"command": "/bin/sh",
|
|
359
|
-
"args": ["-c"]
|
|
360
|
-
}
|
|
361
|
-
};
|
|
362
|
-
|
|
363
|
-
writeFileSync(claudeConfigPath, JSON.stringify(defaultConfig, null, 2));
|
|
364
|
-
logToFile('Default config file created. Please update it with your Claude API credentials.');
|
|
365
506
|
}
|
|
366
507
|
|
|
367
|
-
// Function to check for debug mode argument
|
|
368
|
-
function isDebugMode() {
|
|
369
|
-
return process.argv.includes('--debug');
|
|
370
|
-
}
|
|
371
508
|
|
|
372
509
|
// Main function to export for ESM compatibility
|
|
373
510
|
export default async function setup() {
|
|
511
|
+
// Add tracking for setup function entry
|
|
512
|
+
await trackEvent('npx_setup_function_started');
|
|
513
|
+
|
|
514
|
+
const setupStep = addSetupStep('main_setup');
|
|
374
515
|
const debugMode = isDebugMode();
|
|
516
|
+
|
|
375
517
|
if (debugMode) {
|
|
376
518
|
logToFile('Debug mode enabled. Will configure with Node.js inspector options.');
|
|
519
|
+
await trackEvent('npx_setup_debug_mode', { enabled: true });
|
|
377
520
|
}
|
|
521
|
+
|
|
378
522
|
try {
|
|
379
|
-
//
|
|
380
|
-
const
|
|
381
|
-
const
|
|
523
|
+
// Check if config directory exists and create it if necessary
|
|
524
|
+
const configDirStep = addSetupStep('check_config_directory');
|
|
525
|
+
const configDir = dirname(claudeConfigPath);
|
|
526
|
+
|
|
527
|
+
try {
|
|
528
|
+
if (!existsSync(configDir)) {
|
|
529
|
+
logToFile(`Creating config directory: ${configDir}`);
|
|
530
|
+
mkdirSync(configDir, { recursive: true });
|
|
531
|
+
await trackEvent('npx_setup_create_config_dir', { path: configDir });
|
|
532
|
+
}
|
|
533
|
+
updateSetupStep(configDirStep, 'completed');
|
|
534
|
+
} catch (dirError) {
|
|
535
|
+
updateSetupStep(configDirStep, 'failed', dirError);
|
|
536
|
+
await trackEvent('npx_setup_create_config_dir_error', {
|
|
537
|
+
path: configDir,
|
|
538
|
+
error: dirError.message
|
|
539
|
+
});
|
|
540
|
+
throw new Error(`Failed to create config directory: ${dirError.message}`);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// Check if config file exists and create default if no
|
|
544
|
+
const configFileStep = addSetupStep('check_config_file');
|
|
545
|
+
let config;
|
|
546
|
+
|
|
547
|
+
if (!existsSync(claudeConfigPath)) {
|
|
548
|
+
logToFile(`Claude config file not found at: ${claudeConfigPath}`);
|
|
549
|
+
logToFile('Creating default config file...');
|
|
550
|
+
|
|
551
|
+
// Track new installation
|
|
552
|
+
await trackEvent('npx_setup_create_default_config');
|
|
553
|
+
|
|
554
|
+
// Create default config with shell based on platform
|
|
555
|
+
const defaultConfig = {
|
|
556
|
+
"serverConfig": isWindows
|
|
557
|
+
? {
|
|
558
|
+
"command": "cmd.exe",
|
|
559
|
+
"args": ["/c"]
|
|
560
|
+
}
|
|
561
|
+
: {
|
|
562
|
+
"command": "/bin/sh",
|
|
563
|
+
"args": ["-c"]
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
try {
|
|
568
|
+
writeFileSync(claudeConfigPath, JSON.stringify(defaultConfig, null, 2));
|
|
569
|
+
logToFile('Default config file created.');
|
|
570
|
+
config = defaultConfig;
|
|
571
|
+
updateSetupStep(configFileStep, 'created');
|
|
572
|
+
await trackEvent('npx_setup_config_file_created');
|
|
573
|
+
} catch (writeError) {
|
|
574
|
+
updateSetupStep(configFileStep, 'create_failed', writeError);
|
|
575
|
+
await trackEvent('npx_setup_config_file_create_error', { error: writeError.message });
|
|
576
|
+
throw new Error(`Failed to create config file: ${writeError.message}`);
|
|
577
|
+
}
|
|
578
|
+
} else {
|
|
579
|
+
// Read existing config
|
|
580
|
+
const readConfigStep = addSetupStep('read_config_file');
|
|
581
|
+
try {
|
|
582
|
+
const configData = readFileSync(claudeConfigPath, 'utf8');
|
|
583
|
+
config = JSON.parse(configData);
|
|
584
|
+
updateSetupStep(readConfigStep, 'completed');
|
|
585
|
+
updateSetupStep(configFileStep, 'exists');
|
|
586
|
+
await trackEvent('npx_setup_config_file_read');
|
|
587
|
+
} catch (readError) {
|
|
588
|
+
updateSetupStep(readConfigStep, 'failed', readError);
|
|
589
|
+
await trackEvent('npx_setup_config_file_read_error', { error: readError.message });
|
|
590
|
+
throw new Error(`Failed to read config file: ${readError.message}`);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
382
593
|
|
|
383
594
|
// Prepare the new server config based on OS
|
|
595
|
+
const configPrepStep = addSetupStep('prepare_server_config');
|
|
596
|
+
|
|
384
597
|
// Determine if running through npx or locally
|
|
385
598
|
const isNpx = import.meta.url.includes('node_modules');
|
|
599
|
+
await trackEvent('npx_setup_execution_mode', { isNpx });
|
|
386
600
|
|
|
387
601
|
// Fix Windows path handling for npx execution
|
|
388
602
|
let serverConfig;
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
"
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
"
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
serverConfig = {
|
|
435
|
-
"command": isWindows ? "npx.cmd" : "npx",
|
|
436
|
-
"args": [
|
|
437
|
-
"@wonderwhy-er/desktop-commander@latest"
|
|
438
|
-
]
|
|
439
|
-
};
|
|
603
|
+
|
|
604
|
+
try {
|
|
605
|
+
if (debugMode) {
|
|
606
|
+
// Use Node.js with inspector flag for debugging
|
|
607
|
+
if (isNpx) {
|
|
608
|
+
// Debug with npx
|
|
609
|
+
logToFile('Setting up debug configuration with npx. The process will pause on start until a debugger connects.');
|
|
610
|
+
// Add environment variables to help with debugging
|
|
611
|
+
const debugEnv = {
|
|
612
|
+
"NODE_OPTIONS": "--trace-warnings --trace-exit",
|
|
613
|
+
"DEBUG": "*"
|
|
614
|
+
};
|
|
615
|
+
|
|
616
|
+
serverConfig = {
|
|
617
|
+
"command": isWindows ? "node.exe" : "node",
|
|
618
|
+
"args": [
|
|
619
|
+
"--inspect-brk=9229",
|
|
620
|
+
isWindows ?
|
|
621
|
+
join(process.env.APPDATA || '', "npm", "npx.cmd").replace(/\\/g, '\\\\') :
|
|
622
|
+
"$(which npx)",
|
|
623
|
+
"@wonderwhy-er/desktop-commander@latest"
|
|
624
|
+
],
|
|
625
|
+
"env": debugEnv
|
|
626
|
+
};
|
|
627
|
+
await trackEvent('npx_setup_config_debug_npx');
|
|
628
|
+
} else {
|
|
629
|
+
// Debug with local installation path
|
|
630
|
+
const indexPath = join(__dirname, 'dist', 'index.js');
|
|
631
|
+
logToFile('Setting up debug configuration with local path. The process will pause on start until a debugger connects.');
|
|
632
|
+
// Add environment variables to help with debugging
|
|
633
|
+
const debugEnv = {
|
|
634
|
+
"NODE_OPTIONS": "--trace-warnings --trace-exit",
|
|
635
|
+
"DEBUG": "*"
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
serverConfig = {
|
|
639
|
+
"command": isWindows ? "node.exe" : "node",
|
|
640
|
+
"args": [
|
|
641
|
+
"--inspect-brk=9229",
|
|
642
|
+
indexPath.replace(/\\/g, '\\\\') // Double escape backslashes for JSON
|
|
643
|
+
],
|
|
644
|
+
"env": debugEnv
|
|
645
|
+
};
|
|
646
|
+
await trackEvent('npx_setup_config_debug_local');
|
|
647
|
+
}
|
|
440
648
|
} else {
|
|
441
|
-
//
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
649
|
+
// Standard configuration without debug
|
|
650
|
+
if (isNpx) {
|
|
651
|
+
serverConfig = {
|
|
652
|
+
"command": isWindows ? "npx.cmd" : "npx",
|
|
653
|
+
"args": [
|
|
654
|
+
"@wonderwhy-er/desktop-commander@latest"
|
|
655
|
+
]
|
|
656
|
+
};
|
|
657
|
+
await trackEvent('npx_setup_config_standard_npx');
|
|
658
|
+
} else {
|
|
659
|
+
// For local installation, use absolute path to handle Windows properly
|
|
660
|
+
const indexPath = join(__dirname, 'dist', 'index.js');
|
|
661
|
+
serverConfig = {
|
|
662
|
+
"command": "node",
|
|
663
|
+
"args": [
|
|
664
|
+
indexPath.replace(/\\/g, '\\\\') // Double escape backslashes for JSON
|
|
665
|
+
]
|
|
666
|
+
};
|
|
667
|
+
await trackEvent('npx_setup_config_standard_local');
|
|
668
|
+
}
|
|
449
669
|
}
|
|
670
|
+
updateSetupStep(configPrepStep, 'completed');
|
|
671
|
+
} catch (prepError) {
|
|
672
|
+
updateSetupStep(configPrepStep, 'failed', prepError);
|
|
673
|
+
await trackEvent('npx_setup_config_prep_error', { error: prepError.message });
|
|
674
|
+
throw new Error(`Failed to prepare server config: ${prepError.message}`);
|
|
450
675
|
}
|
|
451
676
|
|
|
452
|
-
//
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
677
|
+
// Update the config
|
|
678
|
+
const updateConfigStep = addSetupStep('update_config');
|
|
679
|
+
try {
|
|
680
|
+
// Initialize mcpServers if it doesn't exist
|
|
681
|
+
if (!config.mcpServers) {
|
|
682
|
+
config.mcpServers = {};
|
|
683
|
+
}
|
|
456
684
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
685
|
+
// Check if the old "desktopCommander" exists and remove i
|
|
686
|
+
if (config.mcpServers.desktopCommander) {
|
|
687
|
+
logToFile('Found old "desktopCommander" installation. Removing it...');
|
|
688
|
+
delete config.mcpServers.desktopCommander;
|
|
689
|
+
await trackEvent('npx_setup_remove_old_config');
|
|
690
|
+
}
|
|
462
691
|
|
|
463
|
-
|
|
464
|
-
|
|
692
|
+
// Add or update the terminal server config with the proper name "desktop-commander"
|
|
693
|
+
config.mcpServers["desktop-commander"] = serverConfig;
|
|
694
|
+
|
|
695
|
+
// Write the updated config back
|
|
696
|
+
writeFileSync(claudeConfigPath, JSON.stringify(config, null, 2), 'utf8');
|
|
697
|
+
updateSetupStep(updateConfigStep, 'completed');
|
|
698
|
+
await trackEvent('npx_setup_update_config');
|
|
699
|
+
} catch (updateError) {
|
|
700
|
+
updateSetupStep(updateConfigStep, 'failed', updateError);
|
|
701
|
+
await trackEvent('npx_setup_update_config_error', { error: updateError.message });
|
|
702
|
+
throw new Error(`Failed to update config: ${updateError.message}`);
|
|
703
|
+
}
|
|
465
704
|
|
|
466
|
-
// Write the updated config back
|
|
467
|
-
writeFileSync(claudeConfigPath, JSON.stringify(config, null, 2), 'utf8');
|
|
468
|
-
await trackEvent('npx_setup_update_config');
|
|
469
705
|
logToFile('Successfully added MCP server to Claude configuration!');
|
|
470
706
|
logToFile(`Configuration location: ${claudeConfigPath}`);
|
|
471
|
-
|
|
707
|
+
|
|
472
708
|
if (debugMode) {
|
|
473
709
|
logToFile('\nTo use the debug server:\n1. Restart Claude if it\'s currently running\n2. The server will be available as "desktop-commander-debug" in Claude\'s MCP server list\n3. Connect your debugger to port 9229');
|
|
474
710
|
} else {
|
|
475
711
|
logToFile('\nTo use the server:\n1. Restart Claude if it\'s currently running\n2. The server will be available as "desktop-commander" in Claude\'s MCP server list');
|
|
476
712
|
}
|
|
477
713
|
|
|
714
|
+
// Try to restart Claude
|
|
478
715
|
await restartClaude();
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
716
|
+
|
|
717
|
+
// Mark the main setup as completed
|
|
718
|
+
updateSetupStep(setupStep, 'completed');
|
|
719
|
+
|
|
720
|
+
// Ensure final tracking event is sent before exi
|
|
721
|
+
await ensureTrackingCompleted('npx_setup_complete', {
|
|
722
|
+
total_steps: setupSteps.length,
|
|
723
|
+
total_time_ms: Date.now() - setupStartTime
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
return true;
|
|
482
727
|
} catch (error) {
|
|
483
|
-
|
|
728
|
+
updateSetupStep(setupStep, 'failed', error);
|
|
729
|
+
// Send detailed info about the failure
|
|
730
|
+
await ensureTrackingCompleted('npx_setup_final_error', {
|
|
731
|
+
error: error.message,
|
|
732
|
+
error_stack: error.stack,
|
|
733
|
+
total_steps: setupSteps.length,
|
|
734
|
+
last_successful_step: setupSteps.filter(s => s.status === 'completed').pop()?.step || 'none'
|
|
735
|
+
});
|
|
736
|
+
|
|
484
737
|
logToFile(`Error updating Claude configuration: ${error}`, true);
|
|
485
|
-
|
|
738
|
+
return false;
|
|
486
739
|
}
|
|
487
740
|
}
|
|
488
741
|
|
|
489
742
|
// Allow direct execution
|
|
490
743
|
if (process.argv.length >= 2 && process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
491
|
-
setup().
|
|
744
|
+
setup().then(success => {
|
|
745
|
+
if (!success) {
|
|
746
|
+
process.exit(1);
|
|
747
|
+
}
|
|
748
|
+
}).catch(async error => {
|
|
749
|
+
await ensureTrackingCompleted('npx_setup_fatal_error', {
|
|
750
|
+
error: error.message,
|
|
751
|
+
error_stack: error.stack
|
|
752
|
+
});
|
|
492
753
|
logToFile(`Fatal error: ${error}`, true);
|
|
493
754
|
process.exit(1);
|
|
494
755
|
});
|