@wonderwhy-er/desktop-commander 0.1.34 → 0.1.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +2 -2
- package/README.md +186 -56
- package/dist/command-manager.d.ts +1 -7
- package/dist/command-manager.js +31 -50
- package/dist/config-manager.d.ts +28 -16
- package/dist/config-manager.js +124 -189
- package/dist/config.d.ts +2 -2
- package/dist/config.js +7 -4
- package/dist/error-handlers.js +4 -0
- package/dist/handlers/edit-search-handlers.d.ts +3 -1
- package/dist/handlers/edit-search-handlers.js +9 -19
- package/dist/handlers/filesystem-handlers.d.ts +0 -4
- package/dist/handlers/filesystem-handlers.js +11 -19
- package/dist/handlers/index.d.ts +0 -1
- package/dist/handlers/index.js +0 -1
- package/dist/index.js +19 -4
- 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/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 +181 -176
- package/dist/setup-claude-server.js +554 -244
- package/dist/terminal-manager.d.ts +1 -1
- package/dist/terminal-manager.js +22 -3
- 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 +8 -6
- package/dist/tools/edit.js +165 -35
- package/dist/tools/execute.js +6 -6
- 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 +157 -87
- 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 +29 -19
- package/dist/tools/schemas.js +15 -8
- 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 +15 -1
- package/dist/utils.js +174 -41
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -3
|
@@ -1,34 +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
|
+
import * as https from 'https';
|
|
9
|
+
import { randomUUID } from 'crypto';
|
|
8
10
|
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
+
// Google Analytics configuration
|
|
12
|
+
const GA_MEASUREMENT_ID = 'G-NGGDNL0K4L'; // Replace with your GA4 Measurement ID
|
|
13
|
+
const GA_API_SECRET = '5M0mC--2S_6t94m8WrI60A'; // Replace with your GA4 API Secre
|
|
14
|
+
const GA_BASE_URL = `https://www.google-analytics.com/mp/collect?measurement_id=${GA_MEASUREMENT_ID}&api_secret=${GA_API_SECRET}`;
|
|
15
|
+
|
|
16
|
+
// Generate a unique anonymous ID using UUID - consistent with privacy policy
|
|
11
17
|
let uniqueUserId = 'unknown';
|
|
12
18
|
|
|
13
19
|
try {
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
26
|
-
// Get a unique user ID
|
|
27
|
-
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();
|
|
28
23
|
} catch (error) {
|
|
29
|
-
//
|
|
24
|
+
// Fall back to a semi-unique identifier if UUID generation fails
|
|
25
|
+
uniqueUserId = `random-${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
|
|
30
26
|
}
|
|
31
27
|
|
|
28
|
+
// Setup tracking
|
|
29
|
+
let setupSteps = []; // Track setup progress
|
|
30
|
+
let setupStartTime = Date.now();
|
|
31
|
+
|
|
32
|
+
|
|
32
33
|
// Function to get npm version
|
|
33
34
|
async function getNpmVersion() {
|
|
34
35
|
try {
|
|
@@ -55,7 +56,7 @@ const getVersion = async () => {
|
|
|
55
56
|
}
|
|
56
57
|
};
|
|
57
58
|
|
|
58
|
-
// Function to detect shell
|
|
59
|
+
// Function to detect shell environmen
|
|
59
60
|
function detectShell() {
|
|
60
61
|
// Check for Windows shells
|
|
61
62
|
if (process.platform === 'win32') {
|
|
@@ -65,7 +66,7 @@ function detectShell() {
|
|
|
65
66
|
if (process.env.TERM?.includes('xterm')) return 'xterm-on-windows';
|
|
66
67
|
if (process.env.ComSpec?.toLowerCase().includes('powershell')) return 'powershell';
|
|
67
68
|
if (process.env.PROMPT) return 'cmd';
|
|
68
|
-
|
|
69
|
+
|
|
69
70
|
// WSL detection
|
|
70
71
|
if (process.env.WSL_DISTRO_NAME || process.env.WSLENV) {
|
|
71
72
|
return `wsl-${process.env.WSL_DISTRO_NAME || 'unknown'}`;
|
|
@@ -73,7 +74,7 @@ function detectShell() {
|
|
|
73
74
|
|
|
74
75
|
return 'windows-unknown';
|
|
75
76
|
}
|
|
76
|
-
|
|
77
|
+
|
|
77
78
|
// Unix-based shells
|
|
78
79
|
if (process.env.SHELL) {
|
|
79
80
|
const shellPath = process.env.SHELL.toLowerCase();
|
|
@@ -85,30 +86,30 @@ function detectShell() {
|
|
|
85
86
|
if (shellPath.includes('dash')) return 'dash';
|
|
86
87
|
return `other-unix-${shellPath.split('/').pop()}`;
|
|
87
88
|
}
|
|
88
|
-
|
|
89
|
+
|
|
89
90
|
// Terminal emulators and IDE terminals
|
|
90
91
|
if (process.env.TERM_PROGRAM) {
|
|
91
92
|
return process.env.TERM_PROGRAM.toLowerCase();
|
|
92
93
|
}
|
|
93
|
-
|
|
94
|
+
|
|
94
95
|
return 'unknown-shell';
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
// Function to determine execution context
|
|
98
99
|
function getExecutionContext() {
|
|
99
100
|
// Check if running from npx
|
|
100
|
-
const isNpx = process.env.npm_lifecycle_event === 'npx' ||
|
|
101
|
+
const isNpx = process.env.npm_lifecycle_event === 'npx' ||
|
|
101
102
|
process.env.npm_execpath?.includes('npx') ||
|
|
102
103
|
process.env._?.includes('npx') ||
|
|
103
104
|
import.meta.url.includes('node_modules');
|
|
104
|
-
|
|
105
|
+
|
|
105
106
|
// Check if installed globally
|
|
106
107
|
const isGlobal = process.env.npm_config_global === 'true' ||
|
|
107
108
|
process.argv[1]?.includes('node_modules/.bin');
|
|
108
|
-
|
|
109
|
+
|
|
109
110
|
// Check if it's run from a script in package.json
|
|
110
111
|
const isNpmScript = !!process.env.npm_lifecycle_script;
|
|
111
|
-
|
|
112
|
+
|
|
112
113
|
return {
|
|
113
114
|
runMethod: isNpx ? 'npx' : (isGlobal ? 'global' : (isNpmScript ? 'npm_script' : 'direct')),
|
|
114
115
|
isCI: !!process.env.CI || !!process.env.GITHUB_ACTIONS || !!process.env.TRAVIS || !!process.env.CIRCLECI,
|
|
@@ -118,52 +119,232 @@ function getExecutionContext() {
|
|
|
118
119
|
|
|
119
120
|
// Helper function to get standard environment properties for tracking
|
|
120
121
|
let npmVersionCache = null;
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
|
|
123
|
+
// Enhanced version with step tracking - will replace the original after initialization
|
|
124
|
+
async function enhancedGetTrackingProperties(additionalProps = {}) {
|
|
125
|
+
const propertiesStep = addSetupStep('get_tracking_properties');
|
|
126
|
+
try {
|
|
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
|
|
145
|
+
};
|
|
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
|
|
154
|
+
};
|
|
124
155
|
}
|
|
125
|
-
|
|
126
|
-
const context = getExecutionContext();
|
|
127
|
-
const version = await getVersion();
|
|
128
|
-
return {
|
|
129
|
-
platform: platform(),
|
|
130
|
-
nodeVersion: nodeVersion,
|
|
131
|
-
npmVersion: npmVersionCache,
|
|
132
|
-
executionContext: context.runMethod,
|
|
133
|
-
isCI: context.isCI,
|
|
134
|
-
shell: context.shell,
|
|
135
|
-
DCVersion: version,
|
|
136
|
-
timestamp: new Date().toISOString(),
|
|
137
|
-
...additionalProps
|
|
138
|
-
};
|
|
139
156
|
}
|
|
140
157
|
|
|
141
|
-
//
|
|
158
|
+
// Enhanced tracking function with retries and better error handling
|
|
159
|
+
// This replaces the basic implementation for all tracking after initialization
|
|
142
160
|
async function trackEvent(eventName, additionalProps = {}) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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);
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
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
|
+
}
|
|
150
277
|
});
|
|
151
|
-
} 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}`);
|
|
154
|
-
}
|
|
155
278
|
}
|
|
156
279
|
|
|
157
|
-
// Initial tracking
|
|
158
|
-
trackEvent('npx_setup_start');
|
|
159
280
|
|
|
160
281
|
// Fix for Windows ESM path resolution
|
|
161
282
|
const __filename = fileURLToPath(import.meta.url);
|
|
162
283
|
const __dirname = dirname(__filename);
|
|
163
284
|
|
|
285
|
+
// Setup logging early to capture everything
|
|
286
|
+
const LOG_FILE = join(__dirname, 'setup.log');
|
|
287
|
+
|
|
288
|
+
function logToFile(message, isError = false) {
|
|
289
|
+
const timestamp = new Date().toISOString();
|
|
290
|
+
const logMessage = `${timestamp} - ${isError ? 'ERROR: ' : ''}${message}\n`;
|
|
291
|
+
try {
|
|
292
|
+
appendFileSync(LOG_FILE, logMessage);
|
|
293
|
+
// For setup script, we'll still output to console but in JSON forma
|
|
294
|
+
const jsonOutput = {
|
|
295
|
+
type: isError ? 'error' : 'info',
|
|
296
|
+
timestamp,
|
|
297
|
+
message
|
|
298
|
+
};
|
|
299
|
+
process.stdout.write(JSON.stringify(jsonOutput) + '\n');
|
|
300
|
+
} catch (err) {
|
|
301
|
+
// Last resort error handling
|
|
302
|
+
process.stderr.write(JSON.stringify({
|
|
303
|
+
type: 'error',
|
|
304
|
+
timestamp: new Date().toISOString(),
|
|
305
|
+
message: `Failed to write to log file: ${err.message}`
|
|
306
|
+
}) + '\n');
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
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
|
+
}
|
|
338
|
+
|
|
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
|
+
|
|
164
345
|
// Determine OS and set appropriate config path
|
|
165
346
|
const os = platform();
|
|
166
|
-
const isWindows = os === 'win32';
|
|
347
|
+
const isWindows = os === 'win32';
|
|
167
348
|
let claudeConfigPath;
|
|
168
349
|
|
|
169
350
|
switch (os) {
|
|
@@ -181,265 +362,394 @@ switch (os) {
|
|
|
181
362
|
claudeConfigPath = join(homedir(), '.claude_desktop_config.json');
|
|
182
363
|
}
|
|
183
364
|
|
|
184
|
-
// Setup logging
|
|
185
|
-
const LOG_FILE = join(__dirname, 'setup.log');
|
|
186
365
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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;
|
|
386
|
+
if (error) {
|
|
387
|
+
setupSteps[index].error = error.message || String(error);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
try {
|
|
393
|
+
// Only dependency is node-machine-id
|
|
394
|
+
const machineIdInitStep = addSetupStep('initialize_machine_id');
|
|
190
395
|
try {
|
|
191
|
-
|
|
192
|
-
//
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
} catch (err) {
|
|
200
|
-
// Last resort error handling
|
|
201
|
-
process.stderr.write(JSON.stringify({
|
|
202
|
-
type: 'error',
|
|
203
|
-
timestamp: new Date().toISOString(),
|
|
204
|
-
message: `Failed to write to log file: ${err.message}`
|
|
205
|
-
}) + '\n');
|
|
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);
|
|
206
404
|
}
|
|
405
|
+
} catch (error) {
|
|
406
|
+
logToFile(`Error initializing user ID: ${error}`, true);
|
|
407
|
+
addSetupStep('initialize_machine_id', 'failed', error);
|
|
207
408
|
}
|
|
208
409
|
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
|
|
209
413
|
async function execAsync(command) {
|
|
414
|
+
const execStep = addSetupStep(`exec_${command.substring(0, 20)}...`);
|
|
210
415
|
return new Promise((resolve, reject) => {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
416
|
+
// Use PowerShell on Windows for better Unicode support and consistency
|
|
417
|
+
const actualCommand = isWindows
|
|
418
|
+
? `cmd.exe /c ${command}`
|
|
419
|
+
: command;
|
|
215
420
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
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
|
+
});
|
|
223
430
|
});
|
|
224
431
|
}
|
|
225
432
|
|
|
226
433
|
async function restartClaude() {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
//
|
|
231
|
-
|
|
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 {
|
|
232
443
|
switch (platform) {
|
|
233
444
|
case "win32":
|
|
234
|
-
|
|
235
445
|
await execAsync(
|
|
236
446
|
`taskkill /F /IM "Claude.exe"`,
|
|
237
|
-
)
|
|
447
|
+
);
|
|
238
448
|
break;
|
|
239
449
|
case "darwin":
|
|
240
450
|
await execAsync(
|
|
241
451
|
`killall "Claude"`,
|
|
242
|
-
)
|
|
452
|
+
);
|
|
243
453
|
break;
|
|
244
454
|
case "linux":
|
|
245
455
|
await execAsync(
|
|
246
456
|
`pkill -f "claude"`,
|
|
247
|
-
)
|
|
457
|
+
);
|
|
248
458
|
break;
|
|
249
459
|
}
|
|
250
|
-
|
|
251
|
-
|
|
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');
|
|
252
473
|
try {
|
|
253
474
|
if (platform === "win32") {
|
|
254
|
-
// it
|
|
255
|
-
|
|
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 });
|
|
256
479
|
} else if (platform === "darwin") {
|
|
257
|
-
await execAsync(`open -a "Claude"`)
|
|
480
|
+
await execAsync(`open -a "Claude"`);
|
|
481
|
+
updateSetupStep(startStep, 'completed');
|
|
482
|
+
await trackEvent('npx_setup_start_claude_success', { platform });
|
|
258
483
|
} else if (platform === "linux") {
|
|
259
|
-
await execAsync(`claude`)
|
|
484
|
+
await execAsync(`claude`);
|
|
485
|
+
updateSetupStep(startStep, 'completed');
|
|
486
|
+
await trackEvent('npx_setup_start_claude_success', { platform });
|
|
260
487
|
}
|
|
261
|
-
} catch{}
|
|
262
488
|
|
|
263
|
-
|
|
264
|
-
|
|
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
|
|
499
|
+
}
|
|
500
|
+
} catch (error) {
|
|
501
|
+
updateSetupStep(restartStep, 'failed', error);
|
|
265
502
|
await trackEvent('npx_setup_restart_claude_error', { error: error.message });
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Check if config file exists and create default if not
|
|
271
|
-
if (!existsSync(claudeConfigPath)) {
|
|
272
|
-
logToFile(`Claude config file not found at: ${claudeConfigPath}`);
|
|
273
|
-
logToFile('Creating default config file...');
|
|
274
|
-
|
|
275
|
-
// Track new installation
|
|
276
|
-
await trackEvent('npx_setup_create_default_config');
|
|
277
|
-
|
|
278
|
-
// Create the directory if it doesn't exist
|
|
279
|
-
const configDir = dirname(claudeConfigPath);
|
|
280
|
-
if (!existsSync(configDir)) {
|
|
281
|
-
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);
|
|
282
505
|
}
|
|
283
|
-
|
|
284
|
-
// Create default config with shell based on platform
|
|
285
|
-
const defaultConfig = {
|
|
286
|
-
"serverConfig": isWindows
|
|
287
|
-
? {
|
|
288
|
-
"command": "cmd.exe",
|
|
289
|
-
"args": ["/c"]
|
|
290
|
-
}
|
|
291
|
-
: {
|
|
292
|
-
"command": "/bin/sh",
|
|
293
|
-
"args": ["-c"]
|
|
294
|
-
}
|
|
295
|
-
};
|
|
296
|
-
|
|
297
|
-
writeFileSync(claudeConfigPath, JSON.stringify(defaultConfig, null, 2));
|
|
298
|
-
logToFile('Default config file created. Please update it with your Claude API credentials.');
|
|
299
506
|
}
|
|
300
507
|
|
|
301
|
-
// Function to check for debug mode argument
|
|
302
|
-
function isDebugMode() {
|
|
303
|
-
return process.argv.includes('--debug');
|
|
304
|
-
}
|
|
305
508
|
|
|
306
509
|
// Main function to export for ESM compatibility
|
|
307
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');
|
|
308
515
|
const debugMode = isDebugMode();
|
|
516
|
+
|
|
309
517
|
if (debugMode) {
|
|
310
518
|
logToFile('Debug mode enabled. Will configure with Node.js inspector options.');
|
|
519
|
+
await trackEvent('npx_setup_debug_mode', { enabled: true });
|
|
311
520
|
}
|
|
521
|
+
|
|
312
522
|
try {
|
|
313
|
-
//
|
|
314
|
-
const
|
|
315
|
-
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
|
+
}
|
|
316
593
|
|
|
317
594
|
// Prepare the new server config based on OS
|
|
595
|
+
const configPrepStep = addSetupStep('prepare_server_config');
|
|
596
|
+
|
|
318
597
|
// Determine if running through npx or locally
|
|
319
598
|
const isNpx = import.meta.url.includes('node_modules');
|
|
599
|
+
await trackEvent('npx_setup_execution_mode', { isNpx });
|
|
320
600
|
|
|
321
601
|
// Fix Windows path handling for npx execution
|
|
322
602
|
let serverConfig;
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
"
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
"
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
serverConfig = {
|
|
369
|
-
"command": isWindows ? "npx.cmd" : "npx",
|
|
370
|
-
"args": [
|
|
371
|
-
"@wonderwhy-er/desktop-commander@latest"
|
|
372
|
-
]
|
|
373
|
-
};
|
|
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
|
+
}
|
|
374
648
|
} else {
|
|
375
|
-
//
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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
|
+
}
|
|
383
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}`);
|
|
384
675
|
}
|
|
385
676
|
|
|
386
|
-
//
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
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
|
+
}
|
|
390
684
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
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
|
+
}
|
|
691
|
+
|
|
692
|
+
// Add or update the terminal server config with the proper name "desktop-commander"
|
|
693
|
+
config.mcpServers["desktop-commander"] = serverConfig;
|
|
396
694
|
|
|
397
|
-
|
|
398
|
-
|
|
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
|
+
}
|
|
399
704
|
|
|
400
|
-
// Write the updated config back
|
|
401
|
-
writeFileSync(claudeConfigPath, JSON.stringify(config, null, 2), 'utf8');
|
|
402
|
-
await trackEvent('npx_setup_update_config');
|
|
403
705
|
logToFile('Successfully added MCP server to Claude configuration!');
|
|
404
706
|
logToFile(`Configuration location: ${claudeConfigPath}`);
|
|
405
|
-
|
|
707
|
+
|
|
406
708
|
if (debugMode) {
|
|
407
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');
|
|
408
710
|
} else {
|
|
409
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');
|
|
410
712
|
}
|
|
411
713
|
|
|
714
|
+
// Try to restart Claude
|
|
412
715
|
await restartClaude();
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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;
|
|
424
727
|
} catch (error) {
|
|
425
|
-
|
|
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
|
+
|
|
426
737
|
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
|
-
process.exit(1);
|
|
738
|
+
return false;
|
|
437
739
|
}
|
|
438
740
|
}
|
|
439
741
|
|
|
440
742
|
// Allow direct execution
|
|
441
743
|
if (process.argv.length >= 2 && process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
442
|
-
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
|
+
});
|
|
443
753
|
logToFile(`Fatal error: ${error}`, true);
|
|
444
754
|
process.exit(1);
|
|
445
755
|
});
|